Skip to content

fix(retrieval): reserve a relevance budget floor so locks can't starve query-relevant hits (#1014) - #1015

Merged
github-actions[bot] merged 2 commits into
mainfrom
fix/retrieve-lock-budget-floor
Jun 29, 2026
Merged

github-actions[bot] merged 2 commits into
mainfrom
fix/retrieve-lock-budget-floor

Conversation

@robotrocketscience

@robotrocketscience robotrocketscience commented Jun 29, 2026

Copy link
Copy Markdown
Owner

What

Fixes #1014: when a store's locked beliefs (L0) alone meet or exceed the retrieval token budget, retrieve() / retrieve_v2() returned only the locks for every prompt — zero query-relevant content surfaced.

Why

L0 locks are injected unconditionally and never trimmed (#379); their tokens are subtracted from the L2.5/L1 budget. Once locks fill the budget, l25_room and l1_remaining_budget both compute to 0. Found on a real store: 12 locks = 2485 tokens vs the 2400 default budget → 0 relevance tokens, so every query returned the same 12 locks.

Fix

Reserve a relevance floor: relevance_budget = max(effective_budget × RELEVANCE_BUDGET_FLOOR_FRACTION, effective_budget − locked_used) (fraction 0.25); cap L2.5+L1 fills at locked_used + relevance_budget in both retrieve() and retrieve_with_tiers().

  • Byte-identical whenever locks leave at least the floor of room (locked_used + relevance_budget == effective_budget), i.e. it only fires in the lock-saturated regime. Lock-free corpora (LoCoMo) are unaffected.
  • Locks are still never trimmed (Redefine locked-belief contract: always-injected pool (supersedes #373) #379 contract preserved); in the saturated regime total output may exceed the nominal budget by up to the floor — the intended trade for never going blind to the query.

Verification

  • New tests (test_relevance_budget_floor.py): saturating locks → ≥1 relevant belief surfaces; locks-fit → all relevant surface unchanged.
  • Regression: 885 retrieval/ranking/budget/lock/cluster/compression tests pass, 0 failures — byte-identical contracts hold.
  • Real-store validation: query-relevant results went from 0 → 20–27 per query (the surfaced relevant beliefs are the user's captured conversational history, not document chunks).

Distinct from prior work

Closes #1014.

Summary by Sourcery

Reserve a relevance token budget floor in retrieval so locked beliefs cannot consume the entire budget and suppress query-relevant results.

Bug Fixes:

  • Ensure retrieval always leaves token budget for L2.5/L1 results even when locked beliefs alone meet or exceed the effective budget.

Enhancements:

  • Introduce a configurable relevance-budget floor fraction and apply it consistently in both retrieval paths to keep behavior unchanged outside lock-saturated regimes.

Documentation:

  • Document the lock-saturation retrieval fix in the v3 changelog with notes on behavior and impact on real stores.

Tests:

  • Add regression tests covering lock-saturated stores, lock-fitting stores, and sanity checks for the relevance-budget floor configuration.

Summary by CodeRabbit

  • Bug Fixes

    • Improved retrieval results when locked items use most of the budget, so query-relevant items are still returned.
    • Added a minimum budget reserve for non-locked results to prevent relevance from dropping to zero in saturated cases.
    • Kept existing behavior unchanged when locks do not dominate the budget.
  • Tests

    • Added regression coverage for lock-heavy retrieval scenarios and normal-budget behavior.

@sourcery-ai

sourcery-ai Bot commented Jun 29, 2026

Copy link
Copy Markdown

Reviewer's Guide

Implements a relevance token budget floor in retrieval so that uncapped locked beliefs cannot consume the entire budget, updates both retrieval paths to respect this floor, and adds tests and changelog entry for the new behavior.

File-Level Changes

Change Details Files
Introduce a relevance-budget floor constant and apply it in retrieval budgeting so locked beliefs can’t starve query-relevant L2.5/L1 hits.
  • Add RELEVANCE_BUDGET_FLOOR_FRACTION configuration constant with documentation explaining the lock-saturation bug and behavior.
  • Compute relevance_budget as max(effective_budget × floor_fraction, effective_budget − locked_used) in retrieval flows.
  • Use relevance_budget instead of effective_budget − locked_used to determine L2.5 room, ensuring at least a reserved slice for relevance.
src/aelfrice/retrieval.py
Adjust L1 and expansion budget checks to cap total tokens at locked_used plus relevance_budget instead of the global effective budget.
  • Change budget overflow checks for L1 packing so they compare used + cost to locked_used + relevance_budget rather than effective_budget.
  • Update cluster-based L1 packing to use remaining budget based on locked_used + relevance_budget − used.
  • Apply the new cap consistently in BFS/expansion loops so post-L0 content remains within the reserved relevance budget envelope.
src/aelfrice/retrieval.py
Document the fix in the changelog and add regression tests covering lock-saturation and non-saturated regimes.
  • Add changelog entry describing the starvation bug, the reserved relevance floor formula, and behavioral guarantees.
  • Create tests ensuring at least one relevant belief surfaces when locks alone overflow the default budget.
  • Add tests verifying behavior is unchanged when locks fit within the budget and that the floor fraction is within (0,1).
CHANGELOG/v3.md
tests/test_relevance_budget_floor.py

Assessment against linked issues

Issue Objective Addressed Explanation
#1014 Modify retrieval logic so that when locked beliefs alone meet or exceed the token budget, a reserved relevance budget floor ensures L2.5/L1 query-relevant content can still be retrieved while keeping locks untrimmed.
#1014 Add regression tests to confirm that (a) lock-saturated stores still surface query-relevant beliefs, and (b) the relevance budget floor is a no-op when locks fit within the budget, with a sanity check on the floor fraction.
#1014 Update documentation/changelog to describe the locked-belief starvation bug and the relevance budget floor fix.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@robotrocketscience, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 40 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6513b751-3a7b-4e7b-8b23-fa06659ceff7

📥 Commits

Reviewing files that changed from the base of the PR and between c9b4fcf and 0a97652.

📒 Files selected for processing (3)
  • CHANGELOG/v3.md
  • src/aelfrice/retrieval.py
  • tests/test_relevance_budget_floor.py
📝 Walkthrough

Walkthrough

Adds RELEVANCE_BUDGET_FLOOR_FRACTION = 0.25 to retrieval.py and applies a max(floor, effective_budget − locked_used) formula in both retrieve() and retrieve_with_tiers(), capping L2.5/L1/BFS budget guards at locked_used + relevance_budget. Adds regression tests and a changelog entry.

Relevance-Budget Floor

Layer / File(s) Summary
Floor constant and budget logic in retrieve() / retrieve_with_tiers()
src/aelfrice/retrieval.py
Declares RELEVANCE_BUDGET_FLOOR_FRACTION = 0.25, then replaces the bare effective_budget − locked_used relevance-room calculation with relevance_budget = max(floor(effective_budget × 0.25), effective_budget − locked_used) in both retrieve() and retrieve_with_tiers(); all token-budget guards for L1 packing, HRR expansion, and BFS hops are updated from effective_budget to locked_used + relevance_budget.
Regression tests and changelog
tests/test_relevance_budget_floor.py, CHANGELOG/v3.md
Three tests cover lock-saturated starvation (floor activates, non-lock belief surfaces), floor as no-op when locks fit (all relevant beliefs returned), and fraction range sanity. Changelog entry documents the fix and behavior contract.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main retrieval bug fix and references the linked issue.
Description check ✅ Passed The description covers the problem, fix, verification, and linked issue, though it is not formatted exactly to the template.
Linked Issues check ✅ Passed The changes implement the relevance-budget floor and preserve lock behavior exactly as required by #1014.
Out of Scope Changes check ✅ Passed The changelog and tests are directly related to the retrieval fix, with no unrelated code changes evident.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/retrieve-lock-budget-floor

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've left some high level feedback:

  • The relevance budget calculation (relevance_budget = max(...)) is duplicated in both retrieval paths; consider extracting this into a small helper to keep the lock-saturation logic consistent and easier to adjust in the future.
  • The changelog entry for this fix is quite long and implementation-heavy; you could tighten it to focus on the behavioral change (locks no longer starve relevant hits) and the high-level tradeoff, leaving out detailed formulae and test anecdotes.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The relevance budget calculation (`relevance_budget = max(...)`) is duplicated in both retrieval paths; consider extracting this into a small helper to keep the lock-saturation logic consistent and easier to adjust in the future.
- The changelog entry for this fix is quite long and implementation-heavy; you could tighten it to focus on the behavioral change (locks no longer starve relevant hits) and the high-level tradeoff, leaving out detailed formulae and test anecdotes.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
tests/test_relevance_budget_floor.py (1)

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

Add a parity regression for retrieve_with_tiers().

This new file only exercises retrieve(), but the same floor/guard math was duplicated into retrieve_with_tiers(). A lock-saturation test against the tiered API would keep the two entry points from drifting.

🤖 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 `@tests/test_relevance_budget_floor.py` around lines 38 - 67, Add a regression
test that exercises retrieve_with_tiers() with the same lock-saturation scenario
used for retrieve(), since the floor/guard logic was duplicated there and could
drift. Reuse the existing test setup patterns in test_relevance_budget_floor.py
and verify that a query-relevant non-lock belief still appears under heavy
locked-belief saturation when calling retrieve_with_tiers(), while keeping the
existing retrieve() coverage intact.
🤖 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 `@src/aelfrice/retrieval.py`:
- Around line 121-133: The public budget contract in the retrieval entry points
is now stale because lock-saturated queries can exceed token_budget by the
reserved relevance floor. Update the docstrings/comments for retrieve() and
retrieve_with_tiers() in retrieval.py so they explicitly describe the new
behavior: locks are never trimmed, and total output may go above token_budget in
the saturated regime to preserve relevance tokens. Keep the wording aligned with
the existing budget logic and the RELEVANCE_BUDGET_FLOOR_FRACTION behavior.

---

Nitpick comments:
In `@tests/test_relevance_budget_floor.py`:
- Around line 38-67: Add a regression test that exercises retrieve_with_tiers()
with the same lock-saturation scenario used for retrieve(), since the
floor/guard logic was duplicated there and could drift. Reuse the existing test
setup patterns in test_relevance_budget_floor.py and verify that a
query-relevant non-lock belief still appears under heavy locked-belief
saturation when calling retrieve_with_tiers(), while keeping the existing
retrieve() coverage intact.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9de5621d-327e-4bf7-a6d6-e2718d835305

📥 Commits

Reviewing files that changed from the base of the PR and between 5143769 and c9b4fcf.

📒 Files selected for processing (3)
  • CHANGELOG/v3.md
  • src/aelfrice/retrieval.py
  • tests/test_relevance_budget_floor.py

Comment thread src/aelfrice/retrieval.py
robotrocketscience added a commit that referenced this pull request Jun 29, 2026
…e() (#1015 review)

In the lock-saturated regime the relevance floor (#1014) lets output exceed
token_budget by up to the floor; note this on the public retrieve() contract
that otherwise promises a hard budget cap.
…e query-relevant hits (#1014)

L0 locked beliefs are injected unconditionally and never trimmed (#379),
and their tokens are subtracted from the L2.5/L1 budget. A store whose
locks alone met or exceeded the token budget therefore returned ONLY the
locks for every prompt — zero query-relevant content (real store: 12
locks = 2485 tokens vs a 2400 budget -> 0 relevance tokens).

Reserve a relevance floor: relevance_budget = max(effective_budget *
RELEVANCE_BUDGET_FLOOR_FRACTION, effective_budget - locked_used); cap
L2.5+L1 fills at locked_used + relevance_budget. Byte-identical outside
the lock-saturated regime (lock-free corpora like LoCoMo unaffected);
locks still never trimmed. Validated on the real store: query-relevant
results went 0 -> 20-27 per query. 885 retrieval/ranking/budget/lock
tests pass.
…e() (#1015 review)

In the lock-saturated regime the relevance floor (#1014) lets output exceed
token_budget by up to the floor; note this on the public retrieve() contract
that otherwise promises a hard budget cap.
@robotrocketscience
robotrocketscience force-pushed the fix/retrieve-lock-budget-floor branch from 9d2b566 to 0a97652 Compare June 29, 2026 22:36
@robotrocketscience robotrocketscience added the ready-to-merge Trigger merge-train: FF main to this PR's head label Jun 29, 2026
@github-actions
github-actions Bot merged commit 0a97652 into main Jun 29, 2026
28 checks passed
@github-actions

Copy link
Copy Markdown

merge-train: merged 0a97652main via FF push.

@github-actions github-actions Bot removed the ready-to-merge Trigger merge-train: FF main to this PR's head label Jun 29, 2026
@robotrocketscience
robotrocketscience deleted the fix/retrieve-lock-budget-floor branch June 29, 2026 22:49
robotrocketscience added a commit that referenced this pull request Jul 30, 2026
…king it

The candidate limit is applied by the search — SQL LIMIT on the FTS5
path, top_k on BM25F — so filtering superseded beliefs afterwards
dropped the pack size by however many were retired and left current,
relevant beliefs stranded just below the cutoff:

  l1_limit=4, three of the top four retired  ->  1 belief returned
  l1_limit=10, eight of the top ten retired  ->  2 beliefs returned

Same shape as the lock-budget starvation fixed in #1014/#1015: a filter
applied after the budget starves the pack. In the degenerate case every
top-l1_limit candidate is retired and the arm returned nothing while the
store held the answer.

The exclusion arm now widens the fetch and retries, stopping as soon as
it has l1_limit survivors or the search runs out of matches, bounded at
three rounds. The demote arm is untouched — it reorders a fixed
candidate set, which is why it always measured full.

This also matters for the ratified three-arm bench: with the arms
differing in pack size as well as in treatment, a loss for exclusion
could not have been attributed to either.

Raised in review on #1191.
robotrocketscience added a commit that referenced this pull request Jul 30, 2026
…king it

The candidate limit is applied by the search — SQL LIMIT on the FTS5
path, top_k on BM25F — so filtering superseded beliefs afterwards
dropped the pack size by however many were retired and left current,
relevant beliefs stranded just below the cutoff:

  l1_limit=4, three of the top four retired  ->  1 belief returned
  l1_limit=10, eight of the top ten retired  ->  2 beliefs returned

Same shape as the lock-budget starvation fixed in #1014/#1015: a filter
applied after the budget starves the pack. In the degenerate case every
top-l1_limit candidate is retired and the arm returned nothing while the
store held the answer.

The exclusion arm now widens the fetch and retries, stopping as soon as
it has l1_limit survivors or the search runs out of matches, bounded at
three rounds. The demote arm is untouched — it reorders a fixed
candidate set, which is why it always measured full.

This also matters for the ratified three-arm bench: with the arms
differing in pack size as well as in treatment, a loss for exclusion
could not have been attributed to either.

Raised in review on #1191.
robotrocketscience added a commit that referenced this pull request Jul 30, 2026
…king it

The candidate limit is applied by the search — SQL LIMIT on the FTS5
path, top_k on BM25F — so filtering superseded beliefs afterwards
dropped the pack size by however many were retired and left current,
relevant beliefs stranded just below the cutoff:

  l1_limit=4, three of the top four retired  ->  1 belief returned
  l1_limit=10, eight of the top ten retired  ->  2 beliefs returned

Same shape as the lock-budget starvation fixed in #1014/#1015: a filter
applied after the budget starves the pack. In the degenerate case every
top-l1_limit candidate is retired and the arm returned nothing while the
store held the answer.

The exclusion arm now widens the fetch and retries, stopping as soon as
it has l1_limit survivors or the search runs out of matches, bounded at
three rounds. The demote arm is untouched — it reorders a fixed
candidate set, which is why it always measured full.

This also matters for the ratified three-arm bench: with the arms
differing in pack size as well as in treatment, a loss for exclusion
could not have been attributed to either.

Raised in review on #1191.
robotrocketscience added a commit that referenced this pull request Jul 30, 2026
…king it

The candidate limit is applied by the search — SQL LIMIT on the FTS5
path, top_k on BM25F — so filtering superseded beliefs afterwards
dropped the pack size by however many were retired and left current,
relevant beliefs stranded just below the cutoff:

  l1_limit=4, three of the top four retired  ->  1 belief returned
  l1_limit=10, eight of the top ten retired  ->  2 beliefs returned

Same shape as the lock-budget starvation fixed in #1014/#1015: a filter
applied after the budget starves the pack. In the degenerate case every
top-l1_limit candidate is retired and the arm returned nothing while the
store held the answer.

The exclusion arm now widens the fetch and retries, stopping as soon as
it has l1_limit survivors or the search runs out of matches, bounded at
three rounds. The demote arm is untouched — it reorders a fixed
candidate set, which is why it always measured full.

This also matters for the ratified three-arm bench: with the arms
differing in pack size as well as in treatment, a loss for exclusion
could not have been attributed to either.

Raised in review on #1191.
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.

fix(retrieval): locked beliefs starve query-relevant retrieval when locks meet/exceed the token budget

1 participant