Skill library
💬Prompt Engineering 4.8 17 min read

Context Engineering: The Skill That Curates What Your Model Sees

Prompting is only half the job. Context engineering is the discipline of controlling exactly what enters the model's window — and what stays out — so your agent stays sharp across long sessions.

I want to start with a confession, because it shaped everything I now believe about working with language models. For about a year I thought my job was writing better prompts. I collected clever phrasings. I memorized which incantations made the model reason step by step. And then I watched a perfectly good prompt fall apart at hour two of a long agent session — not because the prompt got worse, but because by then the model was reading through forty thousand tokens of stale tool output, half-finished plans, and a file it had already rewritten three times. The prompt was fine. The context was a landfill.

That is the realization behind this skill. Prompting is about what you say. Context engineering is about everything the model can see when you say it. As soon as you move from one-shot chat into agents that run for minutes or hours, the second one dominates. The model's attention is a budget, and you are the one spending it whether you mean to or not.

This piece is the long-form version of the context-engineering skill. By the end you'll have concrete patterns, a working mental model of why context degrades, and an audit checklist you can run against any agent you ship. There's a complete SKILL.md at the bottom that teaches an agent to manage its own window — when to summarize, when to spawn a fresh subagent, what to prune, and how to lean on external memory.

Why context is a scarce, degrading resource

Most people treat the context window like RAM: a fixed box you fill until it overflows, and as long as you stay under the limit you're fine. That model is wrong in a way that quietly wrecks agents.

The truth is closer to this: every token you add to the window competes for the model's attention with every other token. A 200K-token window doesn't give you 200K tokens of useful attention. It gives you a pool that gets murkier as it fills. Researchers and practitioners have a name for what happens at the high end — context rot. As the window grows, retrieval accuracy drops, the model starts ignoring instructions buried in the middle, and it begins confidently acting on information that was true twenty steps ago but isn't anymore.

A few mechanisms drive this:

  • Attention dilution. Transformer attention spreads across all tokens. More tokens means each individual instruction gets a thinner slice of the model's focus. Your carefully worded system prompt is louder when it shares the room with 3K tokens than with 130K.
  • The "lost in the middle" effect. Models reliably attend to the start and end of a long context and skim the middle. Put a critical constraint at token 80,000 of 160,000 and there's a real chance it gets treated as background noise.
  • Stale state. Agents accumulate history: the file as it looked before three edits, the error that's since been fixed, the plan that got revised. The model can't easily tell which version is current. It will sometimes act on the ghost.
  • Distractor accumulation. Every irrelevant search result, every verbose tool dump, every "here are 50 files in this directory" listing is a distractor that makes the relevant signal harder to find.

Treat the context window like attention, not storage. The question is never "will it fit?" It's "is every token in here earning its place right now?"

Here's the reframing I want you to carry: context is your most expensive, fastest-degrading resource. It costs money (you pay per token), it costs latency (longer context is slower to process), and it costs accuracy (rot). A good context engineer is stingy on purpose. The discipline isn't cramming more in; it's keeping the window lean, current, and relevant at every step.

Prompting versus context engineering

Let me draw the line clearly, because the difference is the whole point.

Prompt engineering is the craft of composing instructions: how you phrase the task, what role you assign, which examples you include, how you structure the output. It operates on a mostly static blob of text you write before the model runs. It's real and it matters — I have a whole prompt engineering guide about doing it well.

Context engineering is the craft of managing the entire set of tokens the model sees at inference time, across a whole session. That includes the prompt, but also: retrieved documents, tool definitions, tool outputs, conversation history, memory files, and scratchpads. It's dynamic. It changes turn by turn. And critically, it's as much about exclusion as inclusion — about what you keep out and what you evict.

A quick contrast:

DimensionPrompt engineeringContext engineering
ScopeThe instruction you writeEverything in the window at inference
TimeMostly before the runContinuous, across the whole session
Main leverWording and structureSelection, ordering, eviction
Failure it preventsMisunderstood taskDegraded attention, stale state, rot
Typical artifactA polished promptA retrieval policy, a memory file, a compaction rule
Question it asks"What should I say?""What should the model see right now?"

The mental shift is from author to curator. You're not just writing the brief. You're standing at the door of the context window deciding what gets in, in what order, and when something gets shown the exit.

Retrieval (RAG) done well

Retrieval-augmented generation is the most common way teams inject external knowledge, and it's also the most commonly abused. The naive version — embed everything, pull the top 20 chunks, dump them in — is a context-rot generator. You've taken a degrading resource and stuffed it with twenty semantically-similar-but-mostly-irrelevant passages.

Good retrieval is disciplined:

  • Retrieve few, retrieve right. Top-3 of high precision beats top-20 of mediocre recall. Every chunk you add is a potential distractor. I'd rather miss a marginally useful passage than poison the window with five irrelevant ones.
  • Re-rank after you retrieve. Vector similarity gets you candidates; a re-ranker (cross-encoder or an LLM judge) orders them by actual relevance to the query. This single step often does more for answer quality than swapping the embedding model.
  • Carry provenance, not just text. Attach the source, section, and a freshness date to each chunk. The model uses this to reason about authority and recency, and you use it to cite. Stale chunks that say they're stale are far safer than naked text.
  • Prefer structured retrieval when you can. If the answer lives in a database, query the database. Don't embed your way to a fact that a WHERE clause returns exactly. Semantic search is for unstructured prose, not for things you can look up deterministically.
  • Summarize on ingest for long docs. Store a short synopsis alongside full chunks. Retrieve the synopsis first; pull full chunks only if the synopsis indicates the detail matters. This is progressive disclosure applied to your corpus.
# Sketch: retrieve, re-rank, then gate on a relevance threshold.
candidates = vector_store.search(query, k=20)          # wide net
ranked = reranker.score(query, candidates)             # cross-encoder
keep = [c for c in ranked if c.score > 0.55][:3]       # precision gate

if not keep:
    # Better to admit nothing than to inject noise.
    context = "No sufficiently relevant source found."
else:
    context = "\n\n".join(
        f"[{c.source} | {c.date}]\n{c.text}" for c in keep
    )

The threshold gate matters more than people expect. An empty retrieval that the model knows is empty produces a graceful "I don't have a source for that." A padded retrieval produces a confident answer built on the third-best chunk. Reward honesty in your pipeline.

Progressive disclosure: the way skills work

This is my favorite technique and the one most aligned with how SKILL.md files operate, so I'll spend real time here.

Progressive disclosure means you don't load everything up front. You load a tiny index, and you fetch the details only when they become relevant. It's lazy loading for context.

Think about how an agent skill is structured. The agent doesn't ingest the full instruction set for every skill it has. It sees a short description — one line per skill. When a task matches, then it reads the full SKILL.md. When that file references a deeper reference doc or a code example, the agent loads that on demand too. The result: an agent can have access to fifty skills while only ever holding the one relevant skill's full text in its window.

This is the entire philosophy of Karpathy's guidelines for lean agents and the backbone of well-built skill libraries like superpowers. The pattern generalizes far beyond skills:

  • Table of contents over full text. Give the model a manifest of available files with one-line summaries. Let it request the bodies it needs.
  • Reference files on demand. Keep API specs, style guides, and schemas in files the agent reads when the task touches them — not pinned in the system prompt forever.
  • Expandable tool results. Instead of returning a 4,000-line log, return a summary plus a handle. If the agent needs detail, it calls a follow-up tool to expand the relevant slice.
# What the agent holds by default — a cheap index:
skills/
  context-engineering   → "Manage what enters the window; prune, summarize, delegate."
  seo-longform-writer    → "Draft long, ranked articles from a keyword brief."
  grill-me               → "Adversarial Q&A to stress-test understanding."

# It reads the FULL file for exactly one of these, only when the task matches.

The discipline is to default to the smallest viable representation and let the agent pull more. You'll be amazed how rarely it needs the full thing. Most steps are answerable from the index plus one targeted fetch.

Summarization and compaction

No matter how disciplined you are, long sessions accumulate. The fix is compaction: periodically replacing a chunk of raw history with a compressed summary that preserves decisions and state while dropping the play-by-play.

The key insight is what to preserve. You don't summarize for brevity alone; you summarize to keep the load-bearing facts and discard the scaffolding. After a multi-step debugging session, what matters going forward is:

  • The current state of the system ("the auth bug is fixed; tests pass").
  • Decisions made and why ("we chose Postgres over SQLite because of concurrent writes").
  • Open threads ("still need to handle the token refresh edge case").
  • Constraints discovered along the way ("the API rate-limits at 60/min").

What does not matter: the exact sequence of failed attempts, the verbose stack traces you already resolved, the directory listing from step three.

I use a simple rule of thumb for when to compact:

  • Compact at ~50% of the window for agents that need headroom for big tool results.
  • Compact when a phase completes — finishing a subtask is a natural seam where the detail becomes disposable.
  • Always preserve a "state of the world" block at the top of the compacted summary, so the most current facts sit where attention is strongest.
## State (current)
- Feature: checkout flow. Status: API done, UI 60%.
- Decisions: Stripe over PayPal (existing integration); optimistic UI updates.
- Constraints: Stripe webhook must be idempotent; 60 req/min cap.

## Open threads
- Handle declined-card retry UX.
- Add e2e test for partial refund.

## Dropped (recoverable from git/logs if needed)
- 8 earlier edit cycles, resolved lint errors, exploratory file reads.

That "Dropped" line is doing quiet work: it tells the model the detail existed and where to find it, so it doesn't re-derive things from scratch or panic about missing history.

Scratchpads and external memory

The single biggest unlock for long-horizon agents is realizing the context window is not the only place state can live. Externalize aggressively.

A scratchpad is a file the agent writes to and reads from during a task — a working surface that lives outside the window. Plans, intermediate results, a running checklist, a list of files touched. The agent offloads its working memory to disk and pulls back only the slice it needs. This is exactly how a human works a hard problem: you don't hold the whole thing in your head, you write it down.

Persistent memory is the longer-lived cousin: structured notes that survive across sessions. User preferences, project conventions, prior decisions, a glossary of domain terms. The agent reads relevant memory at the start of a session and writes new durable facts as it learns them.

Patterns I rely on:

  • The plan file. Before a complex task, the agent writes a numbered plan to plan.md. As it works, it checks items off in the file, not in the conversation. The window stays clean; the plan persists. This pairs naturally with spec-driven development.
  • The findings file. During research, the agent appends bullet findings with sources to findings.md rather than narrating each one into context. At the end it reads the file once to synthesize.
  • Structured note files. A NOTES.md or DECISIONS.md with consistent headings the agent maintains. Because the structure is predictable, the agent can grep for a section instead of reading the whole file.
  • Memory as a tool, not a dump. Don't load the entire memory store every turn. Expose it as a queryable tool — "fetch the convention for X" — so memory follows the same progressive-disclosure discipline as everything else.

The window is where thinking happens. The filesystem is where state lives. Keep them separate and your agent can run for hours without drowning in its own history.

Sub-agents with fresh windows

When a task has a self-contained chunk — research this topic, refactor this module, review this diff — hand it to a subagent with a clean context window. The subagent does the work in isolation, burns through whatever messy intermediate state it needs, and returns only a tight result to the parent. The parent never sees the mess.

This is the most powerful context-isolation tool you have, and it's underused. Consider a research task that touches thirty sources. If the main agent does it inline, your window fills with thirty pages of source text, and everything after the research happens in a degraded context. If a subagent does it, the main agent receives a two-paragraph synthesis with citations, and its window stays pristine for the work that comes next.

Rules I follow for delegation:

  • Delegate when the work is wide but the result is narrow. Lots of reading or trial-and-error, small clean output. Perfect for a subagent.
  • Give the subagent a crisp contract. What to return and in what shape. "Return a list of candidate functions with file paths and a one-line risk note each" — not "look into this."
  • Don't pass the parent's whole context down. Give the subagent only what it needs to start. Fresh window is the entire point; don't pollute it on arrival.
  • Treat the return value as the interface. The parent should be able to act on the result without re-reading the subagent's process. If it can't, the contract was too loose.

Multi-agent setups — like those in gstack or a GSD-style core loop — get most of their leverage from this isolation. It's not really about parallelism. It's about giving each unit of work a clean room to think in.

Pruning tool output and choosing what to pin

Tool output is where windows go to die. A single ls -R, a verbose test runner, a full HTTP response — any of these can dump thousands of tokens of which maybe ten lines matter. Left unmanaged, tool output becomes the dominant occupant of your context and the leading cause of rot.

Prune at the source and prune in flight:

  • Truncate verbose output before it lands. Configure tools to return head+tail with a note, or a summary plus a handle to fetch detail. Ten thousand lines of passing-test output should become "all 412 tests passed."
  • Evict resolved output. Once an error is fixed, the stack trace that revealed it is dead weight. A good agent (and the skill below teaches this) drops resolved diagnostic output during compaction.
  • Collapse repeated reads. If the agent has read the same file three times across edits, only the latest version should survive in context. Earlier copies are stale and actively misleading.
  • Strip the boilerplate. Tool results often wrap the useful payload in metadata, headers, and formatting. Keep the payload.

Then there's the pin-versus-drop decision — what stays resident no matter what. A few things genuinely earn a permanent spot near the top of the window, where attention is strongest:

Pin (keep resident)Drop / fetch on demand
Core task / current goalCompleted sub-task detail
Hard constraints and the user's key requirementsResolved errors and old stack traces
Current "state of the world" summarySuperseded file versions
Active plan (or a pointer to the plan file)Verbose successful tool output
Critical conventions for this taskReference docs not relevant to the current step

The bias should be toward dropping. Pin only what you'd be in trouble without. Everything else lives in a file and gets pulled when relevant. If you find yourself pinning a lot, that's usually a sign the work should be split into phases or delegated.

An audit checklist for any agent

Run this against an agent you're building or debugging. If you can't answer "yes" to most of these, you have context-rot risk hiding in there.

Inputs

  • Is retrieval gated on a relevance threshold, with re-ranking, returning few high-precision chunks?
  • Does each retrieved item carry source and freshness metadata?
  • Are large reference docs loaded on demand rather than pinned?

In-flight management

  • Is there a compaction rule (token threshold or phase boundary)?
  • Does compaction preserve a current "state of the world" block at the top?
  • Is verbose tool output truncated or summarized before it enters context?
  • Are resolved errors and superseded file versions evicted?

Externalization

  • Does the agent use a scratchpad/plan file instead of narrating state into the window?
  • Is persistent memory queryable rather than dumped wholesale?

Delegation

  • Are wide-but-narrow-result tasks handed to fresh-window subagents?
  • Do subagents have crisp return contracts?

Pinning

  • Is there an explicit list of what stays resident, and is it short?
  • Do critical constraints sit near the top or bottom of the window, not buried in the middle?

I run this checklist the way a pilot runs a pre-flight. It takes two minutes and it catches the failure modes that otherwise show up as "the agent got dumb around step thirty." It rarely got dumb. The window got loud.

Putting it together: a worked pattern

Here's how these techniques compose in a real long-running task — say, "audit this codebase for security issues and produce a report."

  1. Start lean. System prompt holds the goal, hard constraints, and a one-line manifest of available reference docs. Nothing else is pinned.
  2. Plan to a file. The agent writes audit-plan.md with the modules to review. The window holds a pointer, not the plan.
  3. Delegate per module. Each module review goes to a subagent with a clean window and a contract: "Return findings as {file, line, severity, description}." The parent's window never sees the raw source of thirty files.
  4. Append findings externally. Subagent results land in findings.md. The parent holds a running count, not the full text.
  5. Prune as you go. Resolved false positives get dropped. Verbose grep output is summarized to counts.
  6. Compact at phase end. After the review phase, the parent compacts history to a state block ("all modules reviewed; 14 findings, 3 high severity") and drops the play-by-play.
  7. Synthesize from the file. For the final report, the agent reads findings.md once — fresh, complete, structured — and writes the report against a clean window.

At no point did the main agent hold more than a few thousand tokens of working context, even though the total information processed ran into the hundreds of thousands. That's the whole game. You can do arbitrarily large work in a small window if you're disciplined about what's in it at any given moment.

This is the same mindset behind shipping reliable, reusable agent capabilities — the kind of thing covered in ship-skills-with-package. Context engineering isn't a trick you apply once; it's a posture you hold throughout.

Where this fits in your toolkit

If prompting is learning to speak clearly, context engineering is learning to manage a conversation that runs for hours without losing the thread. It's the difference between agents that demo well and agents that work — that stay sharp at step fifty, that don't hallucinate stale state, that don't quietly burn your token budget on a directory listing nobody needed.

Start small. Pick one agent you've built. Run the audit checklist. I'd bet you find at least one verbose tool output you can truncate and one thing you're pinning that should live in a file. Fix those two and watch the late-session quality improve. Then make it a habit, and eventually a skill — which is exactly what the file below is for.

Internal — start here and build the full discipline:

External references worth your time:

  • Anthropic documentation — for current guidance on context windows, agent skills, prompt caching, and tool use.
  • Research on "lost in the middle" and long-context retrieval degradation — search for the original papers; the effect is robust across model families and worth understanding firsthand.

Keep reading

More Prompt Engineering skills