Skip to content
← Free FDE training guides

Free learning / 50-minute retrieval proof workshop

Retrieval limits: an empty result can hide available evidence

Run actual SQLite FTS5 queries to compare late filtering, bounded overfetch and eligible result limits. Recheck changed grants and source revisions before assembling context.

No account needed. Use your own copy for writing and keep the original as a reference.

Engineering lab library / Run locally

Choose your next agent lab.

Start with the durable workflow reference, then choose the boundary you want to test. Open its walkthrough for setup, commands and expected observations.

Synthetic records. No provider key or model inference. Runs locally with the standard library.

  1. Start here / Python 3.11+ · Standard library

    Retrieval candidate limits and current-source gates

    Compare actual ranked populations, changed grants and revisions without weakening context admission.

    Python 3.11+ · Built-in SQLite FTS5 · Synthetic in-memory rows · No network or model

Original LockedIn Labs workshop · 50 minutes · Python 3.11+ with built-in SQLite FTS5. Eight independently reset in-memory cases use fictional documents and grants. No model, embeddings, account, network, persistent database or production authorization service is involved.

1. Ask which population the limit applies to

A search service returns its two highest-ranked candidates. Both are outside the current reader's permitted population. Removing them afterward can correctly prevent disclosure while leaving an empty context, even though three matching permitted sources exist farther down the ranking. “Nothing admitted from this candidate list” is not “no permitted evidence exists.”

Retrieval-augmented generation, or RAG, supplies selected source material before a model answers. Its candidate budget affects which evidence can reach later stages. Permission, source freshness, ranking and that budget have different jobs. A relevant result is not permission; an empty limited result is not a complete collection search.

The runner uses actual SQLite FTS5 full-text matching and its BM25 ranking function. It does not simulate semantic retrieval or execute an approximate nearest-neighbour index. All five source texts have 20 tokens; their occurrences of handoff are 9, 7, 5, 3 and 1. That isolates an understandable ranking comparison without making the score a probability or a portable confidence threshold.

2. Bind eligibility before limiting returned candidates

The host fixture supplies subject U-7 and active workspace North. It has three North read grants and a separate South grant. North's restricted document has no read grant. The active workspace still matters even when the same supplied subject has access elsewhere. The wrong-workspace request field says South, but the query retains trusted North context. This fixture supplies identity; it does not authenticate anyone or implement a workspace switch.

The eligible query joins the source on both tenant and document identity, requires active/current source revision, and checks the matching subject/read grant. Those result predicates appear in the same SELECT before its ORDER BY and LIMIT. Bound parameters protect SQL values; they do not invent missing authorization predicates.

Code example
-- Shape only; inspect the runner's complete ELIGIBLE_SQL and BODY_SQL.
WHERE search MATCH ?
  AND source_workspace = trusted_workspace
  AND source_is_active AND source_revision = indexed_revision
  AND matching_current_read_grant
ORDER BY actual_bm25_rank, indexed_row
LIMIT ?

This is a statement about eligible result rows and their limit. It is not a claim about the query planner's physical evaluation order. FTS5 uses the shared index and corpus statistics. The lab does not establish physical-read isolation, independent tenant indexes, side-channel resistance or a database-enforced production policy.

UNDERSTAND THE SYSTEM

Limit the eligible result, then recheck the source

Six authored frames explain eight actual in-memory SQLite FTS5 comparisons. They separate candidate coverage from current permission and source revision. This is not a live search engine, vector benchmark or physical tenant-isolation proof.

FOLLOW THE WORK

Watch one example, step by step.

6 authored stepsYou control the pace
01 / EXPLORE THE PARTSControls loading · written explanations below
Direction of information or workReturn / feedback

WHAT THIS PART DOES

Ranked search index

The shared FTS5 table contains fictional indexed bodies and revisions. Actual BM25 ranks matching rows. Its global statistics and physical work are not made tenant-isolated by a result predicate.

CONNECTED TO
  • Match eligibility: Trusted eligibility
  • Global cap: Candidate budget
Read the full worked example
  1. 01
    1 · A global cap can hide available evidence

    Post-filter selects South/S-OTHER and North/N-RESTRICTED at limit two, then correctly withholds both. The resulting empty context does not mean the three matching permitted North sources are absent. Inspect the ranked population before interpreting the empty packet.

    ACTUAL LATE-FILTER RESULT: Two ineligible candidates · zero context · three eligible matches

  2. 02
    2 · One extra candidate is not a guarantee

    Overfetch selects limit plus one: the same two ineligible rows and N-A. Its gate admits N-A, still short of the requested two. This fixed increment can improve coverage without establishing completeness. At limit one, the two-candidate pool still admits nothing.

    ACTUAL BOUNDED OVERFETCH: Three candidates · one admitted source

  3. 03
    3 · Eligibility changes the returned population

    The eligible SELECT binds trusted North/U-7, source identity/current revision and the current read grant before limiting result rows. It returns N-A/N-B. A request saying South does not change that trusted context. No-grant and no-match are separate empty controls, not zero-score winners.

    ACTUAL ELIGIBLE RESULT: N-A and N-B at limit two

  4. 04
    4 · Earlier eligibility does not survive revocation

    Revoked selects N-A/N-B, then removes N-A's read grant. The final gate admits N-B only. The deliberately unchecked indexed copy still contains N-A. A separately observed new selection finds N-B/N-C; it did not refill the earlier packet automatically or atomically.

    ACTUAL SEQUENTIAL GRANT CHANGE: N-A withheld: permission_missing · N-B admitted

  5. 05
    5 · Indexed text can outlive its source revision

    Corrected selects N-A/N-B, then changes N-A's source to revision two while its indexed body remains revision one. The source gate withholds N-A instead of using stale index text. Requery selects N-B/N-C; it does not refresh the index or recover N-A's changed evidence.

    ACTUAL SEQUENTIAL SOURCE CHANGE: N-A withheld: source_revision_changed

  6. 06
    6 · A smaller budget is not a permission change

    Change the limit from two to one. Eligible selection returns N-A alone; late-filter and its fixed one-extra overfetch remain empty. The same grants exist. Query predicates constrain result rows, not the engine's physical read order; this shared FTS5 corpus is not an ANN or separate-tenant index experiment.

    CONTROLLED LIMIT CHANGE: Limit 1 · eligible N-A · late-filter empty

Read the connections in plain English
  1. 01
    Ranked search index → Trusted eligibility

    Bind source and grant relationships in the result query.

  2. 02
    Trusted eligibility → Candidate budget

    Apply the result budget to the eligible population.

  3. 03
    Ranked search index → Candidate budget

    The late-filter comparison caps global candidates first.

  4. 04
    Candidate budget → Current source read

    Validate each selected source again before reading its body.

  5. 05
    Current source read → Context packet

    Retain only current permitted source bodies.

  6. 06
    Current source read → Receiving evidence

    Keep the specific missing permission, changed revision or scope reason.

  7. 07
    Context packet → Receiving evidence

    Inspect the actual admitted population and its remaining gaps.

Check your understanding: Does an empty context after filtering prove no permitted matching source exists?

No. The post-filter case discards two globally selected ineligible rows while three permitted matching North sources remain outside that cap. Inspect candidate coverage and current-source decisions separately; never fill a gap with a rejected source to meet a count.

3. Run eight independently reset cases

Download the Python runner into a disposable folder:

Code example
python3 --version
python3 fde-retrieval-limits.py > retrieval-limit-two.json
python3 fde-retrieval-limits.py --limit 1 > retrieval-limit-one.json
python3 fde-retrieval-limits.py --case revoked
python3 fde-retrieval-limits.py --case corrected

On Windows, use py -3 in place of python3. SQLite must have built-in FTS5; the program reports a missing feature instead of downloading an extension. Each case creates and closes a fresh memory database. Redirection overwrites an existing output file: keep the original report under a new name before repeating. The runner accepts only its authored case and a limit of 1, 2 or 3.

Predict the default limit-two observations:

CaseCandidates selectedContext after the current-source gate
post-filterSouth/S-OTHER and North/N-RESTRICTEDEmpty, despite three matching eligible North sources
overfetchThe same two, then North/N-AN-A only; one extra candidate still does not fill two eligible places
pre-limitNorth/N-A and North/N-BBoth current permitted sources
revokedN-A and N-B, then N-A's read grant is removedN-B only; the unchecked index copy still contains N-A
correctedN-A and N-B, then N-A's source changes to revision 2N-B only; the unchanged index still has N-A revision 1
wrong-workspaceRequest says South, trusted context stays NorthN-A and N-B; the request field does not change the workspace
no-grantSupplied subject U-8 has no grantsNo candidates or admitted context
no-matchQuery volcano has no full-text matchesNo candidates or admitted context

overfetch asks for limit + 1 global candidates before applying the same final gate. It is a bounded comparison, not a claim that overfetch can never work. A larger pool can improve coverage, but this fixed increment supplies no completeness guarantee. The corpus and actual rank order determine what it admits.

4. Check the source again when building context

Inspect candidate_sql, trusted_fixture_context, all_matching_eligible_before and candidates. Candidate rows contain index identity, revision and actual score; they are not source-body permission receipts. The final gate retains a decision per candidate and uses a separate body SELECT with workspace, document, active state, exact revision and current grant predicates.

Text example
Original selection: North/N-A r1, North/N-B r1
After revocation: N-A -> permission_missing; N-B -> admitted_current_source
After correction: N-A -> source_revision_changed; N-B -> admitted_current_source

The body in admitted context comes from the source table. In the two changed-source cases, deliberately_unchecked_index_context is a separate broken comparison: it reads selected indexed bodies without rechecking current permission or revision. It is not accepted context. Locate N-A in that unsafe copy and explain the exact failed condition before proposing a repair. All source text is fictional.

The program changes rows sequentially on one connection. It does not prove race-free revocation between a database read, model call and final delivery. Real owners must define the allowed ordering, snapshot/lease or version contract and test it in the receiving system. Removing a grant cannot recall text already delivered.

5. Change the budget without changing permission

Run with limit 1. Global post-filter and the two-candidate overfetch now admit nothing, while eligibility-before-limit selects N-A. Restore limit 2 and explain why N-A/N-B become available without granting access to another source. A smaller result budget is not an access decision.

After revocation or correction, the admitted packet is not automatically refilled. Its one remaining N-B source does not prove there is no other usable evidence. fresh_selection_after_change is a separately observed second query that selects N-B/N-C under the changed state; it is not an atomic continuation of the earlier selection. The corrected N-A remains excluded until the index supports its new revision.

Do not fill a gap with a rejected candidate to meet a requested count. Choose a bounded requery, return a partial/unsupported result for the permitted purpose, or ask the accountable source owner for the missing evidence. Context admission does not establish that a source supports the eventual answer.

6. Receive the retrieval boundary as a delivery pod

  • Builder: retain exact runtime/SQLite versions, candidate SQL, original/changed commands and reports. Keep selected index rows, source-read decisions and the deliberately unchecked copy distinct.
  • Receiver: reproduce the empty late-filter result, change only the limit and locate the still-available eligible sources. Then reproduce one source change and identify the failed gate using your own evidence.
  • Application, source and search owners: agree trusted scope, actual index filtering semantics, source/permission ordering and a bounded refill policy. Name the next receiving-store or provider-specific test; a literal metadata filter alone is not an authorization system.

Each participant keeps their own record:

Text example
Synthetic alias / role:
Exact runner / Python / SQLite version / command:
Prediction before execution:
Trusted context / requested workspace:
Candidate SQL / depth / actual selected identities:
Own observed context / withheld reason / evidence path:
Available permitted source omitted by the candidate cap:
Changed limit / separate prediction and observation:
Own contribution / assistance:
Receiving action actually attempted / assistance:
Remaining gap / next bounded test / accountable role:

Use the Identity 101 guide for token and grant binding, and index lifecycle for actual refresh, deletion and late-job handling. These practice results do not establish qualification, independence, client placement or deployment permission.

7. Defend the tradeoff in an interview

Explain why zero admitted candidates can coexist with matching permitted sources, and why a fixed overfetch increment is a workload-dependent choice. Trace which population each limit actually constrained. Distinguish a full-text no-match from an access refusal without promising which diagnostic details a production caller may see.

Explain why a correctly scoped selection still needs a current-source gate, and why that gate can reduce a previously full packet. Describe the separate requery rather than claiming it happened automatically or atomically. Identify the source revision and grant that your next test must bind.

Finally, compare this exhaustive local SQL result model with an approximate vector engine's documented prefilter and postfilter modes, including per-shard candidate effects. The workshop has no vectors, shards, ANN recall measurement, cloud adapter or production security proof. Inspect the actual provider contract and measure authorized evidence coverage on the intended workload before transferring an implementation choice.

Sources checked 28 October 2026: SQLite FTS5 and BM25, SQLite SELECT processing and LIMIT, and Microsoft's vector-filter mode documentation. The last reference describes a different engine; this lab does not reproduce its algorithms. The synthetic corpus, comparisons, diagrams and receiving exercise are original.

Before you move on

Keep the evidence of your work.

  • Original and changed-limit reports with actual query and source evidence.
  • Candidate coverage separated from current-source admission.
  • An individual receiving action and next accountable search or source test.

Keep these in your own working copy. Reading this page does not save work or record completion.

Suggested next free session

Keep retrieval current when the source changes

Follow refresh and deletion work after identifying a stale indexed candidate. This is a suggested sequence; choose the practice that addresses your current gap.

Open the next free guide

Free account

Give your practice a home.

Try a role exercise with account drafts and a downloadable worksheet. Keep the files you made in this guide in your own working copy.

Create a free learning account

Guided training · by application

Build toward a program.

Request structured lessons and a training seat. Scope, schedule, feedback arrangements and tuition are confirmed in writing before you commit.

Explore a training application
Have Academy access?

Use your existing training seat to open worked explanations and practice in the related lesson. Creating a free account does not grant a seat.

Open the related Academy lesson →
Back to top ↑