
The State vs. Event Law: How to Stop AI Agent Stale Memory Hallucinations
Executive Summary (BLUF): When multi-agent systems and local coding assistants read from flat file trees, naive memory updates cause context contamination, prompting models to resurrect deprecated schemas and retired network configurations. By establishing the State vs. Event Law, I physically bifurcate mutable living state from append-only event streams, guaranteeing deterministic truth precedence across concurrent agent runtimes. Operating this architecture across 20+ software project hubs and 160+ active content pipelines eliminated cross-agent state drift entirely, compressing thousands of lines of conflicting instructions into an ultra-thin root pointer compiling in under 400 milliseconds.
Autonomous coding assistants do not fail because they lack intelligence. They fail because language models possess no innate temporal reasoning engine over unstructured files. If you feed an agent a workspace containing notes from three months ago alongside active directives, the model treats every token as simultaneous truth.
I hit this wall directly while running multi-agent workflows across my software infrastructure. In one critical instance, an agent tasked with updating an automation workflow grepped an archived session log that referenced a retired webhook port. The agent treated that obsolete port as an active constraint, wrote unit tests against it, and pushed changes that broke production execution. Tracing that bug consumed hours, only to reveal that the agent had pulled the dead port from a post-mortem written twelve weeks earlier.
Giving AI coding agents more memory does not make them smarter. It gives them more opportunities to resurrect dead bugs.
Why autonomous coding agents resurrect dead bugs: the failure of flat memory
Autonomous coding agents resurrect dead bugs because standard agent implementations treat working memory as an append-only flat file. When developers instruct an agent to record session learnings into a file like memory.md, the agent sequentially appends chronological notes. Over weeks of active development, that document degrades into a contradictory timeline where two incompatible architectural statements sit ten lines apart.
Because an LLM processes text through semantic attention rather than chronological evaluation, it cannot discern which assertion supersedes the other. The model evaluates tokens based on statistical relevance to the prompt. If the prompt contains keywords heavily matching an older troubleshooting session, the agent prioritizes the deprecated pattern over the current architectural rule.
This breakdown leads directly to prompt dilution. When an autonomous model must parse hundreds of lines of obsolete historical chatter alongside active directives, its attention mechanism fractures. Critical engineering constraints, such as preserving idempotency keys, enforcing strict authentication headers, or maintaining zero-deadlock execution, are drowned out by historical discussions about bugs resolved months ago. The developer is trapped in an infuriating cycle of babysitting, spending more time debugging the agent's memory hallucinations than writing software.
The flaw of naive vector RAG: why cosine similarity cannot resolve temporal truth
Naive vector RAG fails for agent memory because cosine similarity measures conceptual proximity rather than chronological validity. When an agent searches a repository of 800+ notes using semantic embeddings, historical post-mortems frequently score higher similarity than active architectural directives. A detailed post-mortem documenting why an engineering team migrated away from port 3010 contains richer keyword density and emotional troubleshooting markers than a single declarative line stating that active traffic routes to port 8080.
Dense vector embeddings smear exact code identifiers. A query searching for the active database client matches both Supabase and Neon Postgres if both tools appear across historical decision logs. The vector search engine returns both snippets in the retrieval context window. The agent, lacking temporal metadata, frequently selects the obsolete pattern.
Probabilistic retrieval cannot solve a deterministic knowledge problem. To stop agents from hallucinating on outdated code, developers must replace probabilistic similarity guessing with deterministic filesystem boundaries.
The 3-layer architecture: separating living state from immutable event telemetry
To eliminate context contamination, I engineered the Unified Agent Project Memory (UAPM) standard across all my repositories. The core primitive is the State vs. Event Law: living state must be upserted in place, while historical events must be quarantined as immutable, append-only telemetry.
Here is the decoupled architecture blueprint showing how active state is partitioned from historical logs: 👇
Under this architectural contract, the codebase is partitioned into three distinct operational layers:
- The Living State Layer: Comprised exclusively of
02-Projects/<Project>/📖 PROJECT.md, the root working memory (memory.md), and local project state (.ai/MEMORY.md). These files represent the single source of truth for active reality, including the active tech stack, live port numbers, current schema definitions, and verified milestones. Agents are mandated to overwrite (upsert) these files in place whenever architecture changes. Stale facts are never permitted to linger inside the living state layer. - The Historical Event Stream: Encompassing project session digests in
02-Projects/<Project>/sessions/(and global logs in03-Sessions/) and completed plan archives in.ai/plans/history/. These files are immutable receipts documenting past experiments, failed attempts, and historical decisions. Agents are forbidden from treating code blocks, environment variables, or port configurations inside event logs as active truth. They serve purely as forensic telemetry for audits, never as authoritative design constraints. - Compiled Root Pointers: authoring standards centrally in
.ai/PROJECT-RULES.md,.ai/ARCHITECTURE.md, and.ai/GOTCHAS.md. A deterministic compiler script compiles these rules outward into an ultra-thin root file (AGENTS.md) under 50 lines, establishing symlinks for tool-specific interfaces likeCLAUDE.mdandGEMINI.md.
Compiling non-discoverable rules into ultra-thin pointers
My implementation directly incorporates Addy Osmani's agent context research: compiled root pointers should contain strictly non-discoverable information. High-entropy details, such as tricky edge cases, known gotchas, and specialized workflow triggers, belong in the compiled pointer. Conversely, directory structures, package versions, and generic syntax conventions are omitted because coding agents discover them autonomously through globbing, ripgrep, and AST tools.
Instead of manually updating disparate configuration files across .claude/, .gemini/, and .codex/, I centralized governance into a single PowerShell compiler kernel. The script reads core modular directives from .ai/, validates their structure, and compiles them into a unified AGENTS.md file at the repository root.
# .ai/bin/compile.ps1 - Deterministic UAPM Compiler Kernel
# Compiles modular engineering directives into an ultra-thin AGENTS.md root pointer.
# Usage: powershell -File .ai/bin/compile.ps1 [-Force]
param(
[switch]$Force
)
$ErrorActionPreference = "Stop"
# Canonical UAPM marker — present in compiled pointers to detect human edits
$uapmMarker = "Agent Directives - UAPM Standard"
# 1. Deterministic Path Resolution via $PSScriptRoot
$aiDir = Resolve-Path (Join-Path $PSScriptRoot "..")
$projectRoot = Resolve-Path (Join-Path $aiDir "..")
$projectName = (Split-Path $projectRoot -Leaf)
$projectRulesFile = Join-Path $aiDir "PROJECT-RULES.md"
$architectureFile = Join-Path $aiDir "ARCHITECTURE.md"
$gotchasFile = Join-Path $aiDir "GOTCHAS.md"
$decisionsFile = Join-Path $aiDir "DECISIONS.md"
$rulesDir = Join-Path $aiDir "rules"
# 2. Ingest Living Directives (Non-Discoverable Information Only)
$projectRules = if (Test-Path $projectRulesFile) { Get-Content $projectRulesFile -Raw -Encoding utf8 } else { "" }
$architecture = if (Test-Path $architectureFile) { Get-Content $architectureFile -Raw -Encoding utf8 } else { "" }
$gotchas = if (Test-Path $gotchasFile) { Get-Content $gotchasFile -Raw -Encoding utf8 } else { "" }
$decisions = if (Test-Path $decisionsFile) { Get-Content $decisionsFile -Raw -Encoding utf8 } else { "" }
# 3. Gather Modular Rules
$modularRules = ""
if (Test-Path $rulesDir) {
$ruleFiles = Get-ChildItem $rulesDir -Filter *.md
foreach ($file in $ruleFiles) {
$content = Get-Content $file.FullName -Raw -Encoding utf8
$modularRules += "`n### From $($file.BaseName)`n$content`n"
}
}
# 4. Construct Ultra-Thin Root Pointer (<50 Lines)
$compiledContent = @"
# Agent Directives - UAPM Standard (COMPILED - DO NOT EDIT BY HAND)
# PROJECT: $projectName
# Edit .ai/ source files, then re-run: powershell -File .ai/bin/compile.ps1
Before writing any code, read these .ai/ files:
1. .ai/MEMORY.md (active goals)
2. .ai/ARCHITECTURE.md (non-obvious gotchas)
3. .ai/DECISIONS.md (committed architectural decisions)
4. .ai/GOTCHAS.md (known pitfalls)
5. .ai/skills/ (task-specific workflows - load before executing)
---
$projectRules
$architecture
$decisions
$gotchas
$modularRules
"@
# 5. Write Primary AGENTS.md Pointer (Protected by UAPM Marker)
$agentsFile = Join-Path $projectRoot "AGENTS.md"
if ((Test-Path $agentsFile) -and -not $Force) {
$existing = Get-Content $agentsFile -Raw -Encoding utf8 -ErrorAction SilentlyContinue
if ($existing -and ($existing -notmatch [regex]::Escape($uapmMarker))) {
$migratedDir = Join-Path $aiDir "_migrated"
if (!(Test-Path $migratedDir)) { New-Item -ItemType Directory -Path $migratedDir -Force | Out-Null }
$backupName = "AGENTS.md." + (Get-Date -Format "yyyyMMdd-HHmmss") + ".bak"
Copy-Item $agentsFile (Join-Path $migratedDir $backupName) -Force
Write-Host " [!] SKIPPED (human-authored): $agentsFile" -ForegroundColor Yellow
} else {
[System.IO.File]::WriteAllText($agentsFile, $compiledContent, [System.Text.Encoding]::UTF8)
Write-Host " [+] Generated: $agentsFile (PRIMARY)" -ForegroundColor Green
}
} else {
[System.IO.File]::WriteAllText($agentsFile, $compiledContent, [System.Text.Encoding]::UTF8)
Write-Host " [+] Generated: $agentsFile (PRIMARY)" -ForegroundColor Green
}Here is the terminal execution trace showing the compilation and symlink verification running in sub-400ms: 👇
To protect human modifications, the compiler script checks for a canonical UAPM marker string before overwriting any file. If manual edits are detected, the compiler creates a timestamped backup in `.ai/_migrated/` rather than overwriting custom instructions.
Gotcha: Never let agents edit symlinked pointer files directly. If an agent modifiesCLAUDE.md, the next execution of the compiler kernel will overwrite that modification. All behavioral changes must be written to.ai/PROJECT-RULES.md.
How Rule 14 truth precedence resolves multi-agent conflicts
Rule 14 truth precedence resolves multi-agent conflicts by establishing an unambiguous priority ladder across all system prompts. When multiple agents like Claude Code, Google Antigravity, and OpenCode inspect the same repository simultaneously, conflicting statements inevitably arise between past logs and active code.
Here is the exact decision hierarchy used to resolve conflicting assertions deterministically: 👇
Under this hierarchy, priority flows from highest to lowest authority:
- Tier 1 (Highest): Living Project State. Files like
.ai/MEMORY.mdand02-Projects/<Project>/📖 PROJECT.mdoverrule all other assertions. If living state declares port 8080, port 8080 is ground truth. - Tier 2: Compiled Root Pointers. The generated
AGENTS.mdand symlinked entry points provide verified workflow gotchas and boundary invariants. - Tier 3: Repository AST and Active Code. Live code syntax discovered via AST parsers and file tree exploration overrules descriptive documentation.
- Tier 4 (Lowest / Read-Only): Historical Session Logs. Files in
03-Sessions/and plan archives are treated strictly as read-only telemetry. An agent is explicitly instructed to ignore port numbers, schema structures, and dependencies found within Tier 4 when generating active code.
| Architectural Layer | Write Semantics | Content Scope | Precedence Rank |
|---|---|---|---|
| Living State (`.ai/MEMORY.md`) | In-place upsert (destructive overwrite) | Current tech stack, live ports, active schema | Tier 1 (Authoritative) |
| Compiled Pointer (`AGENTS.md`) | Automated compilation | Non-discoverable gotchas, skill routing | Tier 2 (Operational) |
| Active Codebase (`src/`) | Git commit / branch | Live functions, type signatures, AST | Tier 3 (Functional) |
| Event Stream (`03-Sessions/`) | Append-only (immutable receipt) | Debugging post-mortems, chat history, logs | Tier 4 (Informational only) |
By codifying this hierarchy into root system directives, agents resolve contradictions deterministically without asking clarification questions or guessing which file is newer.
Architectural trade-offs and engineering friction
Every architectural choice introduces concrete operational costs. Enforcing the State vs. Event Law resolved memory drift, but it introduced two distinct engineering trade-offs during production implementation.
First, in-place state upsert requires strict disciplinary overhead. Because living state files undergo destructive overwrites, any mistake made by an agent during an upsert can erase valuable operational context. To mitigate this risk, I had to introduce automated git hooks that take ephemeral snapshot commits of .ai/MEMORY.md before subagents execute write operations. This prevents data loss while maintaining single-source-of-truth purity.
Second, symlinking root pointers across divergent agent tools exposes filesystem edge cases. Windows developer environments require elevated permissions or developer mode enabled to create directory symlinks cleanly. On platforms where symlinks fail, the compiler must fall back to hard file copies, which introduces the risk of file drift if developers accidentally bypass the compiler kernel.
What used to require 14 minutes of manual context debugging during complex multi-agent runs now executes in under 400 milliseconds. Over six months of continuous deployment across 20+ software project hubs and 160+ content pipelines, this system maintained zero cross-agent state drift.
● Key Takeaways
- 1.Bifurcate mutable living state from immutable historical event telemetry to eliminate context contamination.
- 2.Enforce strict in-place upsert semantics on living state files, ensuring deprecated configurations never persist in active search spaces.
- 3.Compress root AGENTS.md pointers to under 50 lines by omitting discoverable data like directory structures and package versions.
- 4.Establish deterministic Rule 14 truth precedence so agents resolve conflicting assertions by favoring living state over past session logs.
- 5.Centralize multi-agent rules into a single source compiler to prevent tool configuration sprawl across Claude Code, Antigravity, and OpenCode.
Frequently Asked Questions

Let's Create Something Amazing Together!
Whether you have a project in mind or just want to connect, I'm always excited to collaborate and bring ideas to life.
Continue the Journey
Thanks for taking the time to explore my work! Let's connect and create something amazing together.