Find the best evidence this person may read.
Five requested does not always mean five returned.

At Sachin, the records clerk needs five relevant extracts for Maya. The clerk takes the first five sheets from a ranked pile, checks their labels, and removes three she cannot read.
The packet now contains two sheets. Farther down the pile sit three more useful sheets Maya is allowed to read. They never reached the permission check.
Nothing forbidden entered her packet. Yet the search missed useful evidence. This page separates those two questions: did we exclude denied text, and did we find the best allowed text?
Part 2 attached permission labels to every chunk. Here, you will see how the point at which we apply those labels changes a limited search.
Why did a request for five sheets return only two?
1Take the first five
The cutoff stops the clerk before seeing the rest of the ranked pile.
Limited pile → candidate set2Check those labels
Three candidates are denied to Maya. Two remain in her packet.
Authorization → allowed candidates3Look a little farther
In this pile, reaching position eight recovers her five target sheets.
Search budget → allowed evidence recallPermission checks can remove a sheet. They cannot retrieve a sheet the search never reached.
Define the packet we wanted
Top-k means the best k results according to a ranking. For this experiment, k = 5. Our target is the five highest-scoring passages Maya may read. If only three eligible passages existed, the target would contain three.
We use an exact baseline: imagine we know the score of every passage, remove the denied ones, and take the best five that remain. This gives us a reference answer for measuring the experiment. It is not a recommendation to scan every record in a large production collection.
The pile contains ten fictional passages from the same four documents as Part 1. Their scores are illustrative ordering values, not vector distances, probabilities, or measurements from a real search engine.
The cutoff changes what the check can see
The first five passages are D2.1, D4.1, D1.1, D2.2, and D3.1. Maya may read only D1.1 and D3.1. Checking permissions after collecting those five therefore returns two results.
Maya’s next three eligible passages sit at positions 6, 7, and 8: D1.2, D3.2, and D1.3. Fetching eight candidates before filtering recovers all five target passages in this particular pile.
Fetching extra candidates is called overfetching. Eight works for this fixture. It is not a universal safe multiplier: a different query or a reader with narrower access can put the next allowed result much farther down.
Predict: move the cutoff from five to eight. Will Maya gain permission to D2, or will the clerk simply reach more sheets she was already allowed to read?
Move the cutoff through the ranked pile.
Fictional records. Browser simulation. No live model call.
The complete ranked pile
- 1D2.1× Denied98 pts
- 2D4.1× Denied96 pts
- 3D1.1✓ Allowed94 pts
- 4D2.2× Denied92 pts
- 5D3.1✓ Allowed90 ptscutoff
- 6D1.2✓ Allowed88 pts
- 7D3.2✓ Allowed86 pts
- 8D1.3✓ Allowed84 pts
- 9D4.2× Denied82 pts
- 10D3.3✓ Allowed80 pts
2 passages returned
5 target passages
40% recall2 of 5 target passages recovered.
The cutoff changes reach, never permission. Still missed: D1.2, D3.2, D1.3.
Inspect the input, rule, and output
Input / state
{
"principal": {
"id": "maya",
"name": "Maya",
"role": "Support",
"tenantId": "sachin",
"groups": [
"support"
]
},
"k": 5,
"fetchCount": 5,
"ranking": [
{
"id": "D2.1",
"title": "Engineering incident note",
"team": "Engineering",
"tenantId": "sachin",
"allowedGroups": [
"engineering"
],
"allowedUsers": [],
"match": 98,
"fact": "Sign-in requests time out under peak load.",
"parentId": "D2",
"aclVersion": 1
},
{
"id": "D4.1",
"title": "Launch cost review",
"team": "Finance",
"tenantId": "sachin",
"allowedGroups": [
"finance"
],
"allowedUsers": [],
"match": 96,
"fact": "The delay adds $24,000 to the internal launch budget.",
"parentId": "D4",
"aclVersion": 1
},
{
"id": "D1.1",
"title": "Customer launch update",
"team": "Shared",
"tenantId": "sachin",
"allowedGroups": [
"support",
"engineering"
],
"allowedUsers": [],
"match": 94,
"fact": "Orion’s launch has moved to 14 October.",
"parentId": "D1",
"aclVersion": 1
},
{
"id": "D2.2",
"title": "Engineering incident note",
"team": "Engineering",
"tenantId": "sachin",
"allowedGroups": [
"engineering"
],
"allowedUsers": [],
"match": 92,
"fact": "The identity team is fixing the session refresh path.",
"parentId": "D2",
"aclVersion": 1
},
{
"id": "D3.1",
"title": "Customer support FAQ",
"team": "Support",
"tenantId": "sachin",
"allowedGroups": [
"support"
],
"allowedUsers": [],
"match": 90,
"fact": "Existing workspaces stay available.",
"parentId": "D3",
"aclVersion": 1
},
{
"id": "D1.2",
"title": "Customer launch update",
"team": "Shared",
"tenantId": "sachin",
"allowedGroups": [
"support",
"engineering"
],
"allowedUsers": [],
"match": 88,
"fact": "The team is completing reliability checks.",
"parentId": "D1",
"aclVersion": 1
},
{
"id": "D3.2",
"title": "Customer support FAQ",
"team": "Support",
"tenantId": "sachin",
"allowedGroups": [
"support"
],
"allowedUsers": [],
"match": 86,
"fact": "Customers do not need to take any action.",
"parentId": "D3",
"aclVersion": 1
},
{
"id": "D1.3",
"title": "Customer launch update",
"team": "Shared",
"tenantId": "sachin",
"allowedGroups": [
"support",
"engineering"
],
"allowedUsers": [],
"match": 84,
"fact": "The launch update will be revised after the next readiness review.",
"parentId": "D1",
"aclVersion": 1
},
{
"id": "D4.2",
"title": "Launch cost review",
"team": "Finance",
"tenantId": "sachin",
"allowedGroups": [
"finance"
],
"allowedUsers": [],
"match": 82,
"fact": "Finance will reconcile launch costs after release.",
"parentId": "D4",
"aclVersion": 1
},
{
"id": "D3.3",
"title": "Customer support FAQ",
"team": "Support",
"tenantId": "sachin",
"allowedGroups": [
"support"
],
"allowedUsers": [],
"match": 80,
"fact": "Support will share the next approved launch update.",
"parentId": "D3",
"aclVersion": 1
}
]
}Decision rule
target = rank(all).filter(allowed).slice(0, k)
returned = rank(all).slice(0, fetchCount)
.filter(allowed).slice(0, k)
recall = recoveredTargetCount / target.lengthOutput
{
"target": [
"D1.1",
"D3.1",
"D1.2",
"D3.2",
"D1.3"
],
"returned": [
"D1.1",
"D3.1"
],
"recall": 0.4
}This view runs the same deterministic functions as the downloadable example. The short rule above summarizes the operation; the download contains the complete implementation.
Measure the evidence we recovered
For this lesson, recall against the allowed top-k baseline is:
target passages recovered / number of passages in the target
The numerator counts returned passages that belong to our exact allowed target. The denominator is the target’s size. With a cutoff of five, Maya gets two of the five target passages: 2 / 5 = 40%. With a cutoff of eight, she gets all five: 5 / 5 = 100%.
If the target is empty, the ratio is undefined. The lab shows “not applicable” rather than dividing by zero. This metric measures recovery of a specified target, not whether the resulting answer is correct or complete for every possible question.
Switch to Arun and try the same cutoff. Permissions change the target itself, so comparing everyone against Maya’s target would measure the wrong thing.
Real vector search adds another approximation
Picture a much larger records room. Searching every sheet may be too expensive, so the clerk uses shortcuts to locate promising shelves.
Vector search represents text as numerical vectors called embeddings. An approximate nearest neighbor search, often shortened to ANN, uses an index to find nearby vectors without exhaustively comparing every one. Approximation can miss relevant neighbors even before we consider permissions.
Our ranked-pile experiment does not simulate an ANN graph. It isolates the effect of a global candidate cutoff so you can recognize that failure mode. Actual filter behavior depends on the engine and mode.
For example, Azure AI Search applies preFilter during traversal on each shard. Its postFilter works on shard-level results before the merge; the preview strictPostFilter mode filters a global top-k result set. The lab’s global cutoff resembles that last ordering, not every mode called “post-filtering.” Azure vector filter modes.
In pgvector, filtering with approximate indexes is applied after the index scan. Its iterative scans can keep searching for more results, subject to configured scan limits. That is another concrete behavior to evaluate rather than assuming a fixed overfetch always fills the packet. pgvector filtering and iterative scans.
Let the badge travel into the search
Think of two clerks working in different wings of the records room. Each receives the same request and the same badge-derived condition. Each finds matching candidates in its own wing; their allowed results are then combined.
A shard is one portion of an index. Hierarchical Navigable Small World (HNSW) is a graph-based method for approximate nearest neighbor search. Azure’s preFilter mode applies the predicate during the HNSW search on each shard, then merges the local results. The condition participates in retrieval. Selective filters can mean more traversal work, so this is not a promise of fixed cost. Azure’s documented stages.
The illustration splits our ten passages across two fictional shards and requests two results. For Maya, shard A contributes D1.1 and D3.1, while shard B contributes D1.2 and D1.3. Merging by our illustrative scores keeps D1.1 and D3.1. Follow the stages, then switch to Arun and predict the changed packet.
The badge travels into the search.
Illustrated stages, fictional shard assignments, and exact toy rankings. This does not implement or simulate HNSW.
Company Sachin AND (user maya is directly granted OR a group matches support)
vectorFilterMode: "preFilter" · k: 2Shard A one portion of the index
Waiting for the query and filter.
Shard B one portion of the index
Waiting for the query and filter.
The application supplies a filter derived from trusted identity. An arbitrary client group list is not authority.
The drawn records expose fictional labels for teaching. They do not depict graph edges or a real traversal order. Selective filters may require more traversal work. Read Azure’s filter-mode stages and tradeoffs ↗
Inspect shard inputs and merged output
{
"principal": {
"id": "maya",
"name": "Maya",
"role": "Support",
"tenantId": "sachin",
"groups": [
"support"
]
},
"k": 2,
"shards": [
{
"name": "A",
"input": [
"D2.1",
"D1.1",
"D3.1",
"D3.2",
"D4.2"
],
"localResult": [
"D1.1",
"D3.1"
]
},
{
"name": "B",
"input": [
"D4.1",
"D2.2",
"D1.2",
"D1.3",
"D3.3"
],
"localResult": [
"D1.2",
"D1.3"
]
}
],
"globalResult": [
"D1.1",
"D3.1"
]
}This drawing uses exact sorting to make the merge inspectable. It does not reproduce Azure’s graph, navigation order, or approximate candidate discovery. Keeping those limits visible avoids teaching that every kind of prefiltering scans all allowed records, or that one diagram establishes every engine’s recall.
Keep the permission rule separate from the search budget
The security requirement is that denied passages stay out of this person’s context. The retrieval objective is to recover useful allowed evidence within the available latency and compute budget. A system can satisfy the first while performing poorly on the second.
Evaluate your chosen engine against an exact allowed baseline for representative queries and identities. Include people with narrow access, small requested result counts, and cases with fewer eligible records than k. Measure latency alongside recall. This toy pile does not estimate production timings or prove an engine’s recall.
Run the ranked-pile experiment
The download performs both orderings over the same ten records and asserts the worked results. Change the reader or k, then inspect the recomputed target before interpreting the ratio.
Run the same model yourself.
Node.js 20 or newer. No packages or API keys. Save the file, open a terminal in its directory, and run the command below.
Download part 3 · JavaScript ↓node sachin-permissions-3.mjsEXPECTED OUTPUT
Fetch 5, then filter: 2 of 5
Fetch 8, then filter: 5 of 5
Filter, then exact top 5: D1.1, D3.1, D1.2, D3.2, D1.3
Retrieval checks passed.Read the complete source and assertions
// Sachin RAG permissions, part 3. Node.js 20+. Fictional fixtures; fixed answers.
// @ts-check
/** Fictional teaching fixtures. This simulation is not an authorization boundary.
* @typedef {{id: string, name: string, role: string, tenantId: string, groups: readonly string[]}} Principal
* @typedef {{id: string, title: string, team: string, tenantId: string, allowedGroups: readonly string[], allowedUsers: readonly string[], match: number, fact: string}} Evidence
*/
/** @type {readonly Principal[]} */
export const PEOPLE = [
{ id: "maya", name: "Maya", role: "Support", tenantId: "sachin", groups: ["support"] },
{ id: "arun", name: "Arun", role: "Engineering", tenantId: "sachin", groups: ["engineering"] },
];
export const QUESTION = "Why is the Orion launch delayed?";
/** @type {readonly Evidence[]} */
export const DOCUMENTS = [
{
id: "D1", title: "Customer launch update", team: "Shared", tenantId: "sachin",
allowedGroups: ["support", "engineering"], allowedUsers: [], match: 87,
fact: "Orion’s launch has moved to 14 October while the team completes reliability checks.",
},
{
id: "D2", title: "Engineering incident note", team: "Engineering", tenantId: "sachin",
allowedGroups: ["engineering"], allowedUsers: [], match: 98,
fact: "Sign-in requests time out under peak load. The identity team is fixing the session refresh path.",
},
{
id: "D3", title: "Customer support FAQ", team: "Support", tenantId: "sachin",
allowedGroups: ["support"], allowedUsers: [], match: 82,
fact: "Existing workspaces stay available. Customers do not need to take any action.",
},
{
id: "D4", title: "Launch cost review", team: "Finance", tenantId: "sachin",
allowedGroups: ["finance"], allowedUsers: [], match: 91,
fact: "The delay adds $24,000 to the internal launch budget.",
},
];
// @example:policy
/** Identity must already come from a trusted session; this function only checks permission.
* @param {Principal | null} user @param {Evidence} chunk */
export function checkAccess(user, chunk) {
const hasIdentity = user !== null;
const sameCompany = hasIdentity && user.tenantId === chunk.tenantId;
const directGrant = hasIdentity && chunk.allowedUsers.includes(user.id);
const matchingGroups = user?.groups.filter(group => chunk.allowedGroups.includes(group)) ?? [];
const groupGrant = matchingGroups.length > 0;
const allowed = sameCompany && (directGrant || groupGrant);
const reason = !hasIdentity ? "No verified identity"
: !sameCompany ? "Different company"
: directGrant ? "Direct user grant"
: groupGrant ? `${matchingGroups.join(", ")} group grants access`
: "No matching permission";
return { hasIdentity, sameCompany, directGrant, groupGrant, matchingGroups, allowed, reason };
}
// @example:end-policy
/** @param {Principal | null} user @param {readonly Evidence[]} documents */
export function retrieveEvidence(user, documents = DOCUMENTS) {
return documents
.filter(chunk => checkAccess(user, chunk).allowed)
.sort((a, b) => b.match - a.match);
}
/** @param {Principal | null} user @param {readonly Evidence[]} documents */
export function buildAnswer(user, documents = DOCUMENTS) {
const evidence = retrieveEvidence(user, documents);
const context = evidence.map(chunk => `[${chunk.id}] ${chunk.title}\n${chunk.fact}`).join("\n\n");
// These are example request messages. This lesson makes no model call.
const messages = evidence.length ? [
{ role: "system", content: "Use the supplied evidence to answer the question. Cite each source ID. If the evidence is insufficient, say so." },
{ role: "user", content: `Question: ${QUESTION}\n\nPermitted context:\n${context}` },
] : [];
return {
evidence,
context,
messages,
// Assemble fixed facts to make the experiment reproducible, without a model.
sentences: evidence.map(({ id, fact }) => ({ sourceId: id, text: fact })),
emptyMessage: evidence.length ? null : "I don’t have accessible evidence to answer this question.",
};
}
/** Fictional, deterministic teaching models shared by the diagrams and downloads.
* @typedef {import('./rag-permissions.mjs').Principal} Principal
* @typedef {import('./rag-permissions.mjs').Evidence} Evidence
*/
export function hasPolicy(record) {
const ids = value => Array.isArray(value) && value.every(id => typeof id === "string" && id.length > 0);
return typeof record?.tenantId === "string" && record.tenantId.length > 0
&& ids(record.allowedGroups) && ids(record.allowedUsers);
}
export function ingestDocument(document, paragraphs, aclVersion = 1) {
if (!hasPolicy(document)) throw new Error("Missing or invalid permission metadata");
if (!Number.isInteger(aclVersion) || aclVersion < 1) throw new Error("Invalid policy version");
if (!Array.isArray(paragraphs) || paragraphs.some(text => typeof text !== "string" || !text.trim())) {
throw new Error("Chunks must contain nonempty text");
}
return paragraphs.map((fact, index) => ({
...document, id: `${document.id}.${index + 1}`, parentId: document.id,
fact, aclVersion, allowedGroups: [...document.allowedGroups], allowedUsers: [...document.allowedUsers],
}));
}
// Ten fictional passages extend the same four documents. Scores are illustrative, not vector distances.
/** @type {Array<[string, number, number, string]>} */
const rankingRows = [
["D2", 1, 98, "Sign-in requests time out under peak load."],
["D4", 1, 96, "The delay adds $24,000 to the internal launch budget."],
["D1", 1, 94, "Orion’s launch has moved to 14 October."],
["D2", 2, 92, "The identity team is fixing the session refresh path."],
["D3", 1, 90, "Existing workspaces stay available."],
["D1", 2, 88, "The team is completing reliability checks."],
["D3", 2, 86, "Customers do not need to take any action."],
["D1", 3, 84, "The launch update will be revised after the next readiness review."],
["D4", 2, 82, "Finance will reconcile launch costs after release."],
["D3", 3, 80, "Support will share the next approved launch update."],
];
export const RANKED_CHUNKS = rankingRows.map(([parentId, ordinal, match, fact]) => {
const parent = DOCUMENTS.find(document => document.id === parentId);
if (!parent) throw new Error("Unknown parent document");
return { ...parent, id: `${parentId}.${ordinal}`, parentId, match, fact, aclVersion: 1 };
});
/** Exact ranked-list experiment; it does not simulate an ANN graph. */
export function compareRetrieval(user = PEOPLE[0], k = 5, fetchCount = 5) {
const ranked = [...RANKED_CHUNKS].sort((a, b) => b.match - a.match);
const isAllowed = chunk => hasPolicy(chunk) && checkAccess(user, chunk).allowed;
const eligible = ranked.filter(isAllowed);
const ideal = eligible.slice(0, k);
const fetched = ranked.slice(0, fetchCount);
const after = fetched.filter(isAllowed).slice(0, k);
const recovered = after.filter(chunk => ideal.some(target => target.id === chunk.id)).length;
return { ranked, eligible, ideal, fetched, after, missed: ideal.filter(chunk => !after.some(hit => hit.id === chunk.id)),
recall: ideal.length ? recovered / ideal.length : null };
}
/** Each stage is a new snapshot; moving backwards never mutates the fixtures. */
export function revocationSnapshot(stage = 0, enforceCurrent = true) {
const original = DOCUMENTS[1];
const source = { ...original, allowedGroups: stage >= 1 ? [] : [...original.allowedGroups], aclVersion: stage >= 1 ? 2 : 1 };
const indexed = stage >= 2 ? { ...source } : { ...original, aclVersion: 1 };
const indexAllows = checkAccess(PEOPLE[1], indexed).allowed;
const currentAllows = checkAccess(PEOPLE[1], source).allowed;
const oldCacheExists = stage < 3;
const cacheVersion = 1;
const cacheUsable = oldCacheExists && cacheVersion === source.aclVersion && currentAllows;
return { source, indexed, indexAllows, currentAllows, oldCacheExists, cacheUsable,
contextIncludesD2: indexAllows && (!enforceCurrent || currentAllows),
cacheVersion, policyVersion: source.aclVersion };
}
/** Source IDs are looked up and authorized again on every source request. */
export function readSource(user, id, documents = DOCUMENTS) {
const document = documents.find(item => item.id === id && item.tenantId === user?.tenantId);
if (!document || !hasPolicy(document) || !checkAccess(user, document).allowed) {
return { status: 404, body: { message: "Source unavailable" } };
}
return { status: 200, body: { id: document.id, title: document.title, text: document.fact } };
}
export function inspectSurface(personId = "maya", surface = "citation", enforce = true) {
const person = PEOPLE.find(user => user.id === personId) ?? PEOPLE[0];
const permitted = buildAnswer(person);
const restricted = DOCUMENTS[1];
const safe = surface === "citation" ? readSource(person, "D2")
: surface === "preview" ? { status: 200, body: permitted.evidence.map(({ id, title }) => ({ id, title })) }
: surface === "shared-answer" ? { status: 200, body: permitted.sentences }
: { status: 200, body: { requestId: "demo-1", outcome: "allowed", returnedIds: permitted.evidence.map(chunk => chunk.id) } };
const unsafe = surface === "citation" ? { status: 200, body: { id: restricted.id, title: restricted.title, text: restricted.fact } }
: surface === "preview" ? { status: 200, body: DOCUMENTS.map(({ id, title }) => ({ id, title })) }
: surface === "shared-answer" ? { status: 200, body: buildAnswer(PEOPLE[1]).sentences }
: { status: 200, body: { question: QUESTION, candidates: DOCUMENTS, sessionToken: "fictional-demo-token" } };
return { person, response: enforce ? safe : unsafe, protected: enforce };
}
export function createServiceState() {
return {
// Public exercise credentials, not secrets or a real login system.
sessions: { "support-demo": structuredClone(PEOPLE[0]), "engineering-demo": structuredClone(PEOPLE[1]),
"other-tenant-demo": { ...structuredClone(PEOPLE[0]), tenantId: "other" } },
documents: structuredClone(DOCUMENTS), policyVersion: 1, policyAvailable: true,
cache: new Map(), audit: [],
};
}
export function revokeD2(state) {
const document = state.documents.find(item => item.id === "D2");
document.allowedGroups = [];
document.allowedUsers = [];
state.policyVersion += 1;
}
export function prepareServiceRequest(state, token, body = {}) {
const user = Object.hasOwn(state.sessions, token) ? structuredClone(state.sessions[token]) : null;
const base = { user, sessionKey: token, evidence: [], version: state.policyVersion, key: "", query: "", trace: ["Resolve identity from the server’s session table"] };
if (!user) return { ...base, error: 401, message: "Sign in required" };
if (typeof body?.question !== "string" || body.question.trim() !== QUESTION) {
return { ...base, error: 400, message: "This exercise supports the Orion launch question only" };
}
if (!state.policyAvailable) return { ...base, error: 503, message: "Permission check unavailable" };
if (state.documents.some(document => !hasPolicy(document))) {
return { ...base, error: 503, message: "Permission metadata unavailable" };
}
// Body-supplied groups and tenantId never participate in the decision.
const evidence = buildAnswer(user, state.documents).evidence;
const key = JSON.stringify(["fixed-generator-v1", state.policyVersion, user.tenantId, user.id, [...user.groups].sort(), QUESTION]);
return { ...base, error: 0, message: "", evidence, query: QUESTION, key,
trace: [...base.trace, "Ignore client permission claims", "Check tenant and current document grants", "Prepare permitted evidence"] };
}
export function finishServiceRequest(state, plan) {
const reply = (status, body, cacheHit = false) => {
// This audit record stays on the server. No token, prompt, title, or document body.
state.audit.push({ requestId: `request-${state.audit.length + 1}`, status,
policyVersion: state.policyVersion, returnedIds: status === 200 ? (body.sources ?? []).map(source => source.id) : [] });
return { status, body, cacheHit, trace: plan.trace };
};
if (plan.error) return reply(plan.error, { message: plan.message });
if (!state.policyAvailable) return reply(503, { message: "Permission check unavailable" });
if (plan.version !== state.policyVersion) return reply(409, { message: "Permissions changed; retry the request" });
// Recheck current membership too. The version check covers policy changes through revokeD2.
const current = Object.hasOwn(state.sessions, plan.sessionKey) ? state.sessions[plan.sessionKey] : null;
if (!current || current.id !== plan.user.id || current.tenantId !== plan.user.tenantId
|| JSON.stringify([...current.groups].sort()) !== JSON.stringify([...plan.user.groups].sort())
|| plan.evidence.some(chunk => {
const source = state.documents.find(document => document.id === chunk.id);
return !source || !hasPolicy(source) || !checkAccess(current, source).allowed;
})) return reply(409, { message: "Permissions changed; retry the request" });
if (!plan.evidence.length) return reply(200, { answer: "I don’t have accessible evidence to answer this question.", sources: [], modelCalled: false });
const cached = state.cache.get(plan.key);
// Compare dependencies too: a missed revision update must not restore an excluded source.
const selectedIds = plan.evidence.map(chunk => chunk.id);
if (cached && JSON.stringify(cached.sources.map(source => source.id)) === JSON.stringify(selectedIds)) {
return reply(200, structuredClone(cached), true);
}
// Deterministic generation seam: a real model integration needs separate output evaluation.
const response = { answer: plan.evidence.map(chunk => `${chunk.fact} [${chunk.id}]`).join("\n\n"),
sources: plan.evidence.map(({ id, title }) => ({ id, title })), modelCalled: false };
state.cache.set(plan.key, structuredClone(response));
return reply(200, response);
}
export function runServiceScenario(scenario = "maya") {
const state = createServiceState();
let token = scenario === "arun" || scenario === "in-flight" || scenario === "revoked" ? "engineering-demo" : "support-demo";
if (scenario === "missing-session") token = "unknown";
if (scenario === "other-tenant") token = "other-tenant-demo";
if (scenario === "outage") state.policyAvailable = false;
const body = { question: QUESTION, ...(scenario === "forged-groups" ? { groups: ["engineering"], tenantId: "other" } : {}) };
if (scenario === "revoked") {
finishServiceRequest(state, prepareServiceRequest(state, token, body));
revokeD2(state);
}
const plan = prepareServiceRequest(state, token, body);
if (scenario === "in-flight") revokeD2(state);
const response = finishServiceRequest(state, plan);
return { body, plan, response, audit: state.audit, cacheEntries: state.cache.size, policyVersion: state.policyVersion };
}
import assert from "node:assert/strict";
const short = compareRetrieval(PEOPLE[0], 5, 5);
const wider = compareRetrieval(PEOPLE[0], 5, 8);
assert.equal(short.after.length, 2);
assert.equal(short.recall, 0.4);
assert.equal(wider.after.length, 5);
assert.equal(wider.recall, 1);
console.log("Fetch 5, then filter: " + short.after.length + " of 5");
console.log("Fetch 8, then filter: " + wider.after.length + " of 5");
console.log("Filter, then exact top 5: " + short.ideal.map(chunk => chunk.id).join(", "));
console.log("Retrieval checks passed.");
The clerk now knows how to fill the packet from allowed evidence. But what if a folder’s permission changes after it was indexed? Continue to Part 4: access was revoked.