Context Goes Stale
Make freshness, version, validity, and invalidation explicit in context assembly.
The agent reads the project’s decision record at repository version C1: backend SQLite, migration failing, README describing the current backend. It plans accordingly. By the time it acts, the repository is at C5. The decision record now says PostgreSQL, the migration passes, the README was rewritten twice. Nothing in the agent’s context changed — which is exactly the failure. The captured observation was authoritative, relevant, well-represented, and faithfully preserved. It described a state that no longer exists, and the model configured SQLite with complete confidence. Every chapter before this one asked whether information should enter context. This one asks whether what entered is still true of the world it claims to describe.
Context is a snapshot. Current behaviour should not silently depend on snapshots whose validity against the relevant source state has become unknown.
Old is not stale
The chapter’s central distinction, stated first because everything else depends on it:
AGE
time since capture
≠
FRESHNESS
whether the represented source state
still holds for the current operation
Freshness is whether a represented observation or claim remains valid against the source state relevant to the current computation — never merely now-minus-timestamp. Age is observable; freshness usually requires comparison against the source. A six-month-old immutable decision can remain current. A one-second-old branch-status observation can already be obsolete. The strict inequalities follow: old is not stale, recent is not current, authoritative is not current, a changed source does not falsify every derived claim, and an elapsed time-to-live does not make a fact false. Freshness must never become a synonym for recency, and the chapter polices the word accordingly: nothing is called stale here when merely old is meant.
Authority and freshness vary independently in both directions. The old decision-record observation above lost currency without losing source authority — the record remains the authoritative source for the backend choice, and the correct response is revalidation or replacement of the observation, never demotion of the source. Conversely a current web page is not authoritative over a project decision by virtue of being newer. Chapter 19’s hierarchy survives untouched: freshness operates after authority and source semantics, replacing none of them, and newer chatter never invalidates a canonical record by timestamp alone.
A timestamp is not a version
A captured item needs two different pieces of temporal evidence, and a single timestamp field cannot carry both:
observed_at when was the source observed?
source_version which source state was observed?
The first answer is a timestamp. The second answer is a version or validator identity: a Git commit, tree, or blob identifier; an HTTP entity tag; a database row version; an API revision; a document version; a configuration generation; an artifact revision. No universal mechanism is required — the distinction is the point. A timestamp says when observation happened. A version identifies what was observed. And versions need not be clock-based at all: Git identifiers name immutable content and history without encoding time, and HTTP entity tags are opaque representation validators that need not be hashes, clocks, or anything but comparable. Newer timestamp never means newer truth; that equation is refused wherever it appears.
The established systems literature already draws this split cleanly, and the chapter imports it without importing the tutorial. RFC 9111 on HTTP caching defines a fresh response as one whose age has not exceeded its freshness lifetime, with stale as the complement — and separately defines validation, where a cache holding a validator asks the origin whether its stored version still corresponds to current state rather than downloading everything again. RFC 9110 supplies the validator vocabulary: entity tags and last-modified times, strong validators guaranteeing byte equivalence against weak validators asserting semantic equivalence, conditional requests carrying them back. The engineering pattern transfers whole:
age policy
≠
source validation
A freshness lifetime decides when reuse without checking expires. A validator answers whether anything actually changed. One schedules doubt; the other resolves it.
TTL tells you when to ask
Provider prompt-cache lifetimes belong to a different ledger and must never be confused with the above. A prompt-cache TTL answers how long previously computed prefix state stays reusable — an economic runtime property. A freshness TTL answers how long a context observation may be accepted without rechecking its source. Compute-reuse lifetime and revalidation policy share nothing but four letters, and the bare phrase cache TTL is banned from this chapter wherever the two could meet.
A freshness TTL is a policy, not a truth oracle, and the durable principle reads:
TTL decides when uncertainty becomes expensive enough to check. It does not determine when reality changed.
At 4:59 inside a five-minute TTL the fact may already be false; at 5:00 past it the source may be untouched. Sources change inside lifetimes and outlive them routinely. The chapter is therefore designed around TTL’s two predictable failure modes rather than its average case. Fast change: a ten-minute TTL admits a thirty-second-old falsehood for nine and a half minutes — false freshness, stale admission. Stable source: the same TTL re-reads an unchanged six-month-old record every ten minutes forever — unnecessary validation and refresh. Any experiment missing either case cannot claim to have tested TTL fairly, and both appear below by construction.
Revalidation and refresh separate accordingly. Revalidation asks whether the observed version still corresponds to the relevant source state, with three legal answers: unchanged, changed, unknown. Refresh acquires the new representation only when revalidation establishes — or strongly indicates — that the old one cannot safely be reused. Revalidate is not re-read-everything; a strong validator avoids the full read entirely, which is one of the chapter’s main mechanisms. UNKNOWN stays a first-class outcome rather than a gap to pave over: where validity cannot cheaply be established, policy may reread, defer, abstain, or accept bounded risk depending on the operation — but missing freshness evidence is never converted into fresh. Invalidation, finally, marks a representation as no longer safe to treat as current without further validation or replacement. Invalidated never means proven false. A changed source can retire an old representation simply because nothing now licences its reuse, and claiming more than that would overstate what changed.
flowchart TD
C["Candidate<br/>observed_at and source_version recorded"] --> Q("Does this operation need<br/>current state?")
Q -->|"no, for example the task<br/>names a frozen version"| A0["Admit as captured"]
Q -->|yes| V("Revalidate against<br/>the source")
V -->|unchanged| A1["Admit without a full reread"]
V -->|changed| I["Invalidate the old representation"]
I --> R["Refresh: acquire the new one"]
V -->|unknown| U["Reread, defer, abstain,<br/>or accept bounded risk<br/>never treated as fresh"]
How coarse is the version?
Suppose the README fixes one spelling mistake. A captured claim that the database is PostgreSQL still holds — unless the only validator is the repository commit hash, in which case every commit invalidates everything. Safe, coarse, and wasteful. That gap creates the chapter’s granularity question: at what level should context be versioned. Repository commit, directory tree, file blob, structured record, individual derived claim — coarser is simpler, safer, and more often unnecessarily invalidating; finer reuses more and costs dependency and lineage complexity. No universal level is prescribed. The trade is measured.
Git gives the coding-agent book a concrete coarse-versus-fine case, used for the mechanism rather than taught for its internals. Commits identify complete trees plus history metadata; trees identify directory state; blobs identify file contents; all three are immutable once created. Capture config/database.toml at blob B1 under repository commit C1; later the repository stands at C2 while the file still maps to blob B1. The repository changed and this file did not — coarse invalidation pays a reread it never needed, fine identity reuses safely. But content identity is not sufficient, and the chapter refuses to overlearn its own example. Identical bytes can mean different things when the dependency version, environment, schema, or external API moved underneath them. The correct validator follows the dependency whose state licences the item, not merely the bytes in front of the model — which is where derived state enters.
Freshness is dependency-relative in general. A file observation depends on its blob or version. A test result depends on the source tree, the test configuration, the lock state, and the environment where relevant. No build-system dependency tracker is constructed; the concept travels exactly as far as the experiment needs. Tool observations inherit the same treatment with Chapter 17’s volatility made explicit: branch status, open pull requests, test results, server health, current branch, database rows, API responses — radically different lifetimes, none of them granted permanent residency for having been exact at capture. Generated state reuses Chapter 15’s vocabulary without reopening it: trajectory-obsolete (the step says run the migration test after the test finished) is a freshness class here, world-stale belongs to the same machinery with external dependencies. Memory candidates arrive with whatever validity the memory layer exposes, and Context asks only whether the evidence suffices to treat the representation as current — the durable historical model stays upstream. External artifacts refine Chapter 13’s identity into version semantics: a reference must mean either latest or an exact frozen revision, never an ambiguous both, with exact identity in controlled runs. Retrieved candidates gain the parallel inequality to Chapter 14’s: retrieved is not current, and relevance scoring stays distinct from temporal validity.
What the evidence shows
HoH, an ACL 2025 long paper, builds a large QA benchmark capturing real temporal knowledge evolution through diff algorithms plus model pipelines, and reports two effects the chapter imports narrowly: outdated retrieved material substantially reduces answer accuracy by distracting models from correct information, and it can mislead models into harmful outputs even when current information is also present. Current retrieval and generation approaches struggle on both fronts in the authors’ measurements. The phenomenon transfers; no effect size travels to coding agents, and the leaderboard stays in Vienna.
Re³, an ACL 2026 long paper, targets relevance and recency jointly: a time-aware encoder embedding heterogeneous temporal signals with a conflict-aware recency filter arbitrating among obsolete factual versions, evaluated on three public benchmarks plus a 1.3-million-instance temporal benchmark with reported average gains near ten per cent and larger maxima. The chapter takes one sentence from it: dynamic settings must separate semantic relevance from obsolete factual versions — relevance is not currency. Its retrieval architecture is not the Context solution, and Chapter 14’s separation is cited rather than rebuilt.
ContextNest, a July 2026 preprint, is the closest architectural neighbour and is handled with the corresponding care: governed knowledge vaults with version identity, hash-chained histories, point-in-time reconstruction, and audit traces, reporting a stale-version attack experiment in which governed selection dominates sparse retrieval at roughly a third of the input tokens, plus a determinism experiment favouring exact selectors. Preprint evidence, used for version identity, reconstruction, and the stale-version result — its URI scheme and storage model are not adopted, and no established-standard status is implied.
Derived context inherits its sources
The most dangerous staleness wears no version at all. A generated summary reading “All migration tests pass” can look timeless while the test run it derived from targeted commit C1 and the code now stands at C2. Raw staleness at least retains source, version, and capture time; derived staleness can erase all three. Hence the continuity principle across Chapters 11, 15, and 18:
Derived context should retain the source versions that licensed it when those dependencies can change.
Dependency-aware invalidation follows: source S at v1 licences observation O, summary M, and plan P in a chain, and S moving to v2 suspends the assumption behind all three — not proven false, no longer assumable, each requiring reconsideration. Lineage tells the system what must be reconsidered; it never proves the derived item correct, and traceability is not truth. Change-triggered paths short-circuit the wait where they exist — filesystem events, commits, database updates, configuration generations, webhooks retiring dependent context at the moment of change rather than at the next TTL boundary — but notification is itself fallible machinery. A missed commit hook leaves last week’s configuration admitted, which is the fast-change failure wearing event-driven clothes, and missed, duplicated, delayed, or mis-grained events improve responsiveness without removing the need for validators. The experiment’s second fixture exists to measure exactly this propagation, testing invalidation machinery rather than summarisation quality.
The worked case that decides admissions is fresh observation against cached interpretation: current tool output says the working tree is clean while an older agent summary insists three files remain modified. Chapter 19 may already grant the tool output stronger evidential standing for working-tree state; this chapter adds that the summary’s source version is spent. The system invalidates or downselects the stale derived state before assembly rather than placing both in context and hoping the model arbitrates. Revalidation gates admission throughout — candidate, freshness requirement, validate if required, then admit, refresh, reject, or unknown — but no universal checkpoint is built. Whether an item needs revalidation depends on volatility, operation, staleness cost, and validation cost, and no scalar freshness score collapses those inputs.
Much needs no checking at all. Frozen run artifacts, commit contents, specification versions, and historical incident records require no repeated source validation when the task targets exactly that version — a frozen artifact cannot go stale for a historical query about itself. Currentness is query-relative: what backend did the project use in April wants the April state, and the April observation is not stale for that question simply because PostgreSQL is current now; what backend to configure now wants current state, and the same observation is inappropriate there. The same bytes change status with the question asked — admissible evidence for the historian, inadmissible premise for the deployer — which is why no admission policy may score currentness without reading the task’s temporal standpoint first. Tasks may target a commit, a release, or a date outright, in which case the correct version is the named one rather than latest. Operation decides: explain the frozen commit and the commit is ideal; edit the working tree and current state is required; compare releases and both historical versions enter; deploy the build and strong current validation is mandatory. One global TTL expresses none of this, and no policy language is invented beyond what the experiment needs to show it.
Stale material is not therefore useless material. Debugging a transition wants old state, new state, and the delta between them; stale-for-assertion differs from globally useless, which is one more reason to preserve version, time, and role rather than deleting old observations. Policy stays task-relative throughout. And where old and new versions coexist — C1 says SQLite, C2 says PostgreSQL — version identity converts apparent contradiction into state evolution, the conceptual payoff Chapter 19’s frozen-time disagreements were waiting for, without rebuilding any trajectory model.
Proposed experiments
The questions. Does source-specific validation beat time-to-live and always-reread on staleness and cost, and does lineage stop a derived summary outliving its source?
The design, in brief. A deterministic versioned project fixture, observed at one repository version, with four controlled transitions: a relevant fast change inside the time-to-live, an irrelevant repository change while the relevant file stays byte-identical, a stable source beyond the time-to-live, and a historical query about the old version. The placements are deliberate: they let a time-to-live fail stale in one case and busy in another. The first experiment compares indefinite reuse, newest-observed, one time-to-live, coarse repository-version invalidation, source-specific validators, always-reread as a maximal-cost baseline, and an oracle refresh. The second persists a summary at one version, changes the source, and compares no lineage, timestamp-only state, source-version lineage, lineage plus regeneration, always-regenerate, and an oracle.
The measurement that matters. Stale admissions, missed and unnecessary refreshes, validation calls, full rereads and reintroduced tokens; for derived state, stale-derived admissions and the steps until the old state stops influencing behaviour. There is no combined freshness score.
What would change the book. If always-reread is cheap and equal, or time-to-live matches validators under realistic volatility, or readers resolve old-and-new pairs unaided, the machinery is deleted. Nothing here has been run.
What the compiler will be given
Freshness is a judgement relative to an operation, and the compiler receives it as one: a yes or no per candidate, with a reason, for this request. It does not revalidate anything. It cannot tell that an observation was captured at an old revision, and it cannot reread a source. If a builder hands it a stale observation marked fresh, the compiler admits the observation and records that it passed the gate. What it does guarantee is the other half: a candidate marked stale is removed however cheap and relevant it looks. In the compiler’s own cases the stale form was both cheaper and more relevant than the fresh one, and the fresh one was admitted.
The machinery this chapter describes, observed-at and source version, validators, invalidation, lineage for derived items, is therefore candidate-building work. It has been argued here and not tested. Nothing in the three repositories derives a freshness judgement from a source version. The observer can record capture times and, later, source identity, but the corpus of real sessions is empty, so nothing is known about how often real observations go stale, which tool outputs are most volatile, or how much context could revalidate cheaply.
Cheap context can still be wrong context: a perfectly cache-reusable prefix may carry stale project state, and semantic reuse and compute reuse are different reasons to keep old material that must never be conflated — the Chapter 9 callback that closes the loop. External artifacts add version to Chapter 13’s resolvability: the reference resolves, but to which version. Memory keeps history while Context selects standpoint: the past correctly preserved against the present correctly required. Fresh tools revalidate cheapest where volatility lives, without extending Chapter 17 by a line. Authority sets who may speak; freshness sets whether the speech still describes its subject. And every current answer raises the question this chapter ends on: current for which world — this project with PostgreSQL against the other with SQLite, both fresh, both authoritative, both correct, only one of them belonging here.
References
- Fielding, R., et al. “RFC 9110 — HTTP Semantics” and “RFC 9111 — HTTP Caching.” Internet Standards, June 2022. Validators with strong/weak semantics and conditional requests; freshness lifetime with age, stale responses, and validation. Used strictly as the age-policy versus source-validation pattern. https://www.rfc-editor.org/info/rfc9110/ https://www.rfc-editor.org/info/rfc9111/
- Git Contributors. “Git Internals — Git Objects.” First-party data-model documentation, current pages, verified September 2026. Commits identifying trees plus history, trees identifying directory state, blobs identifying file contents; objects immutable once created. Used for the coarse-versus-fine invalidation case only. https://git-scm.com/book/en/v2/Git-Internals-Git-Objects
- Ouyang, J., Pan, T., Cheng, M., et al. “HoH: A Dynamic Benchmark for Evaluating the Impact of Outdated Information on Retrieval-Augmented Generation.” Peer-reviewed, ACL 2025 long paper (Vienna, pp. 6036–6063). Outdated retrieved material degrading accuracy by distraction and misleading models despite current information present. Phenomenon only; no effect transferred. https://aclanthology.org/2025.acl-long.301/
- Cao, J., Ouyang, J., Cheng, M., et al. “Re³: Relevance & Recency Retrieval for Mitigating Temporal Hallucination.” Peer-reviewed, ACL 2026 long paper (San Diego, pp. 25735–25760). Time-aware relevance with recency arbitration among obsolete versions; 1.3M-instance temporal benchmark. Used for relevance-is-not-currency only; architecture not adopted. https://aclanthology.org/2026.acl-long.1180/
- Sulpovar, M., et al. “ContextNest: Verifiable Context Governance for Autonomous AI Agent.” Preprint, arXiv:2607.02116 v2, July 2026. Version identity with hash-chained histories and point-in-time reconstruction; stale-version attack experiment. Preprint evidence; scheme and storage not adopted. https://arxiv.org/abs/2607.02116
- Memory book (sibling manuscript, unpublished; its source repository
ernanhughes/memoryis private, so it cannot be consulted). Conceptual background only, not evidence for this book: Ch8 valid/record/decision/effective time with a bitemporal minimum model, append-only correction, and the historical-versus-current query standpoint. Temporal machinery stays upstream. No quantitative result is consumed.