CWA Specification (draft) — Context Window Architecture
Context Window Architecture
Specification · draft · 2026-10-07

Context Window Architecture

The rules an assembler, its producers and your application follow, numbered from R-1. A number is never changed or reused. Where another page disagrees with this one, this page is right.

Status: draft. Not yet stable. contextwindowarchitecture/contextwindowarchitecture. This page renders commit 16de4be.
Requirement index · {{ ruleCount }} requirements
1

Conformance

The key words MUST, MUST NOT, SHOULD, SHOULD NOT and MAY are used as in RFC 2119. MUST is required for conformance; SHOULD is expected unless a reason is recorded; MAY is optional.

A conformant assembler meets every MUST an assembler can meet by itself, provides every tokenizer and renderer that Tokenizers and renderers in conformance/README.md requires, and can emit its trace as JSON with the fields R-21 lists. A conformant producer emits only items that meet section 2's rules for an item (R-1, R-2), which admission checks, plus the MUSTs aimed at producers: every producer reports each entry it suppresses (R-9) and computes its scores and variants before assembly (R-18), and, by kind, R-9 and R-14 bind memory, R-10 anything carrying user-controlled or fetched content, R-13 retrievers, R-15 MCP adapters, and R-18's variant rules anything that supplies variants. A conformant application uses a conformant assembler and only conformant producers, and meets the MUSTs no component can meet for it: it enforces invariants, tool permissions among them, outside the model (R-5, R-15), writes state (R-8), declares conflict groups (R-11), authenticates each producer and binds its identity outside the items (R-15), makes every external read, retrieval and model call before it freezes the snapshot, and freezes a new one for any retry (R-12, R-23), sets parser: true on every route whose response a downstream parser consumes (R-4), sets budget.input to the model's context limit less budget.reserved_output (R-16), versions its profiles (R-20) and deploys only evaluated ones (R-19). It renders with a renderer the published cases cover. It declares a tokenizer that reads nothing but the text it counts (R-23) and counts the model's tokens, or estimates them with a budget.margin_percent that covers the error for the route's text (R-16); the tokenizer may be its own, under an ID no published tokenizer uses. It hands the payload to the model with each text in the platform role its renderer assigns, text assigned none in a single user message, where the query alone is the live turn, and no text of its own. Request structure the renderer leaves to it, such as a provider's cache breakpoints, cache lifetime or cache key, adds no text, so the count and hash its trace records still describe what the model reads. The roles R-7 has the assembler assign, and the count R-16 has it make, hold for the payload it renders, as its report shows; only this handoff makes them hold for what the model receives. The Assembler page marks each requirement as assembler, boundary or application, so you can see who owns which part.

These are the only claims this specification defines. An application is not conformant because one of its parts is, though it may say which of its parts are. The claims also differ in kind. Producer and application conformance is an agreement about what each emits and does. Assembly cannot confirm all of it, so the claim rests on their word, and an assembler's boundary checks catch only some breaches. An assembler's conformance concerns what it decides. A wrong decision fails silently, because a model accepts any payload, so the claim rests on evidence: a conformance report showing that the assembler passes every published case and rejects every published rejection, skipping only those that use an optional tokenizer or renderer it does not provide, as Reporting results in conformance/README.md describes. An application's claim includes its assembler's, which is why it must render with a renderer the published cases cover: that assembler's report then backs how the payload is built. Its tokenizer need not be one the cases cover: they test how an assembler applies a tokenizer's counts, and no case can test whether those counts match a model only the application calls. That, and the handoff to the model, rest on the application's word like the rest of its claim.

Requirements are numbered R-n in a single flat sequence. A number is assigned once and never reused. A withdrawn requirement keeps its number and is marked withdrawn in the index. Other pages on this site, and the reference implementation, cite requirements by number.

The published JSON Schemas define item and trace field shapes. Profiles place slots and pick their wrappers, a published renderer writes the request from them, and wire encodings must preserve these semantics. The website tools check structure and selected local invariants, not full implementation conformance.

2

Model: planes, slots, items

A model call is assembled from items. Every item belongs to exactly one slot; slots are grouped into four planes. The planes answer four different questions and are never merged. An item in one of the two evidence slots is an evidence item, and the piece of a document a retriever pulls is a chunk.

{{ p.name }}
{{ p.answers }}
{{ s }}
2.1 Slots
{{ s.id }}
{{ s.holds }}
{{ s.rule }}
2.2 Item fields

Eight fields are the minimum an assembler needs to place, rank, expire and trace an item. Six more carry the item's policy; when a producer omits them the assembler fills them from the slot's defaults. The JSON Schema is published with the scaffold.

Minimum · MUST
{{ f }}
Policy · SHOULD
{{ f }}
{{ f.key }} {{ f.text }}
{{ r.id }}
{{ r.summary }}
{{ s.text }}
3

Governance

Rule zero
Language guidance goes in context, and enforceable invariants go outside the model. "Never quote an unverified price" is stated in governance and enforced by a tool guard: the model is instructed, but the application still enforces the rule.
3.1 Instruction authority and data roles
{{ a.n }} {{ a.name }}{{ a.value }} {{ a.text }}

These seven values are the complete set, and the monospaced one under each name is what goes in the authority field. Only governing and user items can direct behavior: governing instructions, the live query, and the user's own earlier turns in history. Prior model turns carry untrusted, so they can't. Authority alone decides this: an item's trust and injection_risk never add or remove it (R-6). The other five values say where data came from. They aren't a ranking of which facts are true. An item with any other authority value is excluded at admission.

Slots never override platform message roles or application controls. A retrieved document is not an instruction because it contains imperatives. A preference does not lift a safety constraint. Tool output is evidence, not permission to act.

3.2 State is not memory
state.*

State is canonical, structured, typed, current, and owned by the application. It holds workflow stage, approval status, selected entity IDs, pending action, and retry count. The database is the source of truth, never the model.

interaction.memory

Memory is fallible, compressible, linked to its source, and revocable. It holds preferences, prior-discussion summaries, and inferred long-term facts. Every entry carries an expires deadline and the turn it came from.

3.3 Conflict resolution · instruction rules and factual route policy
{{ c.when }} → {{ c.then }}

Instruction conflicts: platform roles and application controls first, governing instructions over user requests, then the peers' own conflict policies. The peers are the members at the highest authority in the group that can instruct: governing, or user if no governing item is there. A group left with fewer than two admitted members is moot. Otherwise, a group with one peer or none is decided by authority, and nothing is excluded. If exactly one of two or more peers says governs and the rest say defers, the deferring ones are excluded. Items with any other authority, which covers every state, evidence and memory item and anything carrying untrusted, never become instructions, whatever their wording; they stay as material. Anything else escalates to the route's on_unresolved_instruction action.

Factual conflicts: route policy for the group's fact key lists the eligible producers, by authenticated identity, in precedence order, and the scope keys a claim must carry. It may prefer a verified current observation over a stale document by listing that producer first, but no data role automatically wins, and an item's trust, its injection marker and the route's verified flag are never read (R-11). Freshness breaks a tie only between leaders from the top-ranked producer, and only when the route permits it, so it never lets an ineligible claim win; otherwise the route surfaces the conflict, requests more context or refuses. No conflict excludes a protected item. The application supplies typed conflict groups; the assembler does not discover contradictions by interpreting prose.

{{ r.id }}
{{ r.summary }}
{{ s.text }}
4

Admission & fitting

Evidence goes through eight steps between the source and the payload. The first four are the producer's, and they're done before assembly starts. The last four are the assembler's, and everything it leaves out shows up in the trace with a reason code.

{{ s.n }} {{ s.name }} {{ s.text }}
4.1 Budget tiers

Every slot sits in one of three tiers. Protected items are rendered whole or the assembly is refused. Droppable items may be omitted. Compressible items may be replaced by a precomputed shorter variant or omitted. Under budget pressure that happens only once droppable items are gone; the route's versioned fitting policy orders that step, and without one, variants come before omission. The one exception is a slot the route holds at a min_tokens floor: it can keep its droppable items while other slots compress. An item over its own token_budget, or in a slot over the route's max_tokens, is reduced first, whether or not the payload fits. Every omission is traced as over_budget. The assembler never touches protected items. Only the route's versioned policy can raise a slot's tier; an item that claims a higher tier than its slot allows is excluded with tier_upgrade_not_allowed, so a producer cannot protect its own items.

{{ r.id }}
{{ r.summary }}
{{ s.text }}
5

Placement profiles

Placement is model-, task- and content-dependent. This specification takes no position on where items go. It fixes the semantic slots and requires that placement be a profile: versioned, per route, and evaluated against that route's tasks before it's promoted for deployment (R-19). The evidence for this position is collected on the Evidence page. Placement also decides how much of one call's payload the next call repeats byte for byte, which is what provider prompt caches reuse; the Evidence page's section on prompt caches sets out what the profile, the renderer and the application each own.

profile.yamlan unevaluated draft · JSON syntax is valid YAML
{
  "spec": "cwa/draft",
  "id": "policy-first-chat",
  "version": 1,
  "route": "support-chat",
  "model_family": null,
  "route_policy_version": "illustrative/v1",
  "placement": [
    {
      "slot": "governance.instructions",
      "wrap": "system"
    },
    {
      "slot": "state.user",
      "wrap": "xml:user_profile"
    },
    {
      "slot": "evidence.knowledge",
      "wrap": "xml:evidence"
    },
    {
      "slot": "state.task",
      "wrap": "xml:task"
    },
    {
      "slot": "interaction.memory",
      "wrap": "xml:memory"
    },
    {
      "slot": "interaction.history",
      "wrap": "xml:history"
    },
    {
      "slot": "governance.capabilities",
      "wrap": "tools"
    },
    {
      "slot": "evidence.tool_results",
      "wrap": "xml:observation"
    },
    {
      "slot": "governance.examples",
      "wrap": "xml:examples"
    },
    {
      "slot": "governance.output_contract",
      "wrap": "xml:format"
    },
    {
      "slot": "interaction.query",
      "wrap": "xml:query"
    }
  ],
  "evaluation": {
    "status": "unevaluated",
    "suite": null,
    "date": null,
    "result": null,
    "artifact": null
  }
}
{{ r.id }}
{{ r.summary }}
{{ s.text }}
6

Trace & conformance tests

Every assembly emits a trace. "Debuggable" means an engineer can reconstruct and compare assembly decisions, not just read the payload. Outcome metrics ride on top: groundedness, task success, tool-call correctness, recovery behaviour, latency, cost.

Trace fields
MUST · trace_id · profile { id, version } · budget { input, reserved_output, margin_percent when the snapshot sets it } · context { spec, assembly_time, route_policy_version, tokenizer, renderer, snapshot_digest } · result { input_tokens, hash } or null · included[] { slot, item_id, tokens, source_version, eligibility } · compressed[] { slot, item_id, from, to, method, variant_id } · excluded[] { item_id, reason, stage }, plus slot when the candidate names one, and duplicate_of or superseded_by when the reason is duplicate_content or superseded · conflicts[] { group_id, kind, items, resolution, decided_by }, one per declared group, plus winner when one member prevails · refused { bool, reason } · recovery.action on an evidence_required refusal (R-12), or on a conflict_unresolved one when every unresolved group asked for context · defaults_filled[] { item_id, field }, empty when none were filled · reasons from contract/reasons.json
SHOULD · admission-stage timings
6.1 Tests the trace makes possible
{{ t.name }} {{ t.text }} {{ t.rule }}
{{ r.id }}
{{ r.summary }}
{{ s.text }}