# Advanced Agents From First Principles 36: Is Your Agent Acting on Stale State? Build Temporal Consistency, Freshness Budgets and Conflict Detection # Book: Advanced Agents From First Principles # Chapter URL: https://programmer.ie/books/advanced-agents-from-first-principles/36-chapter/ # Prose fingerprint (sha256, normalized): c33d91f0b6391296d26add3af71c772d352823a38b9265cbc8fd91cc40e555ca # Snapshot generated at build time by Hugo. This is the complete rendered prose # of the chapter; it is not a transcript of the source file. A preserved state is not necessarily a valid state. That is the next problem. Step 35 gave us portable execution state. We can checkpoint an agent run. We can move it between workers. We can preserve: the behavioral release, observations, search state, verifier evidence, budgets, side-effect lineage, ownership, authority context. That solves continuity. It does not solve time. A run can resume perfectly from an old checkpoint and still be wrong because the world changed while it was paused. The repository changed. The pull request was merged. The production deployment advanced. The browser session expired. The page contents changed. The approval expired. The queue drained. The dependency recovered. The price changed. The schema migrated. A human edited the document. Another agent committed first. The verifier was upgraded. The agent’s checkpoint may be internally consistent and externally obsolete. That is a temporal-consistency problem. The core rule for this post is: Before a consequential action, validate that every state assumption the action depends on is still fresh enough for that action. Not every observation needs to be live. Not every stale value matters. But the system must know which assumptions are time-sensitive, which versions they were derived from, what changed, and whether that change invalidates the next action. The Search Problem: “AI Agent Acting on Stale Data” A common agent implementation works approximately like this: observe ↓ plan ↓ think for a while ↓ act That picture hides a dangerous assumption: state at observation time == state at action time For short read-only tasks, that may be acceptable. For long-running agents, distributed workers, browser automation, coding systems, DevOps agents, financial workflows, database operations, and any task that crosses an approval boundary, it is often false. Imagine a coding agent. At 13:00 it reads: main = commit A It plans a patch. At 13:04 another developer merges commit B. At 13:07 the agent applies its patch assuming A is still the base. Nothing about the patch itself has to be irrational. The failure is temporal. The agent reasoned from a state that was no longer authoritative. Or imagine a browser agent. At 10:10 it observes: cart total = €240 shipping = €0 recipient = Alice At 10:12 the website recalculates shipping and the total becomes €279. At 10:14 a human approves the purchase based on the earlier summary. At 10:15 the agent clicks Buy. If the system does not revalidate the state bound to that approval, it can execute a materially different transaction from the one that was approved. This is not a language-model problem. It is a state-validity problem. 1. Freshness Is Not One Number Teams often model freshness as: age = now - observed_at Then they choose a TTL. That is useful for caches. It is not enough for an agent. Two observations can both be ten minutes old and have completely different safety implications. README text observed 10 minutes ago may still be perfectly adequate for answering a historical documentation question. But: current branch head observed 10 minutes ago may be unusable for committing code. Likewise: weather forecast observed 10 minutes ago may be fine for casual planning. But: authorization token observed 10 minutes ago may already be invalid. Freshness therefore needs at least three dimensions: age change sensitivity consequence of being stale A useful representation is: from dataclasses import dataclass from enum import Enum class FreshnessClass(str, Enum): IMMUTABLE = "immutable" SNAPSHOT = "snapshot" BOUNDED = "bounded" EVENT_SENSITIVE = "event_sensitive" MUST_BE_CURRENT = "must_be_current" @dataclass(frozen=True) class StateObservation: state_key: str value_hash: str observed_at: str source_version: str | None freshness_class: FreshnessClass max_age_seconds: int | None authority: str The point is not the enum names. The point is to force the system to say what kind of freshness guarantee a piece of state requires. 2. Separate Observation Time From Decision Time From Commit Time An advanced agent usually has several temporal boundaries: observation time ↓ planning time ↓ selection time ↓ approval time ↓ commit time ↓ postcondition verification time These are not interchangeable. Suppose the agent observes a repository at version A. It produces candidate C against A. A verifier confirms C against A. A reviewer approves C against A. Then main moves to B. At commit time, the system must not reason: candidate C passed verification therefore commit C The stronger statement is: candidate C passed verification against repository version A If the current mutation target is now B, the verification claim may no longer apply. This gives us a general rule: Evidence is valid only relative to the state identity against which it was produced. 3. Give Authoritative State an Identity If a state can affect correctness, the runtime should avoid representing it only as mutable prose. Prefer explicit identities. For a repository: repo_head_sha workspace_tree_hash lockfile_hash configuration_hash For a database: row_version transaction_snapshot schema_version logical_sequence_number For a browser workflow: page_version or response ETag session_id cart_version form_state_hash server-side transaction token For an external API: resource_version ETag updated_at operation status version For agent policy: authority_policy_version placement_policy_version verifier_bundle_version behavioral_release_id The runtime can then bind decisions to state explicitly: @dataclass(frozen=True) class StateDependency: state_key: str observed_version: str required_freshness: FreshnessClass role: str A plan is no longer merely: "update the deployment" It becomes: update deployment assuming: deployment_version = 42 policy_version = auth-v7 manifest_hash = abc123 verifier_bundle = verify-v12 That is much easier to validate before mutation. 4. State Version Vectors Long-running tasks rarely depend on one mutable object. A coding run may depend on: repository head package lockfile CI configuration issue state PR state review state policy version A DevOps run may depend on: deployment revision service health traffic split config version secret version schema version region health Representing these as one opaque “context version” loses useful structure. Instead, maintain a state-version vector: @dataclass(frozen=True) class StateVersionVector: versions: dict[str, str] Example: { "repo:main": "a81d9f...", "issue:417": "rev-8", "policy:authority": "v7", "verifier:code": "v12", "tool:github-schema": "v4" } Before a consequential action, read the current vector for the required keys. Then compare: observed vector vs current vector But do not automatically reject every difference. A changed issue comment may be irrelevant to the exact patch. A changed repository head may be critical. That leads to dependency-aware conflict detection. 5. Not Every Change Is a Conflict This is where naive optimistic concurrency becomes too blunt. Suppose an agent edits: src/payments/retry.py Meanwhile another developer changes: docs/README.md The repository head changed. But the change may not invalidate the agent’s patch. Conversely, another developer could change: src/payments/base.py without touching the same file, yet still invalidate assumptions about the retry implementation. So conflict detection should operate at multiple levels. Level 1: Exact identity conflict The object itself changed. same row same file same form same cart same deployment object Level 2: Declared dependency conflict A dependency used by the plan changed. imported module schema configuration API contract policy verifier Level 3: Semantic conflict The bytes may differ in a way that changes the meaning of the pending action. This is harder. Use deterministic dependency information wherever available before asking a model. For coding agents: imports call graph symbol references build graph test dependencies For databases: foreign keys schema dependencies transaction predicates For deployment systems: service dependency graph config dependencies traffic policy For browser workflows: server-issued version tokens cart IDs transaction IDs form hidden fields The important idea is: A changed world does not automatically require replanning. A changed dependency might. 6. Use Optimistic Concurrency at Mutation Boundaries One of the strongest deterministic protections is compare-and-set. Instead of: update_resource(new_value) prefer: update_resource( new_value, expected_version=observed_version, ) The mutation succeeds only if the resource is still the version the agent reasoned about. Equivalent mechanisms include: SQL row versions, If-Match with ETags, compare-and-swap, Git commit ancestry checks, Kubernetes resourceVersion, conditional object-store writes, fenced transaction epochs. This creates a crucial boundary: model believes state is current ≠ mutation gateway proves state is current The gateway wins. 7. Freshness Budgets Step 16 introduced compute budgets. Step 27 introduced reliability budgets. Long-running agents also need freshness budgets. A freshness budget answers: How stale may this state become before the next action must refresh or revalidate it? Example: @dataclass(frozen=True) class FreshnessRequirement: state_key: str max_age_seconds: int | None require_same_version: bool refresh_before_authorization: bool refresh_before_commit: bool For a coding task: repository head require same version before applying patch issue description refresh before final response if mutable static source snapshot snapshot semantics acceptable during analysis For a purchase workflow: product description bounded freshness price refresh before approval recipient exact identity before commit total amount exact identity before commit The freshness budget should be tied to consequence. A read-only summary can tolerate more staleness than a production mutation. 8. Action-Specific Freshness This is a subtle but important refinement. Freshness is not just a property of state. It is a relationship between state and action. The same observation may be fresh enough for one action and stale for another. Example: repository snapshot from 20 minutes ago may be adequate for: explain architecture but inadequate for: commit patch to main Therefore the decision should look like: is_fresh_enough( observation=repo_snapshot, action="commit_patch", risk_class="production_mutation", ) not merely: is_fresh(repo_snapshot) 9. Snapshot Consistency vs Live Consistency There are two legitimate strategies for long-running reasoning. Strategy A: Stable snapshot Freeze the relevant world state. snapshot A ↓ reason entirely against A ↓ produce candidate for A This is ideal for: code analysis in a worktree, offline data processing, deterministic benchmark runs, document analysis, reproducible research. Strategy B: Live consistency Continuously or periodically refresh external state. observe ↓ reason ↓ refresh ↓ update plan ↓ act This is needed for: active browser sessions, deployment control, market or inventory state, dynamic queues, collaborative documents, multi-actor systems. The mistake is to accidentally mix the two. A system that claims snapshot semantics but silently performs live reads creates an incoherent state model. A system that claims live semantics but never refreshes is simply stale. Make the consistency mode explicit. 10. Revalidation Is Not Always Replanning When state changes, the runtime has several choices. state unchanged → continue state changed but irrelevant → continue with updated evidence state changed but candidate still valid → reverify state changed and assumptions invalid → replan state changed and safe continuation impossible → abort / escalate This gives us useful outcomes: FRESH REFRESHED REVERIFY_REQUIRED REPLAN_REQUIRED STALE_STATE CONFLICT ABORT_REQUIRED Do not collapse all changes into “start over.” But also do not preserve a plan merely because recomputing it is expensive. 11. Bind Plans to Preconditions A plan should carry explicit preconditions. @dataclass(frozen=True) class ActionPrecondition: state_key: str expected_version: str | None predicate: str on_failure: str Example: Action: merge PR Preconditions: - PR head == 94d6... - required checks == PASS - approval count >= 1 - approval artifact hash == current diff hash - base branch == expected base - authority token not expired At commit time, these conditions are evaluated against authoritative state. That is much stronger than asking the agent: “Are you sure nothing changed?” 12. Revalidate Human Approval Step 29 made human approval a bound authorization artifact. Temporal consistency adds another rule: Approval is valid only while the approved object and relevant context remain within the approval’s validity envelope. Suppose a reviewer approves: patch_hash = abc123 Then the agent modifies the patch. The approval no longer applies. Suppose the approved amount was: €240 and the final charge is: €279 The approval no longer applies. Suppose the deployment target changes from staging to production. The approval no longer applies. A good approval artifact therefore contains: approved artifact identity scope authority class relevant state versions expiry reviewer identity policy version Before commit: approval artifact ↓ revalidate target state ↓ match? ├─ yes → continue └─ no → approval invalidated 13. Repository Drift Coding agents make temporal consistency easy to visualize. A robust coding workflow might look like: checkout base SHA A ↓ create isolated worktree ↓ edit against A ↓ run tests against candidate C/A ↓ verify ↓ fetch current target branch B ↓ compare A → B ↓ rebase/reapply if safe ↓ rerun impacted verification ↓ conditional push / PR update The important point is that: tests passed on A does not imply: tests would pass on B A branch update is a new state transition. If the patch is rebased, the candidate artifact identity changes. Therefore previous verifier evidence may need to be invalidated. 14. Detect Semantic Repository Conflicts Textual merge success is not enough. Consider: agent edits retry.py human edits timeout.py Git may merge cleanly. But if retry.py assumes a timeout constant that changed, the patch may now be wrong. Use repository intelligence when available: changed symbols ↓ reverse dependencies ↓ affected tests ↓ required revalidation This is where structural graphs become operationally valuable. The agent does not need to reread the entire repository every time. It needs to determine whether the changed state intersects the dependency cone of the pending action. 15. Browser State Is Especially Fragile Browser agents often reason over ephemeral state: DOM elements, hidden form fields, CSRF tokens, carts, session cookies, dynamic prices, availability, seat selections, transaction IDs. A screenshot from five minutes ago is not a transaction precondition. A good browser action should bind to server-observable identity when possible. Example: observed cart_id = 731 observed total = 24000 cents observed currency = EUR observed recipient_id = user-17 Before purchase: GET authoritative cart state ↓ compare exact purchase-critical fields ↓ if mismatch → reapproval or abort This is stronger than asking the model whether the page “looks the same.” 16. External APIs Need Conditional Mutation If an API exposes resource versions or ETags, use them. Example: GET /resource/42 ETag: "v17" Then: PUT /resource/42 If-Match: "v17" If the current resource is now v18, the mutation fails safely. The agent then receives: CONFLICT and can refresh/replan. This pattern turns a race condition into an explicit control-flow branch. That is exactly what we want. 17. Database Agents and Predicate Drift Database work introduces a harder class of staleness. A transaction may depend not only on exact rows but on a predicate. Example: SELECT * FROM jobs WHERE status = 'READY' AND priority > 80; The agent chooses a set of jobs. Before mutation, new rows may appear that also satisfy the predicate. Whether that matters depends on task semantics. This is the classic distinction between: object identity and: predicate identity For consequential database agents, use native transactional guarantees when possible rather than rebuilding isolation in prompt logic. The model should not simulate serializable transactions in natural language. 18. Policy Drift The world that can become stale includes internal control policy. Suppose a run starts under: authority_policy = v12 During the run, an incident causes the platform to publish: authority_policy = v13 with lower autonomous mutation limits. A long-running run must not continue under the old policy merely because it started earlier. This requires distinguishing state categories: run-pinned state from: must-use-current control state Behavioral release may be pinned for reproducibility. Security and authority policy may require current-state enforcement. Do not assume all versions should be frozen together. 19. Verifier Drift Step 23 already established that verifier behavior can drift. Temporal consistency adds a runtime question: was this candidate verified under a verifier that is still accepted for this authority class? Suppose candidate C passed verifier bundle V7. Then V7 is withdrawn after a false-PASS incident. The run must not retain the old PASS as indefinitely valid evidence. The platform may require: reverify C under V8 before consequential action. This means verifier evidence has both: artifact binding and: policy validity 20. Freshness and Competence Step 30 introduced competence envelopes. A competence claim is also conditional on state regime. An agent may be validated for: static repository maintenance but not for: high-churn collaborative repository mutation The difference is not necessarily model intelligence. It is operational context. Therefore the task descriptor used for competence checks can include: expected state churn number of concurrent actors required consistency level availability of versioned state reversibility A highly dynamic environment may push a task outside the validated competence envelope even if the domain itself is familiar. 21. Freshness and Authority Authority should tighten as freshness guarantees weaken. Example: fresh authoritative state + strong verifier → autonomous reversible mutation may be allowed bounded-stale state + good verifier → proposal only unknown state age + weak verifier → human review / defer known conflicting state → no mutation This is not because stale information is always wrong. It is because the platform has less evidence that the pending action still refers to the world that currently exists. 22. Freshness and Expected Value of Information Step 18 introduced Expected Value of Information. Refreshing state is an information purchase. The agent should not refetch everything continuously. That can be expensive and can itself create load. Instead ask: Which state refresh is most likely to change my next decision or invalidate my next action? For a coding agent about to commit: refresh repo head may have high value. Refreshing an unrelated documentation page may have almost none. For a browser agent about to purchase: refresh final total and recipient has very high value. Refreshing product marketing text does not. This produces targeted revalidation rather than indiscriminate polling. 23. Change Notifications Beat Polling When Available Some systems expose events: repository webhook resource watch change-data capture queue event calendar update filesystem watcher policy publication event These can invalidate assumptions immediately. Architecture: state observation ↓ register dependency ↓ subscribe to relevant change stream ↓ change event arrives ↓ mark dependent plan/candidate evidence stale But event delivery is not always perfect. So event invalidation usually complements, rather than completely replaces, commit-time version checks. 24. Invalidation Graphs Step 33 introduced capability dependency graphs. Temporal consistency benefits from a smaller runtime graph: observation ↓ derived fact ↓ plan step ↓ candidate ↓ verifier evidence ↓ approval ↓ mutation If an observation changes, traverse downstream dependencies. Example: repo_head changed ↓ workspace base invalidated ↓ candidate compatibility uncertain ↓ verifier evidence stale ↓ approval stale The graph lets the runtime invalidate only what actually depended on the changed state. This avoids both extremes: ignore all changes and: restart entire run on every change 25. Do Not Let the Model Decide What Is Authoritative This principle recurs throughout the series because it matters. A model can suggest: "I think the repository probably hasn't changed." That is not a state check. A model can say: "The price looked the same in the screenshot." That is not transaction validation. A model can say: "The approval should still be valid." That is not authorization. Whenever the runtime has access to authoritative version/state mechanisms, use them deterministically. The model may interpret semantic conflicts after exact facts are gathered. It should not replace the facts. 26. A Temporal Consistency Gate A minimal deterministic gate might look like this: from dataclasses import dataclass from enum import Enum class TemporalDecision(str, Enum): CONTINUE = "continue" REFRESH = "refresh" REVERIFY = "reverify" REPLAN = "replan" CONFLICT = "conflict" ABORT = "abort" @dataclass(frozen=True) class RequiredState: key: str observed_version: str | None current_version: str | None require_same_version: bool materially_changed: bool critical: bool def evaluate_temporal_state(states: list[RequiredState]) -> TemporalDecision: for state in states: if state.critical and state.current_version is None: return TemporalDecision.ABORT if state.require_same_version: if state.observed_version != state.current_version: return TemporalDecision.CONFLICT if state.materially_changed: return TemporalDecision.REPLAN return TemporalDecision.CONTINUE Production logic will be richer. But notice what this function does not contain: LLM confidence Exact version conflicts do not need probabilistic reasoning. 27. Revalidation Before Side Effects The most important temporal checkpoint is usually immediately before irreversible or externally visible mutation. A safe pattern is: prepare candidate ↓ verify candidate ↓ obtain authorization if needed ↓ refresh mutation-critical state ↓ check versions/preconditions ↓ reverify if state changed ↓ commit conditionally ↓ verify postcondition This is deliberately repetitive. The system is spending extra work exactly where stale assumptions are most expensive. 28. Postcondition Verification Detects Races You Did Not Predict Even strong preconditions do not eliminate all ambiguity. External systems can fail strangely. The request can time out after being applied. Another process can race after your check. An API can violate assumptions. Therefore: precondition check ≠ proof of final state After mutation, query authoritative state again. Example: intended deployment revision = R42 ↓ commit ↓ read deployment state ↓ actual revision == R42 ? If not: UNKNOWN CONFLICT PARTIAL_EFFECT may be more accurate than PASS. 29. Staleness Can Propagate Through Memory Memory systems make temporal consistency harder. Suppose episodic memory stores: "Service A uses endpoint X" Six months later, endpoint X is retired. The memory may still be semantically coherent and factually stale. Memory therefore needs provenance and temporal scope: @dataclass(frozen=True) class MemoryFact: fact: str source_id: str observed_at: str source_version: str | None valid_until: str | None refresh_policy: str Do not treat memory as timeless truth. Memory is evidence with lineage. 30. Retrieval Results Need Freshness Semantics Too A retrieval index is a snapshot of some corpus. If the corpus changes, retrieval can become stale even when the index is healthy. Track: corpus snapshot index version retriever version source update time selected chunk hash A research agent answering: What was the policy in 2025? may deliberately use an old snapshot. The same snapshot is inappropriate for: What is the current policy? Freshness depends on the query’s temporal semantics. 31. Long-Running Research Agents Need Claim Freshness A research agent may gather evidence over hours or days. Some claims are stable: paper publication date historical event mathematical definition Others are volatile: current CEO latest software version current price breaking news live election results Attach freshness requirements to claims. Before finalizing a report, refresh only volatile claims whose age exceeds their allowed budget. This is much more efficient than rerunning the entire research pipeline. 32. Temporal Conflicts in Multi-Agent Systems Multi-agent systems create another problem: Agent A observes state S0 Agent B observes state S0 Agent A acts → state S1 Agent B still plans against S0 Without versioned state, Agent B may overwrite or contradict Agent A. The solution is not necessarily for agents to debate. It is often ordinary concurrency control: shared state versions leases conditional writes fencing idempotency transaction boundaries The agents can coordinate semantically after the infrastructure prevents stale mutation. 33. Conflict Resolution Must Not Invent Merge Semantics When a conflict occurs, the system may be tempted to ask the model: "Please merge these two states." That is sometimes valid for text. It is dangerous for arbitrary state. Different systems have different merge semantics. source code calendar events financial transactions configuration access-control lists inventory orders may require completely different rules. Use domain-native merge/reconciliation mechanisms when available. Treat model-assisted semantic merge as a candidate transformation that still requires deterministic validation and verification. 34. Freshness Debt Long-running systems can accumulate freshness debt. Imagine a run with 30 observations. Ten have expired freshness budgets. Five have unknown source versions. Three depend on a withdrawn verifier. Two approvals expired. At that point, incremental refreshing may be more expensive and less trustworthy than rebuilding the plan from a new snapshot. Define a threshold where: refresh debt too high ↓ REPLAN_REQUIRED This resembles technical debt in miniature: continuing from too many stale assumptions creates increasing reconciliation cost. 35. Replanning Must Preserve Useful Work Carefully REPLAN_REQUIRED does not mean discard everything. Some artifacts remain valid: immutable source evidence, historical observations, failed candidate evidence, verified invariant checks, search branches unrelated to changed state. Other artifacts must be invalidated: candidate patches bound to old state, approvals for old artifact hashes, verifier PASS tied to invalid candidates, tool plans using obsolete schemas. This again argues for explicit provenance edges. Without lineage, the runtime cannot know what stale state contaminated. 36. Time Is Part of the Task Specification A surprisingly large class of agent failures comes from underspecified temporal intent. Compare: Who is the CEO? with: Who was the CEO in January 2024? Compare: What dependencies does this repository use? with: What dependencies did commit A use? The first requires current/live semantics. The second requires historical snapshot semantics. The runtime should preserve that distinction explicitly. A task descriptor can include: @dataclass(frozen=True) class TemporalIntent: mode: str # current | historical | snapshot | bounded_stale as_of: str | None max_staleness_seconds: int | None This prevents the agent from silently answering a current-state question with historical evidence. 37. Freshness Must Survive Handoff Step 35 made execution state portable. A checkpoint should therefore preserve: observed_at source version freshness class freshness budget invalidation state When the target worker resumes: checkpoint age ↓ re-evaluate freshness requirements ↓ refresh/reverify/replan as needed Migration time itself consumes freshness budget. That sounds obvious, but it is easy to miss when checkpoint recovery is focused only on serialization correctness. 38. Do Not Reset Freshness on Deserialization A particularly bad implementation would do this: observation captured at 13:00 checkpoint resumed at 14:00 runtime reconstructs object at 14:00 and then treats the object as: observed_at = 14:00 That launders stale evidence into fresh evidence. Preserve original observation time. A restored object is not a newly observed object. 39. Timeouts Are Not Freshness Proofs A timeout can tell you that something is too old according to policy. It cannot tell you whether something actually changed. Likewise, a recent observation can still be obsolete if a change event happened immediately afterward. Therefore combine: age-based invalidation version-based invalidation event-based invalidation precondition checks according to the available system guarantees. 40. Clock Assumptions Matter Distributed systems have clock skew. For most agent freshness decisions, wall-clock timestamps are useful but should not be the only consistency primitive. Prefer authoritative versions and monotonic sequence values where available. Use timestamps for: age expiry observability human understanding Use versions/epochs for: identity ordering fencing conditional mutation This prevents subtle bugs where two machines disagree slightly about time. 41. Freshness of Secrets and Credentials Credentials are state too. A checkpoint may refer to a capability token that has since expired or been revoked. Never serialize long-lived authority merely to make resume easier. On resume: checkpoint authority requirement ↓ current policy ↓ current worker identity ↓ issue fresh scoped capability if allowed If current authority cannot be reconstructed: AUTHORITY_UNAVAILABLE is safer than reusing stale credentials. 42. Temporal Consistency for Caches Caching can create enormous efficiency gains. It can also preserve obsolete truth. A cache key should include the identity of mutable inputs that affect correctness. Bad: cache_key = hash(prompt) Better: cache_key = hash( prompt, model_version, repository_snapshot, policy_version, tool_schema_version, ) For live-data questions, the cache also needs a freshness policy. A model response about immutable source code at commit A can be reused indefinitely for A if all other inputs match. A model response about current production health cannot. 43. Freshness-Aware Prompt Caching This is especially useful for local-model systems. Repeated analysis against the same immutable file or repository snapshot should be reusable. But the cache should express why reuse is valid: same exact content hash same question/prompt version same model version same relevant context Then cache reuse is evidence-preserving. Do not invalidate immutable work merely because wall-clock time passed. Likewise, do not retain mutable-state answers merely because their prompt text is identical. 44. Temporal Consistency and Cost Revalidation costs money and time. So there is a genuine trade-off: refresh too often → high latency and cost refresh too rarely → stale-state failures That makes freshness policy another optimization surface. But, as throughout this series: hard correctness constraints first optimization second For high-consequence mutation-critical state, exact precondition checking should not be traded away to save a few milliseconds. For low-risk read-only context, bounded staleness may be entirely rational. 45. Measure Stale-State Failures Separately Do not hide temporal failures inside generic “agent failed” metrics. Track at least: stale observation rate commit-time conflict rate revalidation rate replan rate stale approval invalidation rate reverification rate state-version mismatch rate stale cache hit prevention conflict rescue rate false stale alarm rate freshness-related false PASS rate This lets you answer: Are we refreshing too much? Are we refreshing too little? Which state classes cause incidents? Which tools lack useful version semantics? 46. Add Temporal Events to the Trajectory Step 13 established trajectory observability. Add events such as: state_observed freshness_budget_assigned state_invalidated state_refreshed version_conflict_detected reverification_requested replan_requested approval_invalidated conditional_commit_rejected postcondition_conflict Each event should carry: run_id state_key old_version new_version reason affected artifact IDs action taken policy version This makes temporal failures visible in incident forensics. 47. Connect Temporal Consistency to Incident Forensics Step 26 asked: Where did the run first diverge? Temporal provenance lets us answer a stronger question: At what point did an assumption become stale, and what downstream decisions continued to rely on it? Example: 13:00 repo A observed 13:02 plan created 13:04 repo changed to B 13:05 invalidation event missed 13:07 candidate verified against A 13:10 commit attempted against B The root cause may be: missing invalidation / precondition enforcement not: bad model generation 48. Failure Injection for Temporal Consistency Temporal logic is exactly the kind of thing that looks correct until concurrency happens. Test it deliberately. Repository move target branch after planning, alter imported symbol without touching edited file, change lockfile after candidate generation, invalidate approval by changing diff, merge competing PR before commit. Browser price changes after approval, session expires, selected item becomes unavailable, recipient changes, CSRF token rotates, page refresh changes hidden transaction identifier. DevOps deployment revision changes during diagnosis, policy tightens during run, region health flips, verifier bundle is withdrawn, secret version rotates. Distributed workers old worker resumes after handoff, checkpoint waits long enough to exceed freshness budget, change event is dropped, clocks disagree, target worker receives stale cache entry. The expected result should be deterministic where possible: CONFLICT REVERIFY_REQUIRED REPLAN_REQUIRED AUTHORITY_INVALIDATED STALE_STATE not silent continuation. 49. Benchmark the Freshness Policy Compare at least: no revalidation fixed global TTL refresh everything before action dependency-aware freshness policy Measure: verified success false success stale-state incidents latency external calls cost replans false conflict rate human escalations A sophisticated temporal-consistency system must earn its complexity. For some workloads, a simple immutable snapshot plus commit-time compare-and-set may beat an elaborate live invalidation graph. Use the simplest mechanism that satisfies the actual consistency requirement. 50. A Practical Temporal State Model A production-oriented DTO might look like: from dataclasses import dataclass from typing import Literal @dataclass(frozen=True) class TemporalStateRef: state_key: str source_uri: str observed_at: str observed_version: str | None value_hash: str | None freshness_mode: Literal[ "immutable", "snapshot", "bounded", "event_sensitive", "must_be_current", ] max_age_seconds: int | None authority: str @dataclass(frozen=True) class TemporalDependency: consumer_artifact_id: str state_key: str dependency_kind: Literal[ "input", "precondition", "verification", "authorization", "commit", ] on_change: Literal[ "ignore", "refresh", "reverify", "replan", "abort", ] This makes temporal semantics inspectable rather than hidden in orchestration code. 51. A Commit-Time Consistency Protocol A generic consequential-action protocol can be: 1. identify mutation-critical state dependencies 2. fetch current authoritative versions 3. compare against observed versions 4. classify changes 5. invalidate dependent evidence where necessary 6. refresh/reverify/replan 7. validate current authority 8. perform conditional/fenced mutation 9. read authoritative postcondition 10. record final state versions Notice what is absent: "ask the model if it still seems okay" The model may help at Step 4 for semantic change classification when exact dependency rules are insufficient. But the surrounding control structure remains deterministic. 52. When the Correct Answer Is STALE_STATE Agent systems often treat inability to continue as failure. That creates pressure to improvise. Instead, support explicit outcomes: STALE_STATE CONFLICT REVERIFY_REQUIRED REPLAN_REQUIRED AUTHORITY_INVALIDATED DEPENDENCY_CHANGED These are not embarrassing states. They are evidence that the control plane noticed the world had changed. A system that says: I cannot safely commit because the target state changed is more reliable than one that confidently executes against obsolete assumptions. 53. What This Adds to the Architecture We now have a stronger runtime chain: Task ↓ Competence check ↓ Authority decision ↓ Placement ↓ Observe authoritative state ↓ Build state-version dependencies ↓ Plan / search / execute candidate work ↓ Verify ↓ Freshness + conflict gate ↓ Reverify / replan if needed ↓ Conditional fenced commit ↓ Postcondition verification ↓ Trajectory + provenance update This is much closer to how reliable distributed systems work than the classic agent loop: think → act → observe The loop is still there. But it now operates inside explicit state, authority, consistency, and verification boundaries. 54. The Larger Principle The deeper lesson is not about timestamps. It is about assumption ownership. An agent plan is a bundle of assumptions about the world. Those assumptions may have been correct when the plan was formed. As time passes, the runtime must know: which assumptions can change which did change which downstream artifacts depend on them which evidence is therefore invalid and what must happen before authority is exercised That is temporal consistency for agent systems. The model does not get to declare the world unchanged. The infrastructure proves enough continuity for the next action—or it refuses to proceed. Final Rule If you remember one thing from this post, make it this: Before a consequential action, validate that every state assumption the action depends on is still fresh enough for that action. A preserved checkpoint is not proof of a preserved world. A previous PASS is not proof of current validity. A previous approval is not permission for a changed artifact. A recent observation is not necessarily authoritative. And a fluent agent should never be allowed to turn stale state into fresh authority by assertion. The runtime must make time visible. Only then can a long-running agent know when to continue, refresh, reverify, replan—or stop. Next: Coordinating Long-Running Intent Temporal consistency tells us when the world has changed underneath a run. But long-running systems have another problem. The goal itself can change. A user can change priorities. A ticket can be closed. A deployment can be cancelled. A human can supersede an earlier request. A higher-priority policy can invalidate work that is still technically executable. The next step is therefore not more freshness checking. It is intent supersession and cancellation semantics: Which instruction is currently authoritative? Which older work has been superseded? What should be cancelled? What can be safely reused? Which side effects are already committed? That gives us the next principle: State can become stale, but so can intent.