# Advanced Agents From First Principles 25: Can You Reproduce an Agent Run Months Later? Add Deterministic Replay and Provenance # Book: Advanced Agents From First Principles # Chapter URL: https://programmer.ie/books/advanced-agents-from-first-principles/25-chapter/ # Prose fingerprint (sha256, normalized): 9fa932dd6d8974df095046c822ad3e58990a1d9df9f2c6e4b0d530c349853339 # 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 production agent fails on Tuesday. You inspect it on Wednesday and cannot reproduce the failure. Three weeks later someone asks a harder question: What exactly did this agent see, which release was running, which tools were called, which evidence was used, and why did the verifier accept the result? If the answer is “we have some logs”, you do not yet have reproducibility. You have fragments. Advanced agents accumulate hidden variability quickly: model versions change, prompts change, retrieval indexes change, tools return different state, schedulers make different decisions, search branches complete in different orders, external APIs mutate, memory evolves, policies are promoted and rolled back, verifiers change, and distributed workers may execute different attempts of the same logical operation. Step 24 treated the full behavioral surface as a release artifact. Now we need to make every important run reconstructable. The core rule is: A run is reproducible only when its behavior can be tied to immutable inputs, versioned policy, external observations, side effects, and verification evidence. That does not mean every model token must be perfectly deterministic. It means the system should preserve enough evidence to distinguish: same release + same recorded observations ↓ logical replay same release + live dependencies ↓ re-execution new release + old recorded observations ↓ counterfactual replay Those are different experiments. Treating them as equivalent is one of the easiest ways to fool yourself while debugging agent systems. 1. Replay Is Not One Thing The word replay is overloaded. A useful runtime distinguishes at least three modes. Exact artifact replay Reconstruct the original run from recorded artifacts without calling mutable external dependencies again. Examples: reuse the exact prompt payload, reuse recorded tool responses, reuse the exact retrieved documents, reuse the exact verifier inputs, replay the recorded decision graph. This is the closest thing to a forensic reconstruction. Logical replay Run the same behavioral release again against controlled or recorded dependencies. The model may still produce a different sampled output, but the environment and policy surface are constrained enough that differences are meaningful. Counterfactual replay Take an old trajectory and ask: What would release B have done if it had received the same evidence release A received? This is crucial for offline policy evaluation, release comparison, routing calibration, and regression diagnosis. The distinction matters because a live rerun against today’s web, today’s repository state, today’s model alias, and today’s retrieval index is not a replay of last month’s incident. It is a new run. 2. The Replay Manifest Every important run should be bound to an immutable replay manifest. A minimal structure might look like this: from dataclasses import dataclass from typing import Mapping, Sequence @dataclass(frozen=True) class ReplayManifest: run_id: str release_id: str task_hash: str environment_snapshot_id: str tool_registry_version: str retrieval_snapshot_id: str memory_snapshot_id: str scheduler_policy_version: str verifier_version: str observation_ids: Sequence[str] side_effect_ids: Sequence[str] artifact_hashes: Mapping[str, str] The point is not the dataclass. The point is that the run is no longer defined by a vague timestamp plus logs. It is defined by a set of references whose content can be checked. 3. Version Names Are Not Enough Suppose the log says: model = gpt-x prompt = repair-v4 That is weak provenance. A model alias may later point to different serving weights. A prompt named repair-v4 may have been edited in place. A tool named repo_search may now use a different implementation. Prefer immutable identity: model_provider model_family provider_model_id serving_revision_if_available prompt_sha256 tool_schema_hash tool_implementation_commit policy_hash verifier_hash retrieval_snapshot_hash If something cannot be immutably identified, record that limitation explicitly. Do not pretend a mutable alias is a stable artifact. 4. Hash the Inputs That Matter A replay system should be able to answer: Are these bytes the same bytes the original run used? For text artifacts, structured payloads, prompts, retrieved documents, configuration, tool responses, and verifier inputs, content hashes are extremely useful. For example: import hashlib import json def canonical_hash(payload: dict) -> str: encoded = json.dumps( payload, sort_keys=True, separators=(",", ":"), ensure_ascii=False, ).encode("utf-8") return hashlib.sha256(encoded).hexdigest() The canonicalization rule must itself be stable and versioned. Otherwise two semantically equivalent dictionaries can hash differently for irrelevant formatting reasons. 5. Record Observations, Not Just Tool Calls This is not enough: tool = git_status status = success You need the observation that changed the agent’s state. For example: { "observation_id": "obs-88", "tool": "git_status", "tool_version": "7f4d...", "input_hash": "...", "output_hash": "...", "observed_at": "2026-08-09T11:00:00Z", "environment_snapshot": "repo-4c91...", "freshness": "live", "authority": "repository", "scope": "worktree:/workspace/a" } Step 17 separated different uncertainty types. Replay requires preserving the evidence used to reduce those uncertainties. 6. Environment State Is Part of the Input For coding agents, the repository state is not background context. It is part of the input. Record at least: repository remote identity, commit SHA, branch/ref, dirty working-tree state if relevant, untracked files if relevant, dependency lockfiles, runtime/container image, environment variables by safe fingerprint, toolchain versions, database migration state when relevant. For browser agents: page URL, response body or snapshot when permitted, page version/fingerprint, authenticated account scope, locale, important cookies/session identity by safe reference, DOM/screenshot artifact IDs. For data agents: dataset snapshot, schema version, query text, database transaction/snapshot identity where possible. For DevOps agents: cluster/account/environment, deployment revision, observed resource versions, change request identity, relevant control-plane state. Without this, “same task” is often not the same experiment. 7. Separate External Observations From Model Inference A model saying: The deployment probably failed because the image tag is stale. is not the same thing as: kubectl observed image tag = api:2026-08-08 expected image tag = api:2026-08-09 Replay artifacts should preserve this distinction. A useful provenance graph might contain nodes such as: TASK OBSERVATION MODEL_OUTPUT POLICY_DECISION TOOL_ACTION SIDE_EFFECT VERIFIER_EVIDENCE OUTCOME with edges such as: DERIVED_FROM OBSERVED_BY SELECTED_BY CAUSED VERIFIED_BY REPLACED CANCELLED That lets you reconstruct not just what happened, but what evidence each decision depended on. 8. Preserve the Decision Graph Step 13 introduced trajectory observability. For replay, keep the lineage. A flat timestamp log loses important structure. Suppose an agent explored three repair branches: root ├── branch A │ └── test fail ├── branch B │ └── verifier PASS └── branch C └── cancelled You should know: which state each branch started from, which observations each branch received, which scorer or policy ranked it, why branch C was cancelled, whether B’s PASS referred to the exact candidate that was later committed. The selected result must remain linked to the exact evidence that justified selection. 9. Replay Must Include Scheduler Decisions Advanced agents do not only generate outputs. They allocate resources. Steps 15 through 18 introduced: routing policy, search policy, escalation policy, stopping policy, budget scheduling, typed uncertainty, Expected Value of Information. Those decisions affect the trajectory. Record them. For example: { "decision_id": "decision-41", "policy_version": "budget-17", "state_hash": "...", "alternatives": [ "run_tests", "generate_candidate", "escalate_model" ], "selected": "run_tests", "expected_cost": 0.04, "expected_value": 0.62, "reason_code": "HIGH_VERIFICATION_UNCERTAINTY" } You do not need hidden chain-of-thought. You need the operational decision boundary. 10. Record Side Effects as First-Class Artifacts A replay system that only records reasoning but not mutations is incomplete. For every consequential side effect, capture: operation_id attempt_id worker_id lease_epoch idempotency_key precondition evidence request fingerprint response fingerprint postcondition evidence commit timestamp Step 20 introduced leases, fencing, retries, and idempotency. Replay should preserve those identities so you can answer: Did the same logical operation execute twice? and: Was the final state produced by the selected worker or a stale worker? 11. External APIs Break Perfect Replay Some dependencies are inherently mutable. Examples: web pages, SaaS APIs, market data, live infrastructure, hosted model endpoints, third-party search engines. You have two choices: record the observation when it happened, call the live dependency again later. Those are different modes. For forensic replay, prefer recorded observations whenever policy, privacy, and storage constraints permit. For live revalidation, call the dependency again but mark the result as new evidence. Do not overwrite the original artifact. 12. Deterministic Replay Does Not Mean Deterministic Models This distinction is crucial. Even with a seed, hosted model execution may not be bit-for-bit reproducible. Serving infrastructure can change. Numerical kernels can change. Parallelism can change. Sampling implementations can change. A good replay system therefore does not promise: same input -> identical tokens forever unless you actually control the full model runtime and have tested that claim. Instead, distinguish: artifact deterministic policy deterministic external observation deterministic model generation nondeterministic Then define what counts as an acceptable replay. For example: same route, same required tools, same invariants, same verifier outcome, same accepted behavioral contract, similar cost envelope. That is often more useful than token identity anyway. 13. Replaying Model Calls For each model call, preserve: provider, model identifier, revision if available, temperature, top-p, seed if used, max output tokens, structured-output schema, tool schema, system prompt hash, user prompt hash, message payload hash, response payload hash, usage metadata, latency, retry/attempt identity. If the exact response body can be stored safely, preserve it as an immutable artifact. Then exact artifact replay can use the stored response without spending another model call. That is also useful for local-model workflows where model calls are expensive and repeated analysis of the same file should not require recomputation. 14. Retrieval Must Be Snapshot-Aware Suppose the original run retrieved: A.md B.md C.md Three months later the index contains different chunks and embeddings. Running the same query against the new index is not a replay. Record: query hash retriever version embedding model version index snapshot id candidate set ranking scores selected chunks source content hashes Then you can ask two distinct questions: Original-evidence replay What would the new agent do with exactly the evidence the old agent saw? New-retrieval replay What evidence would the new retriever obtain for the old task? That decomposition is valuable because it separates retrieval regression from downstream reasoning regression. 15. Memory Needs Provenance Too Long-lived agents often read memories that were written by previous runs. A replay must know: memory item ID, write time, source run, provenance, schema version, content hash, confidence/verification state, promotion policy version, tombstone/supersession state. Otherwise replay can silently inject knowledge that did not exist when the original decision was made. That is temporal leakage. 16. Verify the Exact Candidate One of the most dangerous replay bugs is evidence detachment. Example: candidate A tested -> PASS candidate B selected run recorded -> PASS This is invalid. Verification evidence should bind to an exact candidate identity: candidate_hash -> verifier_run -> evidence_hash -> outcome If the candidate changes, the verification relationship must be invalidated or rerun. 17. Immutable Evidence Beats Editable Logs Logs are operationally useful. But an audit trail should not depend on mutable text files that can be rewritten without detection. Useful options include: append-only event streams, immutable object storage, content-addressed artifacts, hash chains, signed manifests, database rows with immutable payload hashes. The goal is not blockchain theater. The goal is simple: If the evidence changed, we should be able to tell. 18. Tamper-Evident Run Manifests A simple manifest hash can bind all referenced artifacts: @dataclass(frozen=True) class ArtifactRef: kind: str artifact_id: str sha256: str @dataclass(frozen=True) class RunEvidence: run_id: str release_id: str artifacts: tuple[ArtifactRef, ...] previous_manifest_hash: str | None You can then canonicalize and hash the manifest. For stronger audit requirements, sign the manifest with an appropriate key-management process. Again, the useful property is not sophistication. It is detectability of mutation. 19. Replay Should Be Read-Only by Default Never let a forensic replay accidentally resend emails, recreate cloud resources, merge code, modify customer records, or execute payments. Default replay capabilities should be: READ_RECORDED_ARTIFACTS REEXECUTE_PURE_FUNCTIONS RUN_SANDBOXED_TOOLS COMPARE_POLICIES RUN_VERIFIERS not: WRITE_PRODUCTION SEND_EXTERNAL_MESSAGE DELETE_RESOURCE MERGE_CHANGE DEPLOY If side-effect replay is required, route it to an isolated test environment with explicit authorization. 20. A Replay Gateway A useful implementation pattern is to separate tools from their recorded observations. class ReplayGateway: def __init__(self, observations: dict[str, object]): self.observations = observations def call(self, operation_key: str): if operation_key not in self.observations: raise MissingReplayArtifact(operation_key) return self.observations[operation_key] During forensic replay, the agent receives the historical observation. During live execution, the normal gateway calls the real dependency. During counterfactual replay, the candidate release can consume the historical evidence without touching production. 21. Missing Artifacts Must Be Explicit A dangerous replay system silently fills gaps with live data. Suppose the original browser snapshot is missing. Do not quietly fetch the page today and continue. Return something like: REPLAY_INCOMPLETE missing_artifact = browser_snapshot:obs-19 Then the operator can choose a weaker replay mode intentionally. The system should know the difference between: FULL_REPLAY PARTIAL_REPLAY LIVE_REVALIDATION COUNTERFACTUAL_REPLAY 22. Counterfactual Replay for Policy Changes Suppose release A escalated 42% of coding tasks to a frontier model. Release B changes the routing threshold. You can take old trajectories and ask: Under the same observed evidence, where would B route differently? That is useful. But there is an important limit. If B chooses an action A never executed, you may not know the outcome of that action. For example: old policy -> local model -> FAIL new policy -> frontier model -> ? Do not fabricate the counterfactual outcome. Mark it unknown unless you can safely execute the missing branch offline. 23. Replay Search Decisions For search-heavy agents, preserve: node IDs, parent IDs, state hashes, expansion policy version, scorer version, score vectors, prune reasons, cancellation reasons, verifier outcomes, branch cost. Then you can inspect failures such as: FAIL └── selection failure └── winning branch existed └── pruned at depth 3 └── scorer margin 0.02 That is much more actionable than: agent failed 24. Replay Concurrency Carefully Step 19 introduced speculative parallel execution. Concurrency creates another reproducibility problem: branch A finishes first today branch B finishes first tomorrow If selection depends on completion order, the system is schedule-sensitive. Record: launch time, partial-result time, completion time, cancellation time, selection deadline, concurrency policy version. Then provide two replay modes: Historical schedule replay Reproduce the original event ordering from recorded events. Schedule-insensitive replay Feed all completed results into deterministic selection order and see whether the outcome changes. If it does, you have discovered schedule sensitivity. 25. Distributed Attempts Need Lineage Step 20 separated logical operations from physical attempts. Replay should preserve that structure: operation-77 ├── attempt-1 worker-A lease-9 timeout ├── attempt-2 worker-B lease-10 PASS └── stale attempt-1 response rejected by fencing Without this, duplicate work can look like contradictory history. 26. Replay Release Promotion Evidence Step 24 introduced behavioral release manifests and promotion gates. When a release is promoted, store the evidence used for promotion: release candidate ↓ contract tests ↓ offline benchmark ↓ shadow comparison ↓ canary evidence ↓ promotion decision Then six months later you can answer: Why was this release considered safe to deploy? That is release provenance, not just runtime provenance. 27. Provenance for Coding Agents For a coding agent, an audit-ready run might include: repository = org/service base_commit = 81ac... worktree_snapshot = sha256:... request = task-44 behavior_release = agent-r17 planner_policy = plan-8 search_policy = mcts-5 tool_registry = tools-11 patch_candidate = sha256:... pytest_output = artifact:194 lint_output = artifact:195 verifier = code-verifier-9 verdict = PASS commit_operation = op-881 Now a regression can be reconstructed against the same source tree and verifier evidence. 28. Provenance for Research Agents Record: query, source URLs, retrieval timestamps, content snapshots/hashes, citation mapping, source authority metadata, model release, claim-to-source edges, verifier evidence. This lets you distinguish: source changed retrieval changed reasoning changed citation mapping changed verifier changed Those are different failures. 29. Provenance for Browser Agents Record: account scope, page snapshot, action target identity, element/DOM evidence, idempotency key, precondition, action request, response, postcondition, screenshots where useful. For irreversible actions, provenance should make it obvious whether the action actually happened. 30. Provenance for Data Agents Record: input dataset snapshot query/transform code hash schema version execution engine version result hash validation evidence publication operation Then a later discrepancy can be traced to data, code, engine, or validation changes. 31. Provenance for DevOps Agents For infrastructure changes, preserve: environment identity, desired change, observed pre-state, generated plan, approval identity, tool/provider version, fenced operation ID, mutation request, post-state, health verification, rollback target. A statement like “the agent deployed it” is not enough provenance for a production incident. 32. Artifact Retention Has a Cost Full replay data can become expensive. You may store: large prompts, screenshots, browser pages, retrieved documents, model outputs, verifier artifacts, test logs, sandbox snapshots. Use retention tiers. For example: Tier 0: hashes + metadata only Tier 1: decision-critical artifacts Tier 2: full verifier evidence Tier 3: full forensic snapshot Choose tier by risk, contract, incident status, regulation, and debugging value. Do not retain sensitive artifacts merely because replay is useful. 33. Privacy and Secrets Replay systems create a concentration of sensitive data. Treat them accordingly. Important controls include: tenant isolation, secret filtering, field-level redaction, access control, retention limits, encryption, audit logs, purpose limitation. Do not store raw credentials for reproducibility. Store stable references or safe fingerprints where possible. 34. Replay Quality Metrics Track whether your replay system actually works. Useful metrics include: manifest completeness artifact availability rate hash verification rate full replay success rate partial replay rate missing-artifact rate schedule-sensitivity rate counterfactual coverage verifier-replay agreement replay cost replay latency Also track which artifact classes are most often missing. That tells you where your provenance design is weak. 35. Failure Taxonomy A replay-aware agent platform should classify failures such as: MISSING_RELEASE_ARTIFACT MISSING_OBSERVATION HASH_MISMATCH MISSING_ENVIRONMENT_SNAPSHOT MODEL_REVISION_UNAVAILABLE TOOL_VERSION_UNAVAILABLE RETRIEVAL_SNAPSHOT_UNAVAILABLE MEMORY_TEMPORAL_LEAKAGE VERIFICATION_EVIDENCE_DETACHED SIDE_EFFECT_PROVENANCE_MISSING SCHEDULE_SENSITIVITY COUNTERFACTUAL_OUTCOME_UNKNOWN REPLAY_POLICY_MISMATCH This is much better than a generic replay failed. 36. Replay Invariants Useful hard invariants include: selected candidate must have exact candidate hash PASS must reference verifier evidence verifier evidence must reference exact candidate side effect must reference operation ID operation attempt must reference lease/fencing epoch when required observation must reference source and snapshot release ID must resolve to immutable manifest replay must not silently call live dependencies missing artifacts must remain explicit These are ordinary software invariants. They do not need an LLM. 37. A Minimal Replay Runner A compact implementation might look like: class ReplayRunner: def __init__(self, manifest_store, artifact_store, release_store): self.manifest_store = manifest_store self.artifact_store = artifact_store self.release_store = release_store def replay(self, run_id: str): manifest = self.manifest_store.get(run_id) release = self.release_store.get(manifest.release_id) artifacts = { ref.artifact_id: self.artifact_store.get(ref.artifact_id) for ref in manifest.artifacts } self._verify_hashes(manifest, artifacts) gateway = ReplayGateway(artifacts) return release.execute( task=artifacts[manifest.task_artifact_id], gateway=gateway, replay_mode=True, ) Production code needs far more nuance. But notice the direction: The replay runner uses immutable recorded evidence by default. It does not casually call live production dependencies. 38. Do Not Use Replay to Manufacture Evidence A counterfactual replay can suggest that a different policy might have behaved better. That is not automatically production evidence. If the replay substitutes recorded outputs, changes execution order, or omits unavailable side effects, mark those limitations. The same evidence-first rule still applies: A replay is an experiment whose assumptions must be explicit. 39. The Complete Evidence Loop At this point the architecture forms a closed loop: behavioral release ↓ production execution ↓ trajectory + provenance ↓ external verification ↓ immutable replay manifest ↓ offline replay / counterfactual analysis ↓ benchmark ↓ candidate release ↓ shadow / canary / promotion That is much stronger than “we looked at the logs and changed the prompt”. 40. Do You Actually Need Full Replay? Not every agent needs forensic reconstruction. A small personal script may only need: input model version prompt hash tool outputs final result A production coding or infrastructure agent may need far more. Use the same principle as the rest of this series: Add the cheapest provenance mechanism that closes the failure you actually need to diagnose. Do not build a distributed event-sourced audit platform for a five-line local automation. But if an agent can change production systems, spend real money, modify repositories, or make externally visible decisions, being able to reconstruct its behavior is not optional observability. It is part of correctness. 41. Practical Checklist Before calling an agent run reproducible, ask: Can I identify the exact behavioral release? Can I recover the task bytes or their immutable reference? Can I recover the environment snapshot? Can I recover the exact external observations? Can I distinguish observation from inference? Can I reconstruct routing/search/budget decisions? Can I identify every consequential side effect? Can I bind verification to the exact candidate? Can I detect artifact mutation? Can I replay without touching production? Can I state which parts are nondeterministic? Can I distinguish replay from live revalidation? Can I identify missing artifacts explicitly? Can I compare an old trajectory against a new release? Can I preserve privacy while doing all of the above? If several answers are no, the platform probably has logs, not provenance. 42. Final Principle The most important idea is simple: Reproducibility is not identical output. It is the ability to reconstruct the evidence, environment, policy, release, actions and verification that produced an outcome. Advanced agents are probabilistic systems embedded in changing environments. Perfect token determinism is often unavailable. Operational reproducibility is still achievable. Record immutable inputs. Version the behavioral release. Bind decisions to evidence. Bind verification to exact candidates. Bind side effects to operation identity. Preserve lineage. Make missing evidence explicit. And never let a replay silently become a new live experiment. The next stage is incident forensics for agent systems: turning provenance and replay into a disciplined process for reconstructing failures, identifying the earliest incorrect decision, measuring blast radius, distinguishing model error from system error, and producing remediation that can be verified against the original incident trajectory.