# RFC 0111: Context Economy — Transcript Token Budget & Declared Summarization

> Source: https://openwop.dev/rfcs/0111-context-economy.html · OpenWOP, the open-source protocol for multi-agent workflow orchestration. CC BY 4.0.

| Field             | Value                                                                                          |
| ----------------- | ---------------------------------------------------------------------------------------------- |
| **RFC**           | 0111                                                                                           |
| **Title**         | Context Economy — Transcript Token Budget & Declared Summarization                             |
| **Status**        | `Accepted`                                                                                     |
| **Author(s)**     | David Tufts (@davidscotttufts)                                                                  |
| **Created**       | 2026-06-26                                                                                     |
| **Updated**       | **2026-09-26 — `Active` (Parked) → `Accepted`. Evidence tier: tier-2 — MyndHyve `workflow-runtime`, the steward-affiliated sibling host; single witness.** The tripwire fired: a host running **real orchestrator-loop model turns** advertises `contextBudget` and produced a non-vacuous witness. Deployed revision `workflow-runtime-00788-peh`, build `commit:9e5216d4d2ce93e34aaec195a47645b370943119` (its live `/readiness` answers that commit); live discovery advertises `multiAgent.executionModel.contextBudget` `{transcriptTokenBudget: 520, tokenCounter: "chars", summarization: {supported: true, keepLastTurns: 2}}` and lists `conformance-context-budget-live` — re-fetched at the moment of this flip. Evidence: [`evidence/rfc-witnesses/0111-myndhyve-major1-2.42.1.json`](../evidence/rfc-witnesses/0111-myndhyve-major1-2.42.1.json) (file sha256 `3a1eef59054c…`, `witnessSha256 fd41a368bef6…`), signed `myndhyve-bundle-2026-09b` (= the live discovery `signingKeys[0]`), steward-verified with the PUBLISHED suite 2.42.1 (`--verify --host-key` → VERIFIED). `context-budget-transcript-bound` `executed-pass` (27 assertions) and `context-summarization-replay` `executed-pass` (15 assertions), at scenario and `it` level, **with no `partial-witness` detail** — so the #1606 pressure check saw a real eviction or summary, the `chars` recount of `entries[].rendered` equalled `tokenCount`, and a replay fork fed the model the recorded summary byte-for-byte. **The bundle is NOT certified**, and this RFC does not claim a certification: 1415 pass / 168 fail / 190 blocked / 303 inapplicable / 27 skipped, the failing and blocked rows overwhelmingly v1 host-sample seams this host does not mount at major 1. Replay-adjacent rows among them, named so none is hidden: `replay-fanout-suppression`, `aiEnvelope.correlationReplay`, the compensation-behavior/recovery replay legs, `dispatch-per-item-input` replay, `replay-divergence-at-refusal`, `replay-llm-cache-key`, `replay-retention-expiry` — none is one of this RFC's scenarios. **Steward decision, recorded as such:** this RFC predates the RFC 0167 certification program (`check-accepted-predicate.mjs` does not bind it), and its bar is GOVERNANCE §"Acceptance evidence tiers": a witnessed, non-vacuous conformance pass on a deployed host, which a signed per-requirement bundle is; a certified bundle is not required. The witness is at **major 1** because at major 2 both scenarios gate on the seams-profile claim (`conformance.seamsProfile`), which MyndHyve production does not make — at major 2 they record `inapplicable`, which witnesses nothing. Getting here found and fixed six real host defects (myndhyve #498 AI-call dedup key, #499 thinking-token cap, #500 loop turns replaying turn 1's cached output + model 429 retry, #502 server-side Gemini via Vertex, #504 replay fork re-emits recorded supervisor/dispatch outcomes, #505 replay fork carries the replayed turns' transcript windows) and two suite defects (#1606 the witness could not fail; #1625 a hard-coded 30 s test timeout no live run could meet). Not provisional: no comment-window waiver is recorded for this RFC (filed at `Active` 2026-06-26 under the bootstrap rules of the time), so it has no RFC 0156 §B register row. · 2026-09-26 — **amended in place (additive): the fork refusal names its code.** The summary-unavailable refusal said only "mirroring `replay_memory_snapshot_unavailable`" and named no code, so the first implementing host had to emit a vendor code for a condition this RFC owns. It is now `replay_context_summary_unavailable` (`409`, not retriable), registered in `spec/v2/errors.json` since 2.42 and listed in v1 `rest-endpoints.md` §"Common error codes". It binds only hosts advertising `contextBudget.summarization` (none in production at amendment time), so no existing pass changes. No conformance leg asserts the code: a black-box suite cannot make a host's summary artifact unavailable at `fromSeq`, so the code is advertise-and-attest, like the off-seam ceiling of the transcript seam. · **Parked 2026-08-13 — tripwire:** a host running **real orchestrator-loop model turns** advertises `contextBudget` and produces a non-vacuous witness. No steward host does; the mock supervisor is forbidden from advertising it by this RFC's own §Scope, and openwop-app confirmed on 2026-08-13 that it runs no real orchestrator turns. **All five acceptance boxes are ticked and the RFC still does not graduate** — criterion 5's tick covers *deferring* reference-host implementation for `Active`, and the same sentence names this tripwire as the `Accepted` gate. Counting the boxes says ready; reading them says not yet. 2026-06-26                                                                                     |
| **Affects**       | `spec/v1/multi-agent-execution.md`, `spec/v1/host-sample-test-seams.md`, `schemas/capabilities.schema.json`, `schemas/run-event-payloads.schema.json`, conformance |
| **Compatibility** | `additive` per `COMPATIBILITY.md`                                                               |
| **Supersedes**    | —                                                                                              |
| **Superseded by** | —                                                                                              |

## Summary

A v1-conformant host MAY feed an unbounded transcript to each orchestrator turn: `multi-agent-execution.md` §"Stateful agent-loop lifecycle" defines `transcriptWindow` as an *event-count* bound and states "Absent ⇒ unbounded on the wire." This RFC adds an opt-in `contextBudget` object to the multi-agent execution-model capability that lets a host advertise (a) a per-turn **token** budget for the orchestrator transcript and (b) a **declared summarization** contract for turns evicted beyond that budget — making the per-iteration context contract observable and conformance-testable. It does **not** change any existing default: a host that advertises neither field behaves exactly as today.

## Motivation

The protocol is byte-lean on the wire (content-free events, reference-by-id payloads, `updates` delta streams as the default) but says nothing normative about the prompt a host assembles and sends to the model each iteration. A third-party token-efficiency red-team (2026-06-15) identified this as the dominant cost: a conformant host re-sending the full transcript each turn grows per-turn cost O(turns) and cumulative run cost O(turns²). Two facts make this a spec concern rather than a pure implementation choice:

1. `transcriptWindow` already exists (RFC 0061, `version >= 5`) but bounds by **event count**, which does not bound **tokens** — a single large tool result blows the budget regardless of count.
2. The spec is *silent* on what a host does with evicted turns. Per `COMPATIBILITY.md` §4, a new normative requirement on previously-undefined behavior is additive — so the spec is the right place to define a budgeted, summarization-aware context contract that hosts can advertise and clients can rely on.

This RFC makes the contract declarable without forcing it on anyone: the "kill the unbounded default" framing from the red-team is deliberately **not** adopted, because flipping `absent ⇒ unbounded` to `absent ⇒ bounded` is a behavior change requiring the safety-fix process. Opt-in advertisement is the additive-safe path.

## Proposal

### Capability surface

Add an optional `contextBudget` object under `multiAgent.executionModel` (the same object that carries `transcriptWindow`). It is `version >= 5` (RFC 0061 stateful loop) gated.

```diff
   "executionModel": {
     "type": "object",
     "properties": {
       "transcriptWindow": { "type": "integer", "minimum": 1 },
+      "contextBudget": {
+        "type": "object",
+        "additionalProperties": false,
+        "properties": {
+          "transcriptTokenBudget": {
+            "type": "integer", "minimum": 1,
+            "description": "RFC 0111. Max tokens of transcript the host feeds any single orchestrator turn. Advertise-and-honor; complements (does not replace) transcriptWindow. Absent ⇒ no token bound."
+          },
+          "tokenCounter": {
+            "type": "string",
+            "enum": ["o200k_base", "cl100k_base", "chars", "host-defined"],
+            "description": "RFC 0111. The unit transcriptTokenBudget is denominated in, so the bound is interpretable across hosts. REQUIRED when transcriptTokenBudget is present. Same enum as RFC 0113 memory.injectionBudget.tokenCounter — transcript + memory budgets denominate consistently."
+          },
+          "summarization": {
+            "type": "object",
+            "additionalProperties": false,
+            "properties": {
+              "supported": { "type": "boolean" },
+              "strategy": { "type": "string", "enum": ["sliding-window", "recursive", "map-reduce"] },
+              "keepLastTurns": { "type": "integer", "minimum": 0,
+                "description": "Turns kept verbatim at the head of the window; older turns MAY be replaced by a summary." }
+            },
+            "required": ["supported"]
+          }
+        }
+      }
     }
   }
```

### Normative requirements

- **Scope.** `contextBudget` governs the **RFC 0061 per-iteration orchestrator-loop transcript** — `multi-agent-execution.md` §"Per-iteration state inputs" input 3, the SAME transcript `transcriptWindow` bounds — **NOT** a general chat-conversation history (RFC 0005) or any other prompt. A host whose multi-agent orchestrator loop does not run real model turns (e.g. a mock supervisor with no live inference) **MUST NOT** advertise `contextBudget`, exactly as it MUST NOT dishonestly advertise `transcriptWindow`. Budget-only advertisement (`transcriptTokenBudget` + `tokenCounter` **without** `summarization.supported`) is valid for a host that HAS a real orchestrator loop but does not summarize.
- A host advertising `contextBudget.transcriptTokenBudget` **MUST NOT** feed more than that many tokens of transcript to any single orchestrator turn, measured in the unit named by `contextBudget.tokenCounter`.
- A host advertising `transcriptTokenBudget` **MUST** also advertise `tokenCounter`; the budget is otherwise uninterpretable.
- `transcriptTokenBudget` and `transcriptWindow` **MAY** both be advertised; when both are present the host **MUST** honor whichever bound is tighter for a given turn.
- A host advertising `contextBudget.summarization.supported: true` **MUST** keep the most recent `keepLastTurns` turns verbatim and **MAY** replace older in-window turns with a host-produced summary. The active (most recent) turn **MUST NOT** be summarized.
- **Replay determinism (normative).** Per `multi-agent-execution.md` §"Per-iteration state inputs", the transcript is a deterministic, replay-reproducible iteration input — today a pure projection of the event-log tail. A host-produced summary is **nondeterministic host output** that breaks that purity, so it is governed exactly like a nondeterministic LLM envelope under RFC 0041 (`version >= 5` implies the `version >= 4` replay-determinism contract). Therefore:
  - Each substitution **MUST** be recorded as a `context.summarized` event (payload below) whose `summaryRef` points at the persisted summary.
  - On a `POST /v1/runs/{runId}:fork mode: replay`, when a summarization point falls at sequence ≥ `fromSeq`, the host **MUST** reuse the recorded summary (resolved via `summaryRef`) and **MUST NOT** re-summarize. A host that re-summarizes and produces a different model-facing transcript is non-conformant — the direct analogue of RFC 0041 §"Envelope-refusal recovery in replay".
  - `summaryRef` is an **artifactId** (a run-produced artifact read via `GET /v1/runs/{runId}/artifacts/{artifactId}`), keeping the event content-free. By direct analogy to the MAE-3 memory carry-forward (`multi-agent-execution.md` §"Replay carry-forward"), the summary artifact **MUST** be persisted and readable as-of the iteration's event-log index; a host that cannot serve it at the requested `fromSeq` **MUST** refuse the fork with `replay_context_summary_unavailable` (mirroring `replay_memory_snapshot_unavailable`; code named by the 2026-09-26 amendment — see Updated).
  - Summarization that is not recorded this way breaks replay and **MUST NOT** be advertised under this capability.

### Event-payload addition

`schemas/run-event-payloads.schema.json` gains `context.summarized`:

```json
{
  "type": "context.summarized",
  "payload": {
    "iteration": 12,
    "replacedTurns": ["evt_0a1", "evt_0a2"],
    "summaryRef": "art_sum_7c",
    "tokenCounter": "o200k_base",
    "tokensBefore": 4120,
    "tokensAfter": 280
  }
}
```

`summaryRef` is a reference-by-id to the summary artifact (the summary text is **not** inlined on the wire — content-free event discipline). `replacedTurns` lists the event ids the summary stands in for, so a replay engine can reconstruct the exact transcript.

### Conformance seam

The assembled transcript is host-internal and never crosses the wire, so conformance cannot observe it from discovery alone. This RFC adds one seam to `host-sample-test-seams.md`:

```
GET /v1/host/sample/agent/transcript-window?runId=…&iteration=N
→ { tokenCounter, tokenCount, eventIds: [...], summarizedRanges: [...] }
```

The seam returns the host's own accounting of what it fed the model that iteration. Conformance: drive enough turns that an *unbounded* transcript would exceed the budget, then assert (a) `tokenCount ≤ transcriptTokenBudget`; (b) the harness independently re-computes the token sum and confirms the host's reported `tokenCount` is internally consistent and ≤ budget — ***corrected 2026-09-26*** (`COMPATIBILITY.md`, an assertion no implementation could satisfy: events are content-free, so the event log gives the harness nothing to count). The seam now also returns `entries[] { eventId, rendered }`, the exact text fed per item; for `tokenCounter: "chars"` it is REQUIRED and the harness sums the rendered code points, which MUST equal `tokenCount`; every entry is a real event of the run and the verbatim entries are `eventIds` in order. For `o200k_base`, `cl100k_base` and `host-defined` the recount is advertise-and-attest (the suite ships no tokenizer). No host `MUST` moved; (c) `eventIds` are the recent tail (no older event included while a newer eligible one is dropped — *eligible* meaning sharing `(type, nodeId)` with an event fed that turn, defined 2026-09-26 because the RFC had left it undefined); and (d) every `summarizedRanges` entry has a matching `context.summarized` event. A full witness also needs **pressure** — at least one turn where the budget evicted or summarized an older eligible event; a run whose transcript never reaches the advertised budget records `partial-witness`, not a pass (suite 2.41.0).

**Honest non-vacuity limit (documented, not a gap to close):** the model-facing prompt is *genuinely* host-internal, so conformance verifies the host's **declared** transcript accounting is internally consistent and within budget — it **cannot** black-box-prove the host doesn't additionally feed off-seam content to the model. This is the inherent ceiling of governing host-internal assembly via a test seam; the capability is advertise-and-attest, and the seam proves the accounting is consistent + bounded, not that the prompt is nothing more. A host that lies in the seam fails the cross-check in (b)/(c); a host that lies *past* the seam is outside what any wire/seam witness can see.

### Examples

**Positive** — host advertises a 4 000-token budget with sliding-window summarization keeping the last 3 turns verbatim:

```json
{ "multiAgent": { "executionModel": { "version": 5,
  "contextBudget": { "transcriptTokenBudget": 4000, "tokenCounter": "o200k_base",
    "summarization": { "supported": true, "strategy": "sliding-window", "keepLastTurns": 3 } } } } }
```

**Negative** — `transcriptTokenBudget` without `tokenCounter` (uninterpretable budget) → fails schema (`tokenCounter` required when budget present, enforced by an `if/then` clause) and fails conformance.

## Compatibility

**Additive.** Backward-compat clauses:

- `contextBudget` is a new optional object; a host that omits it behaves exactly as today (`transcriptWindow` semantics, including `absent ⇒ unbounded`, are untouched).
- `context.summarized` is a new event type per `COMPATIBILITY.md` §2.1; consumers that do not recognize it ignore it. Server-emitted event schemas are open (RFC 0094), so adding it does not break schema-validating clients.
- `replay_context_summary_unavailable` (amended 2026-09-26) is a new registered error code: it names the refusal this RFC already required, binds only hosts advertising `contextBudget.summarization`, and registering a code is additive (`COMPATIBILITY.md` §2.1; precedent: the 2.36 registry rows added by RFCs 0199/0201/0210).
- No existing field changes type, optionality, or meaning. No default flips. The "bound the transcript by default" option was explicitly rejected as a §4 behavior change (see Alternatives).

## Conformance

- **Existing:** `multi-agent-*` scenarios cover the execution loop and `transcriptWindow` count semantics; none assert a token bound.
- **New:** `context-budget-transcript-bound.test.ts`, gated on `capabilityFamily(d,'multiAgent')?.executionModel?.contextBudget?.transcriptTokenBudget` being present (soft-skip on absence). Drives a multi-turn run, reads the host-sample transcript seam each iteration, asserts `tokenCount ≤ budget` and that `keepLastTurns` recent turns appear verbatim. A second scenario `context-summarization-replay.test.ts` (gated on `summarization.supported`) replays the run and asserts the reconstructed transcript matches, exercising the `context.summarized` → `summaryRef` path.

## Alternatives considered

1. **Flip the default (`absent ⇒ bounded`).** The red-team's headline. Rejected: per `COMPATIBILITY.md` §4 this changes observed behavior on hosts that never advertised a window → safety-fix process, not additive. Not worth a v1.x break for an efficiency optimization hosts can already perform.
2. **Leave it host-internal (do nothing).** Hosts can already token-budget their own prompt assembly with zero conformance impact (Tier-A in the red-team). Rejected as the *whole* answer because it leaves the contract unobservable: a client cannot tell whether a host bounds context, so cross-host portability of "this agent stays cheap on long runs" is impossible. The capability advertisement is the value-add.
3. **Reuse RFC 0084 `budget-policy` `maxTokens`.** Rejected: `maxTokens` is a run-wide spend ceiling that *fails or interrupts* the run on exhaustion; it is not a per-turn context-shaping budget and has no summarization semantics.

## Unresolved questions

1. Should `tokenCounter` permit provider-native counters (e.g., a named Anthropic/OpenAI tokenizer) or stay at a small portable enum? The enum keeps conformance deterministic but may diverge from a host's real provider count.
2. Is `keepLastTurns` the right knob, or should the verbatim floor be expressed in tokens (`keepLastTokens`) to compose with `transcriptTokenBudget`?
3. Does `context.summarized` belong in the normative core event set, or behind the same `version >= 5` gate as the rest of the stateful loop?
4. Should the transcript seam be normative here or deferred to a host-sample-only test seam (as drafted)?

## Implementation notes (non-normative)

- Tier-A host patches that need **no** spec change and **SHOULD** ship independently of this RFC: provider context-caching (byte-identical, front-loaded system prompt), per-task tool sub-allowlisting, gzipped SSE bus, stripping optional event meta. This RFC governs only the *declarable* transcript contract; the rest is host craft.
- Sequence after the schema lands: reference host (sqlite) implements the seam + a sliding-window summarizer first; it is the simplest strategy and exercises the `context.summarized` replay path.
- **Reconcile with openwop-app ADR 0148 A1 (host-internal token-budgeted transcript assembly + `transcriptWindow` advert):** A1 is the Tier-A producer of exactly the budgeted transcript that `contextBudget` advertises and the seam witnesses — the host's A1 assembly path emits the `context.summarized` event + the seam reads its accounting. One model, not two.
- `tokenCounter` shares the RFC 0113 enum (incl. `chars`), so a host budgets transcript (0111) and memory (0113) in one consistent unit.

## Acceptance criteria

- [x] Spec text merged (`multi-agent-execution.md` §contextBudget + `host-sample-test-seams.md` seam) — landed with the `Active` surface 2026-06-26 (CHANGELOG `[Unreleased]`).
- [x] `capabilities.schema.json` + `run-event-payloads.schema.json` updated — `contextBudget` (capabilities) + `context.summarized` (run-event-payloads) both on `main`.
- [x] `context-budget-transcript-bound.test.ts` + `context-summarization-replay.test.ts` in `@openwop/openwop-conformance` — both present; capability-gated + driver-based, so they exercise non-vacuously only against a host advertising `contextBudget` on a real orchestrator loop.
- [x] CHANGELOG entry under the next minor — `[Unreleased]` "RFCs 0111 / 0114 / 0116 — token-economy surfaces (`Active`, 2026-06-26)".
- [x] A reference host advertises `contextBudget` and passes both scenarios non-vacuously, **or the RFC explicitly defers reference-host implementation** — this RFC takes the deferral: no current steward reference host runs a real orchestrator-loop model turn to witness against (the mock supervisor MUST NOT advertise `contextBudget` per §"Scope"). Both conformance scenarios soft-skip on non-advertising hosts; the non-vacuous witness comes from a host that runs real orchestrator turns (the `Active → Accepted` graduation gate). **Gate met 2026-09-26:** a host advertising `contextBudget` on a real orchestrator loop passes both scenarios non-vacuously — MyndHyve (tier-2), see `Updated`.

## References

- `spec/v1/multi-agent-execution.md` §"Stateful agent-loop lifecycle" (`transcriptWindow`, line 238).
- `spec/v1/budget-policy.md` (RFC 0084) — run-wide spend, contrasted in Alternatives.
- `COMPATIBILITY.md` §4 (behavior-only changes) — the §4 table is why the default cannot flip.
- RFC 0061 (stateful agent-loop lifecycle), RFC 0041 (replay determinism), RFC 0094 (schema closure).
- Prior art: Anthropic prompt caching; LangGraph checkpoint/summarization; sliding-window context management.
- Sibling RFCs in this set: 0112 (compact tools), 0113 (memory budget), 0114 (A2UI deltas), 0115 (transport economy), 0116 (prompt-prefix cache).
