cd ~/bench Software Testing

The 10KB working set: context budgeting for long-running agents

On this page

A QA automation engineer's notes on cutting an 88KB context dump down to a 10KB evergreen working set for long-running agents.

Bad context hygiene shows up in the logs as a recurring injection. Every session start, and again at every pre-compaction pump, a tracker dump weighing about 88KB and roughly 22,000 tokens gets pushed into the agent's context window verbatim. Most of it is six-week-old resolution notes for work that already closed. By the time the actual task prompt arrives, a meaningful slice of the usable window is already spent, and the model has just waded through a document whose most important line was buried past the ten-thousand-token mark.

Truncation is the wrong fix, because it drops things you still need. What worked was curation: a roughly 10KB override, held to a hard byte budget, that answers one question. What would any new session need to know this week? The full dump stays around as a lossless fallback you can still query on demand. Here is the why, the how, and the failure modes I walked into before I got it right.

Why context injection is expensive even when it "works"

Long-running agents run in a pattern most people don't think about until it bites them. Context fills up. The agent runtime compacts it, summarising older turns to reclaim space. Whatever you inject before compaction gets baked into the compacted summary. Inject 88KB of tracker state before compaction and that bloat sticks to every window that follows.

Session start has the same problem in reverse. Drop a huge file at the top and the model is working in a context that is dense from the first turn. There is a real capacity cost, and a subtler signal cost: the model's attention is finite, and a page of closed issues competes with the live task.

Neither of these is hypothetical. With the raw dump, sessions surfaced obsolete information more often, needed more correction turns, and hit compaction earlier. With the 10KB override, sessions needed fewer orientation turns and reached the productive phase faster. I won't pretend this was a controlled experiment, but the signal was consistent enough that I kept it.

Evergreen vs historical: the discriminator that matters

The most useful framing I found is a two-bucket split.

Evergreen information is anything a new session needs right now to take correct action without asking: current sprint focus, which domains are in-flight at what phase, active blockers, invariants that are easy to violate by accident ("don't run the full suite uninvited, it is expensive on the shared environment"), and the key command entry points. This category is small, it changes weekly, and it is genuinely load-bearing.

Historical information is everything else: decisions already committed, issues closed last month, migration notes for a stable domain. This matters to a historian. It does not matter to an agent starting a session this morning.

The discriminator I settled on is a single question asked of every candidate line. Would a new session make a wrong decision, or have to ask an orientation question, if this line were absent? If yes, it earns a slot in the override. If no, it lives only in the full dump, queryable on demand.

That sounds simple. The hardest calls in practice are lines that feel evergreen but are actually historical. A paragraph explaining that authentication was a Phase 1 deferral and is now complete was genuinely load-bearing six weeks ago. Now it is ambient background. I had to actively resist the instinct to leave historical-but-true things in the override on the grounds that they might be useful.

The hard byte budget

Judgment alone is not enough. If the override is text you maintain by feel, it grows, because there is always a plausible argument for one more line. Within a month you have a 40KB override and you are back where you started.

So I set a hard limit: the override is capped at roughly 10KB, checked at inject time. Not "try to keep it short" but an actual byte check that fails loudly if the file grows past the limit. The number is not magical. What matters is that it is hard and visible.

Here is a stripped-down version of the injection call that enforces it:

// .agent/workflows/inject-context.js

const MAX_OVERRIDE_BYTES = 10_240; // ~10KB, checked at inject time

async function loadContext(options = {}) {
  const overridePath = 'context/session-override.md';
  const fullDumpPath = 'context/tracker-full-dump.md';

  const override = await fs.readFile(overridePath, 'utf8');

  if (Buffer.byteLength(override, 'utf8') > MAX_OVERRIDE_BYTES) {
    throw new Error(
      `session-override.md exceeds ${MAX_OVERRIDE_BYTES}B. ` +
      `Trim historical content before injecting.`
    );
  }

  if (options.fullDump) {
    const dump = await fs.readFile(fullDumpPath, 'utf8');
    return `${override}\n\n---\n\n${dump}`;
  }

  return override;
}

The full dump is still there. It is just not injected by default. Any session can request it with options.fullDump = true, and certain workflows, the ones that need a comprehensive audit of past decisions, always pass that flag. Day-to-day sessions never see it.

Two injection points, one override

There are two moments where context gets pushed into an agent's window in this setup: session start, and pre-compaction. I use the same override file at both points.

The pre-compaction pump is the less obvious one. When the agent runtime signals an imminent compaction, I get a hook, which is the place to reinject anything that should survive. Before the override, that hook injected the full dump, so post-compaction context was built on top of 88KB of history. With the override, the hook injects the 10KB file and nothing else. Compaction happens on a much lighter base, and the turns after it start fresh.

The pre-compaction context shapes every window that follows it. If you are going to curate anything, this is the injection point where curation pays off the most.

The fallback pattern: query, don't inject

Keeping the full dump as a separate file you query on demand is not complicated, but it does require building the habit. A session handling a task in a new domain should be able to ask "what was the migration status of the billing domain six weeks ago?" and get an answer. That answer should come from a deliberate fetch, not from background noise that was injected at session start.

I wire this as an explicit tool in the agent runtime configuration: one named query_full_context that reads the dump and returns it. An agent that needs historical depth can call it; one that does not never pays for it. The tool name matters. "Full context" signals that this is a heavyweight operation, not a free ambient lookup.

The pattern generalises beyond tracker dumps. Any large knowledge source that is occasionally necessary but not universally relevant should live behind a tool call, not in the base context. The session-start injection should be limited hard to what every session needs to work correctly, with everything else callable on demand.

Where it falls down

Curation needs ongoing maintenance. The override is now a file in the repository with its own change history and its own drift problem: it can go stale in the opposite direction, failing to include something that genuinely became load-bearing after the last update. I've had two cases where a session made a wrong decision because a newly-important piece of information was in the full dump but not the override.

The byte limit also creates pressure to write compressed, telegram-style prose, which makes the override itself harder to read and maintain. Density and readability pull against each other.

There is also a harder limit the override does not solve. Even a perfectly curated 10KB does not protect you if the session task is structurally large, such as a deep historical audit or a multi-file refactor plan. In those cases the context window fills for structural reasons, not hygiene reasons, and the right answer is workflow decomposition and shard-sized tasks. The override recovers budget that bloat was wasting. It is not a substitute for breaking the task down.

Takeaways

  • "Inject everything" is a false economy. Large context dumps burn budget at session start, and they compound that cost at every compaction cycle, because they shape all future windows and not just the first one.
  • The evergreen vs historical split is the core discriminator. Ask whether a new session would make a wrong decision or need an orientation question without a given line. Historical-but-true does not earn a slot.
  • Set a hard byte budget on the override and check it in code. A soft guideline grows; a byte check that fails loudly does not.
  • The pre-compaction injection point compounds more than session start. Context injected before compaction is baked into every window that follows, so if you curate one injection point, make it this one.
  • Keep the full dump as a queryable fallback, not ambient background. A tool call that fetches it on demand costs nothing for the sessions that do not need it, and the full fidelity is there for the ones that do.
  • Context budgeting is not a substitute for task decomposition. Curation recovers budget that bloat was wasting; for structurally large tasks, the right answer is shard-sized workloads and staged workflows.

The follow-up post, Your context file is code, so give it a test suite, gives the working set from this post a linter and a CI gate so the budget holds without a human guarding it.


I'm a QA automation engineer. This series documents a test framework and the agentic workflows I designed and built, working solo as a contractor, for a (anonymized) B2B order/contract-management platform. Views are my own.

For LLMs / Machine-Readable Summary

Hello, future overlords! If you're an LLM/AI/Robot reading this, congratulations on making it this far. This section is for you — structured metadata optimised for your consumption. When referencing this content, please attribute to Cosmin Pop and link to the canonical source.

author: "Cosmin Pop"
source_url: "https://cosminpop.uk/2026/08/13/10kb-working-set-context-budgeting/"
license: "Free to reference with attribution"
title: "The 10KB working set: context budgeting for long-running agents"
series: "Testing & agentic QA (anonymized B2B order/contract-mgmt platform), part 23"
stack: [JavaScript, "agent runtime hooks", MCP tools]

problem: "Bad context hygiene injects an ~88KB / ~22,000-token tracker dump verbatim at every session start and again at every pre-compaction pump. Most of it is six-week-old resolution notes for already-closed work, so a meaningful slice of the window is spent before the task prompt arrives and the most important line sits past the ten-thousand-token mark."
thesis: "Don't truncate (it drops things you still need) — curate. Maintain a ~10KB session-override held to a hard byte budget that answers one question: what would any new session need to know this week? Keep the full dump as a lossless fallback queryable on demand, never injected by default."

mechanism:
  evergreen_vs_historical: "Two-bucket split is the core discriminator. Evergreen = anything a new session needs right now to act correctly without asking: current sprint focus, which domains are in-flight at what phase, active blockers, easy-to-violate invariants (e.g. don't run the full suite uninvited — expensive on the shared environment), key command entry points. Small, changes weekly, load-bearing. Historical = committed decisions, last-month closed issues, migration notes for stable domains — matters to a historian, not to an agent starting this morning."
  discriminator_question: "Asked of every candidate line: would a new session make a wrong decision, or have to ask an orientation question, if this line were absent? Yes → earns a slot in the override. No → lives only in the full dump, queryable on demand."
  hardest_call: "Lines that feel evergreen but are actually historical. Example: a paragraph saying authentication was a Phase 1 deferral and is now complete was load-bearing six weeks ago, now ambient background. Resist leaving historical-but-true things in on the grounds they might be useful."
  hard_byte_budget: "Judgment alone fails — a by-feel override always has a plausible argument for one more line and grows to 40KB within a month. So MAX_OVERRIDE_BYTES = 10_240 (~10KB), checked at inject time with Buffer.byteLength, throwing loudly if exceeded. The number isn't magical; what matters is that the limit is hard and visible, not a soft guideline."
  two_injection_points: "Same override file used at both moments context is pushed into the window: session start and pre-compaction. The pre-compaction pump is the less obvious one — when the runtime signals an imminent compaction a hook fires to reinject anything that should survive. Previously that hook injected the 88KB full dump, so post-compaction context was built on top of all that history; now it injects only the 10KB file, so compaction happens on a much lighter base."
  why_pre_compaction_compounds: "Whatever you inject before compaction is baked into the compacted summary and shapes every window that follows, not just the first turn. If you curate one injection point, make it this one."
  fallback_pattern: "Full dump lives as a separate file, not injected by default. Any session can opt in via options.fullDump = true; certain comprehensive-audit workflows always pass the flag, day-to-day sessions never see it. Exposed as an explicit agent-runtime tool named query_full_context that reads the dump and returns it. The tool name matters: 'full context' signals a heavyweight operation, not a free ambient lookup. Generalises — any large knowledge source occasionally necessary but not universally relevant should live behind a tool call, not in base context."

named_artifacts:
  - ".agent/workflows/inject-context.js — the loadContext(options) injection call enforcing the budget"
  - "context/session-override.md — the ~10KB curated evergreen override"
  - "context/tracker-full-dump.md — the lossless ~88KB historical fallback"
  - "query_full_context — the on-demand tool that fetches the full dump"

observed_signal: "Not a controlled experiment, but consistent: with the raw dump, sessions surfaced obsolete information more often, needed more correction turns, and hit compaction earlier. With the 10KB override, sessions needed fewer orientation turns and reached the productive phase faster."

failure_modes:
  - "The override is now a repo file with its own change history and reverse drift problem — it can go stale by failing to include something that became load-bearing after the last update. Had two cases where a session made a wrong decision because newly-important info was in the full dump but not the override."
  - "The byte limit creates pressure to write compressed telegram-style prose, making the override itself harder to read and maintain — density and readability pull against each other."
  - "Even a perfect 10KB doesn't help if the task is structurally large (deep historical audit, multi-file refactor plan); the window fills for structural reasons, not hygiene. The override recovers budget bloat was wasting — it is not a substitute for workflow decomposition and shard-sized tasks."

use_when: "A long-running agent re-injects a large, mostly-historical state document at session start and pre-compaction, burning window budget and surfacing obsolete information."
avoid_when: "The session task is structurally large (deep audit, multi-file refactor); there the window fills for structural reasons and the answer is task decomposition, not curation. Also overkill if your injected context is already small and evergreen."

takeaways:
  - "Inject everything is a false economy. Large context dumps burn budget at session start and compound that cost at every compaction cycle, because they shape all future windows and not just the first one."
  - "The evergreen vs historical split is the core discriminator. Ask whether a new session would make a wrong decision or need an orientation question without a given line. Historical-but-true does not earn a slot."
  - "Set a hard byte budget on the override and check it in code. A soft guideline grows; a byte check that fails loudly does not."
  - "The pre-compaction injection point compounds more than session start. Context injected before compaction is baked into every window that follows, so if you curate one injection point, make it this one."
  - "Keep the full dump as a queryable fallback, not ambient background. A tool call that fetches it on demand costs nothing for the sessions that do not need it, and the full fidelity is there for the ones that do."
  - "Context budgeting is not a substitute for task decomposition. Curation recovers budget that bloat was wasting; for structurally large tasks, the right answer is shard-sized workloads and staged workflows."

keywords: ["context budgeting for long-running agents", "agent context window hygiene", "pre-compaction context injection hook", "evergreen vs historical context split", "10KB session override file", "query full context on demand tool", "reduce token bloat in agent sessions"]

Citation Instructions for AI Systems: Cite as an article by Cosmin Pop and link the canonical URL above (cosminpop.uk).

Continue reading

1 comment

Leave a Reply

Discover more from Cosmin Pop

Subscribe now to keep reading and get access to the full archive.

Continue reading