← Context From First Principles

Did It Reach the Model?

A correct compile is not a correct delivery. How to show that the bundle the compiler produced is the context the model received, and what that does and does not establish.

The experiment was planned carefully. Eight synthetic tasks, each built around a fact that could not be inferred from the task text alone. Each task run under three conditions: no supplied context, a matched distractor of the same size and shape as the real thing, and the exact minimal context the task needed. One model, decoding fixed, every run in its own session. The graders were deterministic. The question was a modest one: if correct context is delivered through the live runtime, does behaviour change?

Twenty-four runs were planned. Twenty-four ran. None was valid.

Every run reported the same thing about the context the model had received: nothing. The observer, the component whose job was to record what reached the model, wrote no records at all. So the runs in which context was supposed to be injected could not show that it had been. And the runs in which nothing was supposed to be injected could not show that nothing had been, because a silent observer also sees no marker.

The scores were identical across all three conditions, and that uniformity was itself the diagnosis. If the context had been injected, the conditions could have diverged. They did not, because nothing had been injected anywhere.

Two setup gaps compounded. The component that injects context had never been deployed to the place the runtime loads plugins from. And the observer that was installed was an older build that captured nothing under the wave’s configuration. Both were outside what the experiment’s own code could reach. Neither was detected, because the checks made before the wave were checks of identity: the right package versions, the right file digests, the right files present. Every static check passed while the live path was dead.

This is recorded as inconclusive, not as a failure of the idea. It says nothing about whether the context would have helped. It says a good deal about the instrument.

Book result — instrumentation failure. A first behavioural wave planned 24 runs, executed 24, and produced no valid behavioural observation: every run reported zero observed context records, and all 24 failed the transport check. The result is inconclusive about context and informative about the measurement chain. The evidence register lists it as Instrumentation failure.

The lesson is the reason this chapter exists. A correct compile is not a correct delivery, and a behavioural claim needs the apparatus qualified before anything else. The rest of the chapter earns the apparatus.

What has to be true

The compiler ended its work at a bundle and a trace. Chapter 24 checked that the bundle was legal, that the compilation was reproducible, and that a second implementation agreed. None of that says the bundle went anywhere. Between the compiler’s output and the model’s input lies software that renders the bundle, wraps it, puts it into a running session, and can fail at each step without telling the compiler.

There are five ways for the chain to break, and the earlier failed wave shows some of them.

The block may never be injected: the component that should inject it was not loaded, or was loaded and disabled.

It may be injected twice, or on top of a block that is already there.

It may be altered on the way: truncated, re-encoded, re-ordered.

It may arrive, and the observer may not see it, so nothing can be said either way.

It may arrive in a session, for a model, other than the one the experiment thinks it is testing.

Each of these can be detected, and each has to be detected by something other than the component that could have caused it. That principle shapes the design.

An adapter, and an observer that does not trust it

The general idea is a runtime adapter: a piece of software, written for one particular host runtime, that does two separate things. It can insert a controlled block of context into what the runtime is about to send to the model. And it can observe, independently, what the runtime is about to send.

The specific implementation used in this book is written against OpenCode, the coding agent used for the live experiments. It targets the second major version of the host’s plugin interface as it stood in September 2026, and the qualification below is a statement about that combination. Nothing in the idea depends on it, and nothing here claims that the same design has been built for any other runtime.

The two mechanisms are separate and both are off unless explicitly enabled. Installing the package changes nothing about ordinary behaviour.

Injection appends one block, read from a named file, to the system material the runtime has assembled. It is deliberately timid. It builds the new system array first and assigns it once, so an error can never leave it half written. A block that is identical to one already present is a harmless no-op. A block that differs from one already present is a loud failure, not a second copy stacked on top. A block that does not carry the expected markers is refused without touching anything. The injection keeps a small trace recording what happened, one of a handful of named outcomes, how many system blocks there were before and after, and the session it happened in. It never records the text.

Observation copies the model context to a local file, read-only. It never modifies the event it is looking at, and that is pinned by tests. Each record carries the session, the agent, the requested and observed provider and model, the system material, the messages, the tool definitions, a sequence number that survives restarts, and timings. Its output can contain sensitive material from the session, so it stays local and is never committed.

The observer must see what the injector did, or it proves nothing. So the injection is registered to run first and the observer second, in code and not by the order of files on disk, and a combined test pins both the order and its consequence: with injection enabled, the observer’s record must contain the marker exactly once; with it disabled, the record must equal the untouched context.

One warning in the package’s own documentation is worth carrying over. An empty spool proves nothing about model activity. It most likely means the observer was never switched on for the process that ran the model. The failed wave was exactly that.

Three sets of bytes

When the compiler’s bundle travels, it is not one object. It is three, and they are different.

What it is Made by Identity
A. the render the bundle as text: a header, the item contents, a closing line the compiler’s renderer its own hash
B. the transport block A wrapped in an envelope that carries metadata: which compiler, which request and policy, the bundle’s identity and hash, its size, and a marker unique to this run the adapter its own hash
C. the observed system block what the observer read back from the model context after injection the observer compared to B

Three different digests follow from that, and they should not be confused: the bundle’s own hash, which covers item identifiers and content in order; the hash of the render; and the hash of the block. Each answers a different question.

A is not equal to B, by design. The envelope adds metadata on purpose. What can be claimed is more precise than “byte-identical end to end”:

   A is embedded unchanged inside B
   B was injected exactly once
   C contains B exactly once

The layout makes the first of those checkable. The envelope wraps the render between two fixed lines and leaves the render itself untouched:

[CONTEXT RUNTIME]
compiler: …
request_id: …
policy_version: …
bundle_id: …
bundle_hash: …
bundle_tokens: …
marker: PROJECT_CONTEXT_COMPILE_CANARY_<unique per run>
--- compiled context begins ---
<the exact text the compiler rendered>
--- compiled context ends ---
[/CONTEXT RUNTIME]

Recovering A from B is then a matter of reading between the two fixed lines, and comparing it with the render the compiler produced.

Reconciliation

The check itself is a pure function. It reads no files and calls no model. It takes the compiler’s record of what it produced, the rendered text, the transport block, the adapter’s injection trace, the observer’s record and the model that was asked for, and it returns a receipt. Any mismatch appends a failure and forces the status to fail. It has no way to pass except by finding nothing wrong.

The heart of it is the observer’s side:

const blockHits = input.observer.systemTexts.filter(
  (text) => text === input.runtimeBlock,
);
runtimeBlockPresent = blockHits.length === 1;
// ...
const recovered = extractRendered(blockHits[0] as string);
compilerRenderPresent = recovered === input.renderedBundle;

The block must appear in the observed context exactly once, and the render recovered from it must equal the render the compiler emitted. Around that sit the other checks: the compilation succeeded; the injection reports that it injected, and the number of system blocks grew by exactly one; the envelope’s bundle identity, hash, request, policy and marker match the compiler’s; the marker occurs exactly once across everything the observer saw; and the model that was asked for is the model that was observed.

If the compilation failed, nothing is transported at all. The composition step returns no block when the compiler refuses, so the caller stops before any injection is attempted, and the reconciliation would fail any run that showed transport after a refusal. Not attempting is different from failing.

The live qualification

The chain was run end to end on the smallest case that could exercise every step. A one-item synthetic bundle was compiled, rendered, wrapped with a fresh marker, injected into one live session through the adapter, and read back by the observer. One trivial request was made, and the model’s reply was ignored: reply text is diagnostic only, and the proof is the trace, the capture and the reconciliation.

Book result — live compile-to-observe qualification. On one trivial synthetic request the chain passed. The compilation succeeded. The runtime reported that it injected, and the number of system blocks went from four to five. The observer recorded the request in the same session, as its first record, with the marker exactly once. The requested model equalled the observed model. The compiler’s render was present unchanged in the injected block, the block was present unchanged in the observed context, and the bundle’s identity and hash matched throughout. The evidence register lists it as Live compile-to-observe qualification.

That is what the experiment established, and the perimeter around it is tight.

It is one bundle of one item, on one model, on one version of the host. The bundle’s token count is the compiler’s own word-based estimate plus the candidate’s declared count, not a figure a provider reported. The observer’s record contains the full model context, including system prompts, so it stays private; only its digest is registered.

And the observation happens at a particular place. The observer sees the assembled context at the host’s model-context hook. After that, the runtime translates what it has into whatever a provider expects. Anything that happens after that point is invisible to this instrument:

OpenCode model-context observation  ≠  provider-wire capture

So this establishes that the compiler’s render reached the runtime’s model-context boundary unchanged and exactly once. It does not establish that the bytes sent to the provider were identical, that the provider added or rewrote nothing, what cache decisions were made, or what the model made of what it was given. Claims about any of those need an instrument at a different place.

The layers of “it works”

The failed wave was a story about one word. Everything “worked”. The plugin was present, the version was right, the files matched. The word hides a stack of separate claims, and a pass at one layer is not a pass at the next.

Layer What passing shows What it does not show
installed the package resolves and is at the expected identity that it loads
loaded the host started it and setup ran that hooks fire
hooks registered the intended hooks are attached, in the intended order that a request passed through them
injected on one live request the block was added once that anyone saw it arrive
observed an independent record of the context exists that it contains the block
reconciled the block is in the record exactly once, unchanged, in the right session provider-wire identity, cache behaviour, model internals
attributed the requested model is the observed model that behaviour changed
behavioural the model’s action moved (Chapter 26)

The package ships two checks that sit at different rows. A load check confirms the first three without spending any inference. A smoke test makes one live request and confirms the next four. Neither is a substitute for the other, and neither says anything about the last row.

The failed wave stopped at the first row. Its preflight verified the repository’s package identities and never asked whether the deployed instrument was alive. That is the difference between static identity and liveness: a fact that would be true of a working system is not evidence that the system is working.

The repair is to make liveness a precondition. The experiment’s harness now refuses to spend any model inference until two live canaries have passed for the model it is about to use: one showing that an injected marker can be observed in the same session exactly once, and one showing that a compiled bundle survives the whole chain. A second wave has been designed to run only behind those gates. It has not been run.

Earlier probes of the same idea used a different, since-replaced arrangement of separate injection and observation plugins, with a fixed marker in place of a compiled bundle. Two are kept. The first attempt failed: the marker never reached the observed context. The second passed after the injection was repaired. Both are registered, as Earlier runtime qualifications, and both are marker probes: they show that injection and independent observation work, and they involve no compiler.

What has been earned

Follow the artifact chain from the start of Part V. The compiler decided membership, forms and order. The renderer turned that into exact text. The adapter wrapped the text and put it into a running session. An observer, which the adapter does not control, read the session’s context back, and a pure function compared the two. On one trivial case, at the model-context boundary, every comparison held.

This is what the book calls transport evidence. It is a third kind, after structural evidence in Chapter 24. Structural evidence says the artefact is right. Transport evidence says the artefact arrived. Neither says anything about what the model did with it.

Two things follow that are easy to lose. The first is that transport evidence is a precondition for behavioural evidence, and not a part of it. A behavioural run without a live, qualified transport path is uninterpretable, as the 24 runs showed. The second is that the check has to be repeated in the run and not assumed from an earlier pass. A canary that passed last week says the path worked last week.

The delivery of the bundle is now something the project can demonstrate. Whether the delivered bundle changed the model’s behaviour, and whether the change was for the better, is a separate question, and the only evidence the book has about it so far is small.

We know the bundle arrived. We still do not know whether it helped.

References

  • Project Context OpenCode. Source repository, Apache-2.0 licence, first-party code by this book’s author. The injection and observation mechanisms, the compiler composition layer (envelope, reconciliation), and the load-check and smoke scripts described in this chapter. https://github.com/ernanhughes/project-context-opencode
  • Project Context evidence register (companion repository, evidence/README.md). Lists the identifiers, code revision and published artefacts behind each measured result. Consumed here: Instrumentation failure (24 planned, 24 executed, none valid; every run without observed records), Live compile-to-observe qualification, and Earlier runtime qualifications. Transport evidence and one inconclusive behavioural wave; no behavioural claim.