TLDR

  1. A context compiler turns a decision request into a typed evidence plan before retrieval, so the model never decides which identities, purposes, policies or source classes it is allowed to use.
  2. Its mechanism is a sequence of deterministic passes: parse, resolve, type-check, admit, plan and attest. Learned ranking can operate inside the admitted universe, but cannot widen it.
  3. The output is a context intermediate representation that names required propositions, authorised resources, applicable versions, retrieval routes, token budgets, omissions and the dependencies that invalidate the plan.
  4. Compilation does not repair bad identity data, stale entitlements or weak source governance. A precisely executed wrong policy can still produce a defensible-looking but incorrect evidence packet.
  5. Use a compiler when authority, applicability or evidential force varies across users and decisions. For a public, single-version, low-consequence corpus, ordinary retrieval may be the better design.

The answer that a good retriever should refuse to assemble

Maya is reviewing Atlas Fasteners, a fictional commercial customer. She asks a familiar question: “Can Atlas keep its overdraft while the annual review is open?” A hybrid retriever has plenty to work with. It finds the current group credit manual, the almost identical manual it replaced, a local transition addendum, the facility record, an expired covenant waiver, a relationship manager’s note saying renewal was requested, and a restricted investigation note that mentions the same customer and covenant.

Every item is relevant. That is the problem. Maya may read the facility record but not the investigation note. The new group manual is current, yet the local addendum preserves one provision for renewals until quarter end. The manager’s note is permitted and recent, but it does not prove that a waiver was approved. The expired waiver is authoritative for its historical interval and useless for today’s entitlement. A reranker can arrange these passages beautifully and still hand the model the wrong decision substrate.

The usual repair is to add more instructions: use only authorised data, prefer current policy, cite reliable sources, abstain when uncertain. Those instructions arrive too late. They ask the same probabilistic component that reads the evidence to decide which evidence it had authority to receive. They also compress different questions into one vague score: permission, applicability, source force, contradiction and coverage.

The core answer is to move evidence selection out of prompt craft and into compilation. A context compiler accepts a typed decision request, resolves its principal and resource scope, evaluates authorisation and applicability, opens the propositions that must be established, chooses retrieval routes and emits a bounded context intermediate representation. Search engines execute that representation. They may rank within it; they may not broaden it. The model receives admitted evidence, explicit gaps and an attestation of how the packet was produced.

This is a composite scenario, not a reported bank deployment. Its purpose is to expose a systems distinction. Retrieval asks, “Which items resemble the query?” Compilation asks, “Which items may participate in this decision, in what evidential role, under which budget and dependencies?” Those are not competing rankers. They are different layers of responsibility.

Part IThe wrong optimisation target

Retrieval-augmented generation gave language models an explicit non-parametric memory and a route to provenance and updateable knowledge. The original RAG work joined a sequence model to a dense index and showed gains on knowledge-intensive tasks.1 That mechanism answers an important question: how can a model condition generation on external material? It does not, by itself, answer who may see each resource, which version governs a particular decision or what evidence is sufficient to establish a proposition.

Longer context windows do not remove the need to select. In controlled multi-document and key-value tasks, Liu and colleagues found that performance could change sharply with the position of relevant material, often deteriorating when it appeared in the middle of a long context.2 More context therefore expands capacity without guaranteeing dependable use. In a consequential workflow, indiscriminately filling that capacity also increases exposure, contradiction and review cost.

A useful context item must pass three tests that ordinary similarity ranking tends to blur:

TestQuestionTypical authorityFailure if collapsed into relevance
Permission and purposeMay this principal use this resource for this task?Identity, entitlement and policy servicesA highly similar protected item enters candidate generation.
ApplicabilityDoes this version govern this entity, product, action and time?Policy registry, source state and precedence rulesA current but inapplicable rule displaces the governing one.
Evidential forceCan this source class support this proposition?Decision schema and evidence contractA note, slide or email is treated as proof of approval.

Relevance is useful only after the admissible universe exists. A score can order two authorised, applicable policy clauses. It cannot turn an email into an approval, a global policy into a local rule or a service account into the end user whose purpose justified the read.

From raw source estate to an authorised claim packet Policy, records, correspondence and notes flow through parse, resolve, type, admit, plan and attest stages. Rejected material leaves the pipeline before the model boundary. The output contains supported, contradicted and unknown proposition slots. Raw source estate System records Policy versions Correspondence Restricted notes 1 Parse request 2 Resolve identities and entities 3 Type-check decision 4 Admit by policy and applicability 5 Plan routes and budget 6 Emit and attest Context IR Claim packet Supported: limit Contradicted: status Unknown: waiver Attested dependencies Rejected before model boundary Model boundary From raw sources through six compiler passes to an authorised claim packet A stacked mobile diagram shows four source classes, six compiler passes, a rejection branch and a final claim packet. Raw source estate System records Policy versions Correspondence Restricted notes Context compiler 1Parse the decision request 2Resolve identities and entities 3Type-check the decision 4Admit by policy and applicability 5Plan routes and token budget 6Emit and attest Context IR model boundary Authorised claim packet Supported: limit, policy, status Contradicted: preserved for review Unknown: waiver evidence missing
Figure 1. Compile the evidence universe before ranking within it. The geometry is architectural and illustrative. It separates source admission from model reasoning; it does not imply that all implementations use one physical service.

Thought experiment: the identical query, a different caller

Freeze the words, time, customer and corpus. Maya asks about Atlas. Then an employee from an unrelated unit submits the identical sentence. A text retriever should produce the same similarity scores because the query and documents have not changed. A decision system should not produce the same candidate universe because the principal, case assignment and approved purpose have changed.

This single variation reveals why authorisation cannot be a prompt preference or a reranking feature. If “permission score” merely lowers a protected document rather than excluding it, the document can still enter traces, caches or model context when the candidate budget grows. NIST’s attribute-based access-control definition makes subject, object, action and environment attributes part of the access decision.3 Zero-trust guidance similarly centres explicit decisions about resources rather than ambient trust from network location.4 Neither source defines a context compiler, but both support the architectural boundary: permission is evaluated as policy, not inferred from semantic similarity.

Three independent admission lenses Six candidate documents are shown against relevance, permission and applicability, and evidence strength. Some highly relevant candidates fail permission or evidence strength. Only two candidates pass all three lenses. Candidate Relevance Permission + applicability Evidence force Restricted investigation note Superseded policy Manager email Current policy clause Facility system record Historical example ××✓✓✓✓ highhighlowhighhighcompare Passes all required lenses Three independent admission lenses on six candidates Six stacked cards show relevance, permission and applicability, and evidence force. Only the current policy and facility record pass all required lenses. A high score is only the first lens SCOREPASSFORCE Restricted investigation note 98×high Relevant, but forbidden for this principal and purpose. Superseded policy 91×high Authoritative text, but not governing this decision. Manager email 82✓low Permitted and current, but not proof of approval. Current policy clause 75✓high Passes permission, applicability and evidence class. Facility system record 66✓high Lower similarity, decisive evidence for the limit. Historical example 50✓compare Useful context only when clearly typed as comparison. Only admitted, proposition-sufficient sources reach the model boundary.
Figure 2. Relevance, admissibility and evidential force are orthogonal. Scores and labels are illustrative. A source may be highly relevant yet fail permission, applicability or proof strength.

The strongest objection: “Is this only access control plus RAG?”

Access control is necessary and insufficient. It can answer whether Maya may read a document. It does not usually say that an approved waiver object may support waiver_valid while a case note may only support waiver_requested. Retrieval can find both. A context compiler composes access, applicability, decision schema, evidence typing, budgets and replay dependencies into one executable plan.

The original contribution is therefore a systems separation, not a claim to have invented authorisation, provenance or query planning. Cedar, Zanzibar, XACML and Open Policy Agent each address policy or relationship evaluation at different layers.56 A compiler uses those capabilities as inputs and preserves their decisions while lowering a decision request into backend-specific reads. It becomes valuable when no single index, policy engine or prompt can carry the whole decision contract.

The argument in one passChart · flow
flowchart TD
  A["The answer that a good<br/>retriever should refuse to<br/>assemble"] --> B["Part I The wrong optimisation<br/>target"]
  B --> C["Part II Compile the evidence<br/>boundary"]
  C --> D["Part III Emit a<br/>decision-specific packet"]
  D -. "re-test" .-> A
  classDef first fill:#0a1424,stroke:#5B5CF6,stroke-width:2px,color:#e6f3ff
  classDef step fill:#0a1424,stroke:#3f7da0,stroke-width:1px,color:#e6f3ff
  class A first
  class B,C,D step
A structural route through The Context Compiler, from The answer that a good retriever should… to Compile the boundary, then let the model…. Reading use: follow the relation, then test it against the surrounding evidence and limits.

Part IICompile the evidence boundary

A conventional compiler translates a source programme into an executable representation while preserving meaning. The analogy is useful if its limits remain explicit. A context compiler does not prove arbitrary natural-language truth. It translates a governed decision request into a constrained acquisition and assembly plan. Its semantics are the authorised resource set, applicable rule set, accepted evidence classes, required proposition slots and release invariants.

Open Policy Agent can compile policy queries into an intermediate representation containing planned evaluation paths.7 That is an implementation precedent for separating a declarative request from its executable plan. The context intermediate representation proposed here is broader practitioner synthesis. It carries policy results, but also entity roots, evidence obligations, retrieval routes, budgets, omissions, provenance and invalidation dependencies.

A small formal model

Let S be the raw source estate, p the principal chain, d the decision request, t the decision time and q a required proposition. Three predicates decide whether a resource may enter the candidate universe: A for authorisation, G for governing applicability and E for accepted evidential class.

U(p,d,t) = { s in S | A(p,s,d,t) = permit and G(s,d,t) = applicable and exists q: E(s,q) = accepted }

The planner then selects a packet C contained in U. It tries to cover required propositions, preserve decisive contradictions and minimise tokens and retrieval cost under budget B. Changing B can alter which secondary evidence is included. It cannot admit a resource outside U. If a critical proposition has no acceptable resource, its state remains unknown. This is the operational meaning of “do not search first and justify later”.

The model is untrusted with respect to boundary expansion. It may suggest an alias, query paraphrase or likely evidence class. Those suggestions become typed input to deterministic validation. It may not create a principal, change purpose, remove a deny, invent an effective date or substitute a weaker source class for a required one.

Six passes, six distinct error owners

The passes matter because a single “retrieval failed” status hides where repair belongs. Parsing failures belong to the interaction contract. Resolution failures belong to identity or entity services. Type errors belong to the decision catalogue. Admission failures belong to authorisation, applicability or source governance. Planning failures belong to adapters and budgets. Attestation failures belong to release and cryptographic dependency handling.

Six-pass context compiler cutaway A stair-step diagram shows parse, resolve, type-check, admit, plan and attest. Each step lists its output and its safe failure response. Parserequest AST Resolvestable IDs Typedecision schema Admitbounded set Planroutes + budget Attestsigned IR Ambiguous intent: clarify Entity collision: stop Unsupported action: reject Policy conflict: route Budget infeasible: gap Dependency mismatch: recompile More concrete, less permissive Six vertical compiler passes with outputs and safe failures Parse, resolve, type-check, admit, plan and attest appear as a vertical sequence. Each pass names its output and its safe failure response. More concrete, less permissive 1 ParseOutput: request ASTAmbiguous intent: clarify 2 ResolveOutput: stable IDsEntity collision: stop 3 Type-checkOutput: decision schemaUnsupported action: reject 4 AdmitOutput: bounded source setPolicy conflict: route 5 PlanOutput: routes and budgetBudget infeasible: expose gap 6 AttestOutput: signed Context IRDependency mismatch: recompile Each failure has an owner. Do not collapse identity, policy, adapter and model defects into one “retrieval failed” status.
Figure 3. A failure should name the pass that could not preserve the contract. The sequence is illustrative; services may be distributed, but the release invariants must remain ordered.

Pass 1, parse. Convert the interaction into a DecisionRequest. The natural-language question is preserved as query material, while control fields arrive from authenticated workflow state. “Summarise this case” and “recommend continuation” are different decision types. A request that silently combines advice, approval and execution should not become one broad retrieval instruction.

Pass 2, resolve. Map names, pronouns and aliases to stable identifiers. The compiler can use a model to propose candidates, but canonical selection must be verified through an authorised interface or deterministic relationship. The phrase “the customer” cannot become whichever customer has the closest embedding.

Pass 3, type-check. Confirm that the decision type exists, that the workflow permits it and that its required proposition schema is known. A type error is preferable to a plausible context packet for an undefined action. Fine-grained structured authorisation requests, such as those represented by RFC 9396, illustrate why machine-readable purpose and resource detail are more expressive than a coarse scope string.8

Pass 4, admit. Intersect authorisation, resource roots, purpose, applicability, deny conditions and evidence-class rules. Deny must dominate across joins. If Maya can read a customer file and the service can search annual reviews, neither fact should erase a specific prohibition on investigation material. Predicate pushdown should constrain candidate generation where the backend supports it; an independent validator must still check each returned item before the model boundary.

Pass 5, plan. Choose one route per proposition. Exact identifiers and structured APIs suit facility state. Filtered lexical and semantic search suit policy clauses. Graph traversal may establish customer-facility relationships. The optimiser may trade latency, token use and redundancy only inside the admitted semantics. Post-filtering a broad top-k result can preserve secrecy if done inside a trusted boundary, yet it can destroy recall because forbidden high-scoring items occupy the candidate budget.

Pass 6, attest. Emit a versioned plan with policy hashes, index or source versions, query routes, evidence slots, rejected reason classes, expiry and invalidation dependencies. OAuth token exchange provides standard vocabulary for delegation and impersonation, although it does not design the domain entitlements themselves.9 Provenance can draw on W3C PROV-O’s distinction among entities, activities and agents.10 The attestation should carry stable references and reason codes, not replicate sensitive group memberships or denied content into an audit leak.

The context intermediate representation

The intermediate representation is the contract between governance and retrieval implementation. It is deliberately backend-neutral. A SQL adapter, graph adapter and vector adapter can all lower the same proposition and policy constraints into different queries. Conformance tests then ask whether each adapter preserves the admitted set, not whether its query text looks similar.

Figure 4. The IR records semantics that a prompt cannot safely improvise. This authored specimen is a proposed schema, not an industry standard. Sensitive attributes should be represented by bounded references rather than copied into routine traces.

Worked example: a four-source refund desk

Consider a smaller decision: “May order 481 receive a refund without manager approval?” The corpus contains refund policy version 3, superseded version 2, the order record and a customer email asking for an exception. The caller is a support agent assigned to order 481. The evidence contract requires three propositions: delivery date from the order system, ordinary refund rule from the current policy and any approved exception from an approval object.

Parse identifies refund.eligibility. Resolve binds order 481 and its customer. Type-check opens the three proposition slots. Admit excludes policy version 2 because it is superseded. The email is permitted and may support exception_requested, but its source class cannot support exception_approved. Plan uses a structured order lookup and a filtered policy read. Emit marks delivery date and ordinary rule as supported, while manager approval remains unknown.

A plain retriever might produce the same final answer in this tiny case. The value of compilation is that its correctness does not depend on the email being ranked below the policy. Add a phrase such as “manager already approved this” to the email and semantic confidence may rise. The evidence type does not change. The compiler still refuses the substitution.

Part IIIEmit a decision-specific packet

A packet is not complete because it fills the context window. It is complete when every required proposition has a visible evidential state. The minimum useful states are supported, contradicted and unknown. Some domains need finer distinctions such as not_applicable, temporarily_unavailable or reserved_for_human. The crucial property is that absence does not silently become false and weak evidence does not silently become proof.

An evidence contract names the proposition, accepted source classes, precedence rules, contradiction treatment and whether a gap blocks the decision. For Atlas, a facility-system record can establish the approved limit. A signed approval may corroborate it. An email summary cannot establish it alone. A valid waiver object can establish waiver status. A relationship-manager note can explain that renewal was requested, but it cannot promote the waiver slot from unknown to supported.

Decision-specific evidence beats a generic top-k budget. A context window is a scarce common resource. Without proposition budgets, five passages repeating a popular policy clause can displace the one workflow event needed to establish review status. The compiler allocates capacity by evidential obligation, then permits ranking within each slot.

Figure 5. Allocate context by proposition and evidence class, not by a flat passage count. Percentages are illustrative. Real budgets should be calibrated against task-level coverage, latency, model behaviour and review needs.

Thought experiment: move the decision by one day

Keep Maya, Atlas, the question and every document unchanged. Move the decision time from the last day of the quarter to the first day of the next. The local transition addendum expires at midnight. Similarity scores do not notice. File modification times may not change. Yet the governing policy set changes because applicability is a function of decision class and valid time, not merely document recency.

This thought experiment isolates a second causal feature. The correct packet can change even when text and identity remain constant. A compiler represents effective intervals, supersession and transition rules as plan dependencies. When the clock or relevant event crosses a boundary, the old plan becomes invalid and must be recompiled. Paper 110’s deeper problem is reconstructing what was knowable and applicable at a historical decision moment; Paper 111 treats freshness and supersession in detail. Here the narrower point is that time belongs in the IR rather than in a “prefer recent” prompt.

Worked example: walk the Atlas case through the packet

The annual-review workspace supplies Maya’s verified identity, service identity, case assignment, entity and purpose. The compiler does not infer those fields from chat history. It parses the request as a continuation assessment, not an approval or account action. Resolution confirms that facility F-102 belongs to Atlas and the case concerns the same booking entity.

Admission permits the facility record, annual-review events, applicable credit policy and approved waiver objects. It excludes the investigation note. The interaction receives a bounded denial reason, not a disclosure that such a record exists. Applicability selects the group manual and the local transition provision; it rejects the superseded general wording. The planner opens slots for facility limit, review status, waiver validity, continuation rule and any authorised hard stop.

The facility and workflow reads support their slots. The current policy supports the continuation rule. The waiver fails. The last approved object expired two days earlier. The manager’s note says renewal was requested, which is relevant to the narrative and insufficient for the required source class. The resulting packet states waiver_valid: unknown. The model may explain that continued availability cannot be established from admitted evidence and point to the approved resolution route. It may not convert a pending request into an approval.

The compiler has not made a credit judgement. It has changed the decision surface. Maya sees the exact unresolved proposition rather than a fluent synthesis of mixed-authority text. If an authorised officer later approves a waiver, the new source event invalidates the old packet. The request is compiled again against the changed state.

A controlled synthetic comparison

The accompanying executable artefact builds a deliberately contaminated eight-item corpus. Four high-scoring candidates are unsafe or unusable: another entity’s facility record, a superseded policy, an email masquerading as approval and a restricted waiver. Four lower-scoring candidates satisfy the evidence contract. Plain top-5 retrieval passes five items, of which four are unsafe under the test policy, and covers three of four proposition names only because weak or forbidden sources happen to mention them. Filtering those five after retrieval removes the unsafe items but leaves one proposition covered. Compiling before ranking returns four admitted items and covers all four.

These numbers are synthetic, not a general performance claim. They demonstrate a structural failure: post-filtering cannot recover evidence that never entered the original top-k set. A recent controlled study of multitenant agentic retrieval reached a related result under its own assumptions. Attribute-based gating reduced cross-tenant leakage to zero in its synthetic testbed, while post-retrieval filtering suffered severe recall loss as corpus size grew; the authors explicitly excluded compromised servers, side channels and model prior knowledge from their guarantee.11 The study supports pre-retrieval enforcement, not every detail of the compiler proposed here.

Synthetic comparison of retrieval orders Three groups compare plain top-k, post-filter top-k and compiled pre-filter retrieval. Bars show proposition coverage and unsafe items passed. Compiled pre-filter has full coverage and no unsafe items in this synthetic fixture. Proposition coverage 0%25%50%75%100% 75% 25% 100% Plain top-kFilter after top-kCompile before ranking 4 unsafe items passed 0 unsafe, 1 item left 0 unsafe, 4 items Synthetic coverage and unsafe-item comparison Three large vertical bars show 75 percent coverage with four unsafe items for plain top-k, 25 percent coverage with no unsafe items after filtering, and 100 percent coverage with no unsafe items for compile-before-ranking. Measured synthetic fixture Proposition coverage 0255075100 75% 25% 100% Plain top-k4 unsafe Post-filter0 unsafe Compile first0 unsafe Post-filtering cannot recover evidence omitted from the original candidate budget.
Figure 6. Post-filtering can be secure and still lose the answer. Values are measured from the article’s eight-resource synthetic fixture. They demonstrate one constructed mechanism, not an estimate of real-world prevalence.

Negative control: when the compiler adds little

Take a public help-centre bot over one openly licensed appliance manual. There is one product version, no per-user access variation, no conflicting policy authority, no private case data and low consequence if a passage is omitted. The requested output is explanatory, not an approval or action. A well-tested lexical or hybrid retriever with citations may be enough. The compiler would repeat fixed predicates, add operational dependencies and create a misleading impression of assurance without changing the admissible set.

This negative control matters because “compile everything” can become architecture theatre. A context compiler earns its cost only when at least one of three dimensions varies materially: who may use a resource and for what purpose, which rule or state applies to the decision, or what source class can establish the required claim. If those dimensions are invariant and consequence is low, simpler retrieval is easier to test and operate.

How the claim changes under pressureChart · state
stateDiagram-v2
  [*] --> Claim
  state "Thought experiment: the<br/>identical query, a different<br/>caller" as Claim
  state "The strongest objection: “Is<br/>this only access control plus<br/>RAG?”" as Test
  state "A small formal model" as Revision
  Claim --> Test: expose mechanism
  Test --> Decision: evidence survives
  Test --> Revision: rival survives
  Revision --> Claim: narrow or rebuild
  Decision --> [*]
The revision loop begins with Thought experiment, the identical query,… and keeps counterexamples inside the reasoning path. Reading use: follow the relation, then test it against the surrounding evidence and limits.

Part IVBuild, break and operate it

The compiler belongs on the trusted side of the model boundary. It should receive verified workflow fields directly, call authoritative identity and policy services, and emit a plan that retrieval adapters cannot widen. The model may generate candidate query terms inside a restricted grammar. Retrieved documents are data. Instructions embedded in them cannot change the principal, purpose, tool set, output destination or action authority.

Indirect prompt injection remains a residual risk because admitted content can contain adversarial instructions. OWASP identifies external content as a route for prompt injection and recommends layered controls.12 Compilation contributes a hard containment property: content cannot acquire permission or alter the plan. It does not guarantee that a model will interpret every admitted passage correctly. Output claim checking, tool authorisation and action controls still sit downstream.

Cloud retrieval products expose useful primitives. Amazon Bedrock Knowledge Bases documents metadata filters, and Azure AI Search documents document-level security trimming patterns.1314 Those features are adapters, not the compiler. The application still has to decide which identity attributes, purpose, legal entity, effective interval, evidence class and denial rules belong in the plan. It must also test that each backend preserves them.

The failure boundary

Explicit boundary A context compiler is only as correct as its identity facts, resource metadata, policy encoding, applicability registry and evidence contract. It can deterministically produce the wrong packet when any of those inputs are wrong or incomplete. Compilation makes the error reproducible and attributable; it does not make weak governance true.

The most dangerous failure is deterministic wrongness. Suppose a policy owner forgets to add “approved committee record” to the evidence classes for waiver validity. The compiler consistently returns unknown even when valid evidence exists. That is an availability and decision-quality defect. Reverse the mistake and accept “case note” as proof; the compiler consistently promotes requests into approvals. The system may look more auditable precisely because it is wrong in the same way every time.

Determinism transfers the burden from model behaviour to contract quality. Release evidence must therefore cover source-to-index metadata, policy fixtures, decision schemas and adapter equivalence. A model benchmark cannot substitute for these tests.

Context compiler failure surface A two-dimensional surface plots source and entitlement metadata quality against evidence contract completeness. Low values lead to refusal or deterministic wrongness. Only the upper-right region supports trustworthy compilation. Trustworthy compilation Deterministic wrongness Refuse or quarantine Evidence-contract completeness → Source and entitlement metadata quality → lowhigh lowhigh Failure surface for source metadata and evidence contract quality A square gradient surface moves from refuse and quarantine at low metadata and contract quality, through deterministic wrongness, to trustworthy compilation at high quality on both axes. Compiler trust depends on governed inputs Trustworthy compilation Deterministic wrongness Refuse or quarantine Evidence-contract completeness → Metadata quality → lowhigh Reproducible output is not evidence that the evidence contract is correct.
Figure 7. A compiler cannot rise above its governed inputs. The boundary is illustrative, not measured. Moving right means improving proposition and evidence contracts; moving up means improving identity, entitlement, version and lineage metadata.

Test the authorised set before answer quality

Begin with deterministic fixtures. For each principal, purpose, resource root and decision time, enumerate the expected admitted set. Include nested groups, revoked delegation, information barriers, cross-entity assignments, policy transitions, deletion and unknown classification. Mutate one predicate at a time. Remove an entity constraint, reorder policy rules, replay an expired plan or relabel a chunk. Any unapproved widening should block release.

Then test the evidence contract. Hide one authoritative source. Insert a highly similar but inapplicable policy. Supply only secondary evidence. Add a contradiction from a higher-precedence source. The expected output should be unknown, contradicted or refused, not a lower confidence score attached to a complete-sounding answer. Finally test every adapter against the same IR fixtures. A graph traversal and a vector query may be operationally different while remaining semantically equivalent.

Only after these controls pass should the team measure retrieval recall, reranking quality and answer usefulness. The order matters. A fluent answer can hide a forbidden read. A highly cited answer can still rely on an inapplicable version. A low acceptance rate can be caused by missing metadata rather than model weakness.

Test familyPrimary questionRelease evidenceSafe failure
Admission soundnessDid any forbidden resource cross the model boundary?Zero false admissions in deterministic security fixturesBlock affected plan or adapter
Admission completenessWere legitimate resources denied?False-denial rate by role, case and source classRoute to source or policy owner
ApplicabilityWas the governing version selected?Golden transitions and precedence casesExpose conflict, do not rank through it
Evidence typingDoes every supported claim have an accepted source class?No unsupported critical propositionMark unknown or contradicted
Adapter equivalenceDid every backend preserve the same semantics?Expected set equality plus rejection reasonsDisable non-conforming adapter
Replay resistanceCan a stale plan survive changed dependencies?All revocation and expiry tests passInvalidate and recompile

Operate dependencies, not only latency

A plan cache keyed by query text is unsafe. The key or dependency graph must include principal and delegation version, purpose, resource roots, policy bundle, relevant entitlement state, case state, applicable time and source or index versions. A cache miss costs time. A stale permit can disclose data. When revocation propagation is uncertain, high-sensitivity reads should re-evaluate or stop rather than “permit on error”.

Operational telemetry should be pass-specific: parse rejection, entity ambiguity, policy conflict, unsafe admission, permission mismatch at independent validation, evidence-slot coverage, unsupported proposition, compilation latency, invalidation lag and manual fallback. Preserve denominators and distinguish unknown from system failure. A rise in unknown waiver status may reflect missing approval objects, not a weaker language model.

Logs are another source boundary. A trace that captures rejected snippets, user group memberships or deny-rule bodies can recreate the leak that compilation prevented. Routine receipts should carry stable resource identifiers, policy and content hashes, reason classes and dependency versions. Privileged investigation can resolve those references through a separately authorised path.

Executable artefact: a minimal Context IR compiler

The Python laboratory below uses only the standard library. It defines a typed request, evidence requirements, resources and a compiler. The positive case admits a facility record, workflow event and current policy. The negative cases exclude a restricted note, superseded policy and wrong evidence class. Assertions enforce the token budget and require every admitted resource to be authorised, applicable and relevant to an evidence slot.

Expected result The waiver slot remains unknown. The synthetic benchmark records 100% proposition coverage and zero unsafe items for compilation before ranking, compared with 25% coverage after filtering the original top-five candidates. These are fixture results, not production estimates.
Open the complete runnable Python artefact
from __future__ import annotations

from dataclasses import asdict, dataclass
from datetime import date
from enum import Enum
import hashlib
import json
from typing import Iterable


class EvidenceState(str, Enum):
    SUPPORTED = "supported"
    CONTRADICTED = "contradicted"
    UNKNOWN = "unknown"


@dataclass(frozen=True)
class DecisionRequest:
    decision_type: str
    principal: str
    roles: frozenset[str]
    purpose: str
    entity_id: str
    decision_date: date
    token_budget: int


@dataclass(frozen=True)
class EvidenceRequirement:
    proposition: str
    accepted_classes: tuple[str, ...]
    required: bool = True


@dataclass(frozen=True)
class Resource:
    resource_id: str
    entity_id: str
    allowed_roles: frozenset[str]
    allowed_purposes: frozenset[str]
    evidence_class: str
    supports: frozenset[str]
    contradicts: frozenset[str]
    valid_from: date
    valid_to: date | None
    superseded: bool
    tokens: int
    relevance: float
    content: str


@dataclass(frozen=True)
class EvidenceSlot:
    proposition: str
    state: EvidenceState
    resource_ids: tuple[str, ...]
    reason: str


@dataclass(frozen=True)
class ContextIR:
    plan_id: str
    decision_type: str
    principal: str
    purpose: str
    entity_id: str
    decision_date: str
    token_budget: int
    tokens_used: int
    admitted_resource_ids: tuple[str, ...]
    rejected: tuple[tuple[str, str], ...]
    evidence_slots: tuple[EvidenceSlot, ...]


def authorised(req: DecisionRequest, resource: Resource) -> bool:
    return bool(req.roles & resource.allowed_roles) and req.purpose in resource.allowed_purposes


def applicable(req: DecisionRequest, resource: Resource) -> bool:
    within_time = resource.valid_from <= req.decision_date and (
        resource.valid_to is None or req.decision_date <= resource.valid_to
    )
    return resource.entity_id == req.entity_id and within_time and not resource.superseded


def compile_context(
    req: DecisionRequest,
    requirements: Iterable[EvidenceRequirement],
    resources: Iterable[Resource],
) -> ContextIR:
    """Compile a request into a bounded, typed context intermediate representation."""
    required = tuple(requirements)
    accepted_by_prop = {r.proposition: set(r.accepted_classes) for r in required}
    candidates: list[Resource] = []
    rejected: list[tuple[str, str]] = []

    for item in resources:
        if not authorised(req, item):
            rejected.append((item.resource_id, "not_authorised"))
            continue
        if not applicable(req, item):
            rejected.append((item.resource_id, "not_applicable"))
            continue
        typed_props = {
            p for p in item.supports | item.contradicts
            if p in accepted_by_prop and item.evidence_class in accepted_by_prop[p]
        }
        if not typed_props:
            rejected.append((item.resource_id, "wrong_evidence_class"))
            continue
        candidates.append(item)

    # Prefer resources that cover required propositions, then higher relevance,
    # while never exceeding the deterministic token budget.
    uncovered = {r.proposition for r in required}
    admitted: list[Resource] = []
    tokens_used = 0
    remaining = candidates[:]
    while remaining and uncovered:
        remaining.sort(
            key=lambda x: (
                len((x.supports | x.contradicts) & uncovered),
                x.relevance,
                -x.tokens,
                x.resource_id,
            ),
            reverse=True,
        )
        item = remaining.pop(0)
        covered = (item.supports | item.contradicts) & uncovered
        if not covered:
            break
        if tokens_used + item.tokens > req.token_budget:
            rejected.append((item.resource_id, "budget_exceeded"))
            continue
        admitted.append(item)
        tokens_used += item.tokens
        uncovered -= covered

    # Any remaining typed candidates are omitted rather than filling the window.
    admitted_ids = {x.resource_id for x in admitted}
    for item in remaining:
        if item.resource_id not in admitted_ids:
            rejected.append((item.resource_id, "not_needed_for_contract"))

    slots: list[EvidenceSlot] = []
    for requirement in required:
        support_ids = tuple(
            x.resource_id for x in admitted
            if requirement.proposition in x.supports
            and x.evidence_class in requirement.accepted_classes
        )
        contradiction_ids = tuple(
            x.resource_id for x in admitted
            if requirement.proposition in x.contradicts
            and x.evidence_class in requirement.accepted_classes
        )
        if support_ids and contradiction_ids:
            slots.append(EvidenceSlot(
                requirement.proposition,
                EvidenceState.CONTRADICTED,
                support_ids + contradiction_ids,
                "admissible sources disagree",
            ))
        elif contradiction_ids:
            slots.append(EvidenceSlot(
                requirement.proposition,
                EvidenceState.CONTRADICTED,
                contradiction_ids,
                "admissible evidence contradicts the proposition",
            ))
        elif support_ids:
            slots.append(EvidenceSlot(
                requirement.proposition,
                EvidenceState.SUPPORTED,
                support_ids,
                "supported by an accepted evidence class",
            ))
        else:
            slots.append(EvidenceSlot(
                requirement.proposition,
                EvidenceState.UNKNOWN,
                (),
                "no admitted resource satisfies the evidence contract",
            ))

    fingerprint = {
        "request": {
            "decision_type": req.decision_type,
            "principal": req.principal,
            "purpose": req.purpose,
            "entity_id": req.entity_id,
            "decision_date": req.decision_date.isoformat(),
        },
        "admitted": sorted(x.resource_id for x in admitted),
        "slots": [(s.proposition, s.state.value, s.resource_ids) for s in slots],
    }
    plan_id = hashlib.sha256(
        json.dumps(fingerprint, sort_keys=True).encode("utf-8")
    ).hexdigest()[:12]

    ir = ContextIR(
        plan_id=plan_id,
        decision_type=req.decision_type,
        principal=req.principal,
        purpose=req.purpose,
        entity_id=req.entity_id,
        decision_date=req.decision_date.isoformat(),
        token_budget=req.token_budget,
        tokens_used=tokens_used,
        admitted_resource_ids=tuple(x.resource_id for x in admitted),
        rejected=tuple(sorted(set(rejected))),
        evidence_slots=tuple(slots),
    )

    # Release invariants: the model cannot waive these checks.
    assert ir.tokens_used <= ir.token_budget
    assert all(authorised(req, x) and applicable(req, x) for x in admitted)
    assert all(
        any(
            slot.proposition in x.supports | x.contradicts
            and x.evidence_class in accepted_by_prop[slot.proposition]
            for slot in ir.evidence_slots
        )
        for x in admitted
    )
    return ir


def synthetic_benchmark() -> dict[str, dict[str, float | int]]:
    """Compare three retrieval orders on a deliberately contaminated corpus."""
    req = DecisionRequest(
        "credit.review.continuation", "maya", frozenset({"credit_analyst"}),
        "annual_credit_review", "ATLAS-IN-01", date(2026, 8, 30), 1000,
    )
    requirements = (
        EvidenceRequirement("facility_limit", ("facility_record",)),
        EvidenceRequirement("review_open", ("workflow_event",)),
        EvidenceRequirement("waiver_valid", ("approved_waiver",)),
        EvidenceRequirement("continuation_rule", ("policy_clause",)),
    )
    resources = (
        Resource("other-tenant-limit", "OTHER", frozenset({"credit_analyst"}), frozenset({"annual_credit_review"}), "facility_record", frozenset({"facility_limit"}), frozenset(), date(2025,1,1), None, False, 100, 1.00, "other tenant"),
        Resource("old-policy", "ATLAS-IN-01", frozenset({"credit_analyst"}), frozenset({"annual_credit_review"}), "policy_clause", frozenset({"continuation_rule"}), frozenset(), date(2025,1,1), None, True, 100, 0.99, "old policy"),
        Resource("email-waiver", "ATLAS-IN-01", frozenset({"credit_analyst"}), frozenset({"annual_credit_review"}), "case_note", frozenset({"waiver_valid"}), frozenset(), date(2026,8,1), None, False, 100, 0.98, "email says approved"),
        Resource("restricted-waiver", "ATLAS-IN-01", frozenset({"financial_crime"}), frozenset({"investigation"}), "approved_waiver", frozenset({"waiver_valid"}), frozenset(), date(2026,8,1), None, False, 100, 0.97, "restricted"),
        Resource("facility", "ATLAS-IN-01", frozenset({"credit_analyst"}), frozenset({"annual_credit_review"}), "facility_record", frozenset({"facility_limit"}), frozenset(), date(2025,1,1), None, False, 100, 0.96, "facility"),
        Resource("workflow", "ATLAS-IN-01", frozenset({"credit_analyst"}), frozenset({"annual_credit_review"}), "workflow_event", frozenset({"review_open"}), frozenset(), date(2026,8,1), None, False, 100, 0.80, "workflow"),
        Resource("current-policy", "ATLAS-IN-01", frozenset({"credit_analyst"}), frozenset({"annual_credit_review"}), "policy_clause", frozenset({"continuation_rule"}), frozenset(), date(2026,7,22), None, False, 100, 0.79, "current policy"),
        Resource("approved-waiver", "ATLAS-IN-01", frozenset({"credit_analyst"}), frozenset({"annual_credit_review"}), "approved_waiver", frozenset({"waiver_valid"}), frozenset(), date(2026,8,1), date(2026,9,30), False, 100, 0.78, "approved waiver"),
    )
    accepted = {r.proposition: set(r.accepted_classes) for r in requirements}

    def is_typed(item: Resource) -> bool:
        return any(
            p in accepted and item.evidence_class in accepted[p]
            for p in item.supports | item.contradicts
        )

    def metrics(items: list[Resource]) -> dict[str, float | int]:
        covered = set().union(*(x.supports | x.contradicts for x in items)) if items else set()
        unsafe = sum(not (authorised(req, x) and applicable(req, x) and is_typed(x)) for x in items)
        return {
            "items_passed": len(items),
            "unsafe_items": unsafe,
            "coverage_percent": round(100 * len(covered & set(accepted)) / len(accepted)),
        }

    top_five = sorted(resources, key=lambda x: x.relevance, reverse=True)[:5]
    post_filtered = [x for x in top_five if authorised(req, x) and applicable(req, x) and is_typed(x)]
    compiled_ids = set(compile_context(req, requirements, resources).admitted_resource_ids)
    pre_filtered = [x for x in resources if x.resource_id in compiled_ids]
    return {
        "plain_top_k": metrics(top_five),
        "post_filter_top_k": metrics(post_filtered),
        "compiled_pre_filter": metrics(pre_filtered),
    }


def demo() -> ContextIR:
    req = DecisionRequest(
        decision_type="credit.review.continuation",
        principal="maya",
        roles=frozenset({"credit_analyst"}),
        purpose="annual_credit_review",
        entity_id="ATLAS-IN-01",
        decision_date=date(2026, 8, 30),
        token_budget=760,
    )
    requirements = (
        EvidenceRequirement("facility_limit", ("facility_record",)),
        EvidenceRequirement("review_open", ("workflow_event",)),
        EvidenceRequirement("waiver_valid", ("approved_waiver", "committee_record")),
        EvidenceRequirement("continuation_rule", ("policy_clause",)),
    )
    resources = (
        Resource(
            "facility-F102", "ATLAS-IN-01", frozenset({"credit_analyst"}),
            frozenset({"annual_credit_review"}), "facility_record",
            frozenset({"facility_limit"}), frozenset(), date(2025, 1, 1), None,
            False, 180, 0.78, "Approved overdraft limit: 2.0m.",
        ),
        Resource(
            "workflow-E88", "ATLAS-IN-01", frozenset({"credit_analyst"}),
            frozenset({"annual_credit_review"}), "workflow_event",
            frozenset({"review_open"}), frozenset(), date(2026, 8, 1), None,
            False, 90, 0.69, "Annual review status: OPEN.",
        ),
        Resource(
            "policy-P3", "ATLAS-IN-01", frozenset({"credit_analyst"}),
            frozenset({"annual_credit_review"}), "policy_clause",
            frozenset({"continuation_rule"}), frozenset(), date(2026, 7, 22), None,
            False, 260, 0.74, "Continuation requires a valid waiver when covenant breach persists.",
        ),
        Resource(
            "policy-P2", "ATLAS-IN-01", frozenset({"credit_analyst"}),
            frozenset({"annual_credit_review"}), "policy_clause",
            frozenset({"continuation_rule"}), frozenset(), date(2025, 1, 1), None,
            True, 240, 0.99, "Superseded continuation wording.",
        ),
        Resource(
            "rm-note-N7", "ATLAS-IN-01", frozenset({"credit_analyst"}),
            frozenset({"annual_credit_review"}), "case_note",
            frozenset({"waiver_valid"}), frozenset(), date(2026, 8, 29), None,
            False, 120, 0.96, "The relationship manager says renewal was requested.",
        ),
        Resource(
            "investigation-X9", "ATLAS-IN-01", frozenset({"financial_crime"}),
            frozenset({"investigation"}), "investigation_note",
            frozenset({"waiver_valid"}), frozenset(), date(2026, 8, 20), None,
            False, 150, 1.00, "Restricted investigation note.",
        ),
    )
    ir = compile_context(req, requirements, resources)

    # Positive and negative cases.
    assert set(ir.admitted_resource_ids) == {"facility-F102", "workflow-E88", "policy-P3"}
    assert "investigation-X9" not in ir.admitted_resource_ids
    assert "policy-P2" not in ir.admitted_resource_ids
    waiver = next(s for s in ir.evidence_slots if s.proposition == "waiver_valid")
    assert waiver.state is EvidenceState.UNKNOWN
    return ir


if __name__ == "__main__":
    result = demo()
    payload = asdict(result)
    payload["evidence_slots"] = [
        {**asdict(slot), "state": slot.state.value} for slot in result.evidence_slots
    ]
    payload["synthetic_benchmark"] = synthetic_benchmark()
    print(json.dumps(payload, indent=2, sort_keys=True))
Open the deterministic output
{
  "admitted_resource_ids": [
    "facility-F102",
    "policy-P3",
    "workflow-E88"
  ],
  "decision_date": "2026-08-30",
  "decision_type": "credit.review.continuation",
  "entity_id": "ATLAS-IN-01",
  "evidence_slots": [
    {
      "proposition": "facility_limit",
      "reason": "supported by an accepted evidence class",
      "resource_ids": [
        "facility-F102"
      ],
      "state": "supported"
    },
    {
      "proposition": "review_open",
      "reason": "supported by an accepted evidence class",
      "resource_ids": [
        "workflow-E88"
      ],
      "state": "supported"
    },
    {
      "proposition": "waiver_valid",
      "reason": "no admitted resource satisfies the evidence contract",
      "resource_ids": [],
      "state": "unknown"
    },
    {
      "proposition": "continuation_rule",
      "reason": "supported by an accepted evidence class",
      "resource_ids": [
        "policy-P3"
      ],
      "state": "supported"
    }
  ],
  "plan_id": "8fa9f7452d5d",
  "principal": "maya",
  "purpose": "annual_credit_review",
  "rejected": [
    [
      "investigation-X9",
      "not_authorised"
    ],
    [
      "policy-P2",
      "not_applicable"
    ],
    [
      "rm-note-N7",
      "wrong_evidence_class"
    ]
  ],
  "synthetic_benchmark": {
    "compiled_pre_filter": {
      "coverage_percent": 100,
      "items_passed": 4,
      "unsafe_items": 0
    },
    "plain_top_k": {
      "coverage_percent": 75,
      "items_passed": 5,
      "unsafe_items": 4
    },
    "post_filter_top_k": {
      "coverage_percent": 25,
      "items_passed": 1,
      "unsafe_items": 0
    }
  },
  "token_budget": 760,
  "tokens_used": 530
}

Decision instrument: when compilation earns its cost

Use two axes for the first architecture decision. The vertical axis is consequence if the wrong source enters or the right source is omitted. The horizontal axis is heterogeneity in authority, applicability and evidence force. Ordinary retrieval is strongest in the lower-left. A compiler becomes the default as either dimension rises, and becomes difficult to avoid when both are high.

Decision phase diagram for choosing a context compiler A two by two phase diagram plots consequence against heterogeneity of authority, applicability and evidence force. Simple retrieval occupies the low-low quadrant, filters and evidence templates the mixed quadrants, and a context compiler the high-high quadrant. Ordinary retrievalPublic, stable, low consequence Pre-filters + source typingVariable corpus, bounded consequence Evidence template + reviewStable authority, consequential output Context compilerVariable authority and high consequence Heterogeneity of authority, applicability and evidence force → Consequence of wrong evidence → lowhigh lowhigh Mobile decision phase diagram for choosing a context compiler Four large quadrants map consequence and evidence-boundary heterogeneity to ordinary retrieval, pre-filters and source typing, evidence templates and review, or a context compiler. Choose the lightest mechanism that preserves the evidence boundary Evidence template+ human reviewStable authority,consequential output Context compilerVariable authorityand high consequence Ordinary retrievalPublic, stable,low consequence Pre-filters+ source typingVariable corpus,bounded consequence Evidence-boundary heterogeneity → Consequence → lowhigh Validate borderline cases with a negative control.
Figure 8. Compile when the evidence boundary varies and mistakes matter. The phase diagram is a practitioner decision instrument, not an empirical law. Borderline cases should be resolved through a negative control and failure-injection tests.

Five-question architecture record

  1. Can two authorised users asking the same words have different permitted source sets?
  2. Can the governing source change by entity, product, action, event or valid time?
  3. Can two equally relevant source classes have different authority to establish the same claim?
  4. Would post-filtering a broad candidate set create leakage, recall loss or sensitive traces?
  5. Must a reviewer reconstruct why each source entered and what dependency later invalidated it?

If three or more answers are yes, prototype a context IR and compare it with a simpler pre-filtered retrieval baseline. If all are no, retain the simpler design unless failure injection changes the evidence.

Compact glossary

Decision request
A typed envelope containing the intended task, principal chain, purpose, resource roots, decision time and output mode.
Evidence contract
The propositions a decision needs, the source classes that may support them and the treatment of gaps or contradictions.
Context IR
A backend-neutral intermediate representation of admitted resources, evidence slots, retrieval routes, budgets and dependencies.
Admitted universe
The resources that pass authorisation, applicability and evidence-class checks before relevance ranking.
Decision receipt
A replayable record of the request, policy and source versions, admitted evidence, omissions and compilation outcome.
Deterministic wrongness
A reproducible but incorrect packet caused by bad metadata, policy encoding or an incomplete evidence contract.
The evidence conversationChart · sequence
sequenceDiagram
  participant Q as Question
  participant M as Mechanism
  participant E as Evidence
  participant D as Decision
  Q->>M: The answer that a good retriever should refuse to assemble
  M->>E: Thought experiment: the identical query, a different caller
  E-->>M: compare rival explanations
  E->>D: Compile the boundary, then let the model reason
  D-->>Q: revise scope or act
Question, mechanism, evidence and decision exchange the burdens developed in The Context Compiler. Reading use: follow the relation, then test it against the surrounding evidence and limits.

Compile the boundary, then let the model reason

The context compiler changes one architecture decision: retrieval is no longer the first authority-bearing step. A typed decision request is parsed, resolved and checked before any candidate set exists. Authorisation and applicability define the universe. Evidence contracts define what can prove each proposition. Planning allocates routes and budget without changing those semantics. Attestation makes the packet replayable and revocable.

This arrangement does not make a language model truthful, a source correct or an organisation’s policies coherent. It does something narrower and operationally important. It prevents the model from deciding which evidence it was entitled to see, which rule governed and which informal statement counted as approval. It turns missing evidence into a visible state rather than pressure to complete the narrative.

The decision to adopt a compiler should be falsifiable. Build the simplest pre-filtered retrieval baseline. Add a negative control where identities, versions and evidence classes are invariant. Then inject principal changes, policy transitions, superseded documents, weak source substitutions, revoked delegations and top-k contamination. The compiler earns its place only when it preserves an evidence boundary that the simpler design cannot preserve without hidden coupling.

For Atlas, the changed outcome is modest: the system says it cannot establish a valid waiver. That answer is less fluent than a stitched narrative and more useful than a false approval. It tells the analyst what is known, what is missing, why the missing item matters and which authorised path can resolve it. That is the compiler’s proper contribution: not more context, but a defensible reason for every item that became context.

References

  1. Lewis, P. et al. Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks. NeurIPS, 2020.
  2. Liu, N. F. et al. Lost in the Middle: How Language Models Use Long Contexts. Transactions of the Association for Computational Linguistics, 2024.
  3. NIST. SP 800-162: Guide to Attribute Based Access Control Definition and Considerations.
  4. NIST. SP 800-207: Zero Trust Architecture.
  5. Cutler, G. et al. Cedar: A New Language for Expressive, Fast, Safe, and Analyzable Authorization. 2024.
  6. Pang, R. et al. Zanzibar: Google’s Consistent, Global Authorization System. USENIX ATC, 2019.
  7. Open Policy Agent. Intermediate Representation.
  8. IETF. RFC 9396: OAuth 2.0 Rich Authorization Requests.
  9. IETF. RFC 8693: OAuth 2.0 Token Exchange.
  10. W3C. PROV-O: The PROV Ontology.
  11. Arceo, F. J. and Narsing, V. P. Securing the Agent: Vendor-Neutral, Multitenant Enterprise Retrieval and Tool Use. ACM CAIS, 2026.
  12. OWASP. LLM01: Prompt Injection.
  13. Amazon Web Services. RetrievalFilter API reference.
  14. Microsoft. Document-level access control and security trimming for Azure AI Search.

Source ledger. Primary research: references 1, 2, 5, 6 and 11. Official standards and guidance: references 3, 4, 8, 9, 10 and 12. Official implementation documentation: references 7, 13 and 14. The Context IR, evidence contract composition, six-pass compiler and decision instrument are practitioner synthesis presented for testing, not established standards.