Context Engineering and Prompt Management

Context Engineering and Prompt Management

Context Isolation — Patterns

Context isolation › Patterns

31 of 57 in this book · 3 min read

Boundary key threaded through every producer

Problem. Isolation is enforced at the point someone remembered to enforce it. The retrieval query filters correctly, the newly added cache does not, and the leak is in the component that was added last and reviewed least.

Approach. Make the boundary identity a required argument of the request context, carried into every producer and every cache as part of its key rather than as an optional filter. Construct store clients already bound to the boundary so an unbound query is not expressible, and assert on the way out that every admitted span carries the expected key, failing the request rather than the assertion.

Trade-off. Threading an identity through every layer is invasive and shows up in the signature of every helper. The outbound assertion also costs a pass over the assembled context on every request, and it only catches spans that carry the key — anything derived, summarised, or produced by a component that forgot to propagate it passes the check silently.

Quarantined sub-call with a typed return

Problem. Material that cannot be trusted has to be read by a model anyway. Reading it in the main context exposes the instructions, the tools, and the rest of the conversation to whatever it says.

Approach. Invoke a separate call whose context contains only the untrusted material and a narrow task statement, with no tools, no credentials, and no conversation history. Require its result to conform to a contract chosen so that the space of expressible outputs is small — an enumerated label, a number, a list of extracted fields — and admit only the parsed value into the parent, never the raw text.

Trade-off. It costs an extra model call and a round-trip, and the narrow contract is the whole control: the moment the return type includes a free-text field, the boundary is decorative. It also genuinely limits what the sub-call can tell you, so tasks needing open-ended analysis of untrusted material cannot use it without widening the return and losing the guarantee.

Explicit task reset with a declared carry-over set

Problem. Users move between unrelated pieces of work in one conversation. Context from the first silently conditions answers in the second, and nothing in the system knows a transition happened.

Approach. Make task boundaries explicit events — user-initiated, or detected and confirmed — that start a fresh context. Define exactly what carries over as an enumerated set: durable user preferences, the active entity if the user asked for it, nothing else. Everything outside that set is dropped, including derived summaries, which are otherwise the artefact that survives every reset.

Trade-off. Users dislike repeating themselves, and an aggressive reset policy reads as amnesia. Detecting a topic change automatically is unreliable in both directions, so the mechanism either asks often enough to be irritating or resets at the wrong moments.

Isolation tests as adversarial fixtures

Problem. Separation is assumed because the code looks right. Nobody has ever observed the system failing to leak, because nobody has tried to make it leak.

Approach. Keep a standing test that drives two boundaries concurrently with distinctive, searchable content, then asserts on the assembled context, the cache contents, the retrieval results, and the stored traces that neither contains the other's markers. Run it against caches that are warm, since the cold path is the one that is usually correct, and include the paths added since the last audit.

Trade-off. It proves the absence of verbatim carryover on the paths tested and nothing more; a leak that passes through a summarisation step or an embedding will not carry the marker. The test also needs production-like concurrency and warm caches to be meaningful, which makes it slow and awkward to run on every change.