Is Your Agent Acting on Stale State? Build Temporal Consistency, Freshness Budgets and Conflict Detection
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-Matchwith 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.