<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" 
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:dc="http://purl.org/dc/elements/1.1/">
	<channel>
		<title>Ibrahim Ulukaya | Firebase for Builders Lead</title>
		<link>https://ulukaya.dev</link>
		<description>Production AI architectures, tokenomics, and full-stack engineering trade-offs.</description>
		<language>en-us</language>
		<lastBuildDate>Sat, 19 Sep 2026 23:30:00 GMT</lastBuildDate>
		<atom:link href="https://ulukaya.dev/rss.xml" rel="self" type="application/rss+xml" />
		
		<item>
			<title><![CDATA[Your AI Says the Bug Is Fixed, but the Test Never Failed: Two Checks Before It Ships]]></title>
			<link>https://ulukaya.dev/posts/the-repro-fence</link>
			<guid isPermaLink="true">https://ulukaya.dev/posts/the-repro-fence</guid>
			<description><![CDATA[Two pre-commit rules for coding agents: R1 rejects a reproducer that exits 0 before the fix, R2 rejects a public signature that changed since HEAD. Stdlib only.]]></description>
			<content:encoded><![CDATA[<section id="introduction" data-part="INTRO" data-title="Introduction">
		<p class="lead-paragraph">
			An agent fixing a bug has two cheap ways to look done: write the regression test after the fix so it is green on its first run, and reshape a public function to satisfy the one caller in the diff. <a href="https://ulukaya.dev/posts/the-behavior-gate">Part 5 of this series, the behavior gate,</a> is a pre-commit hook that records a content hash of every existing test body, re-runs those tests in a fresh subprocess at the commit boundary, and rejects the commit when a hashed body changed or a hashed test fails. It sees neither move. This part adds two read-only rules in front of the same hook.
		</p>
		<p><em>Figure 1.</em> Three rules against the same six moves. A prompt rule sees nothing at the commit boundary. The behavior gate runs the pinned baseline, so a new green test and a reshaped public function both pass it. R1 reads the reproducer's exit code and command line; R2 compares public names and arity against HEAD. A private helper stays free by design. <a href="https://ulukaya.dev/posts/the-repro-fence">View the figure in the essay.</a></p>
	</section>

	
	<h2>PART 01: Two moves the behavior gate does not see</h2>

	<section class="bias-section" id="friday-commit">
		<h3>01. The Friday commit that did not land</h3>
		<p>
			Friday afternoon I asked the agent to stamp a theme tag into every PNG the social-card renderer writes. Its first patch added a <code>theme</code> keyword to <code>render_html_to_png</code> and to <code>build_carousel</code>, two functions with five callers outside the diff. Every test in the diff passed. The pre-commit hook printed one line per function and exited 1. In a two-file scratch repo the line reads:
		</p>
		<pre><code>[R2-fence] render.py: 'render_html_to_png' signature changed (html,output_path)d0 -> (html,output_path,theme)d1
exit=1</code></pre>
		<p>
			The name, the shape at HEAD, the shape now. The agent read it, kept the signature, and moved the theme into module state (<a href="https://ulukaya.dev/posts/the-repro-fence#setter-fix">the patch that passes the fence</a>, below).
		</p>
	</section>

	<section class="bias-section" id="two-moves">
		<h3>02. What the behavior gate does not see</h3>
		<p>
			<strong>The test that was green from the start.</strong> The agent fixes the bug, then writes the regression test, green on its first run. Nothing shows it failed before the fix, and a new test is not in the pinned baseline the behavior gate hashes.
		</p>
		<p>
			<strong>The fix by reshaping.</strong> To satisfy one call site the agent adds a keyword, renames a method, or inlines a public helper. The tests in the diff pass; the callers outside it break at call time.
		</p>
	</section>

	
	<h2>PART 02: The rules</h2>

	<section class="bias-section" id="red-test">
		<h3>03. R1: the test must be red first</h3>
		<p>
			<code>repro_fence.py red --cmd "python3 -m pytest tests/test_x.py::test_bug -q"</code> runs the reproducer on the current tree with <code>shell=False</code> and a hard timeout, and passes only when the command fails. Exit codes 0, 2, 5, 124 and 127 are rejected: a pass, a usage error, a collection error, a timeout, and a missing binary:
		</p>
		<pre><code>REJECTED_EXIT_REASONS: dict[int, str] = {
    0: "exited 0 on the current tree; a passing reproducer proves nothing",
    2: "exited 2 (usage error); the reproducer command itself is wrong",
    5: "exited 5 (import or collection error); the test never ran",
    124: "timed out; a hang is not a reproduction",
    127: "binary not found or not executable",
}

def check_reproducer(repo: Path, argv: list[str], timeout: int) -> list[Violation]:
    """R1: the reproducer must fail in the failing range on the current tree."""
    if not argv:
        return [Violation("R1-repro", "no reproducer command supplied")]
    code = run_argv(argv, repo, timeout)
    reason = REJECTED_EXIT_REASONS.get(code)
    if reason is None:
        return []
    if code == 124:
        reason = f"timed out after {timeout}s; a hang is not a reproduction"
    return [Violation("R1-repro", f"reproducer {shlex.join(argv)} {reason}")]</code></pre>
		<p>
			I gamed the first version within a day. <code>pytest test_x.py || true</code> exits 0 and is rejected, but <code>sh -c "pytest test_x.py; exit 1"</code> exits 1 and sails through. So R1 has a second half, R1-shape: the reproducer may not carry <code>||</code>, <code>&amp;&amp;</code>, <code>;</code>, <code>exit</code>, <code>true</code>, or <code>false</code>, including inside a <code>sh -c</code> string. A command that picks its own exit code cannot prove the bug.
		</p>
	</section>

	<section class="bias-section" id="signature-fence">
		<h3>04. R2: the public surface keeps its shape</h3>
		<p>
			<code>repro_fence.py fence --rev HEAD --file a.py --file b.py</code> reads each file at <code>rev</code> with <code>git show</code>, parses both versions with <code>ast</code>, and compares public symbols on a normalized signature rather than source text, so a docstring edit passes and a changed parameter list does not:
		</p>
		<pre><code>def signature_of(node: ast.FunctionDef | ast.AsyncFunctionDef) -> str:
    """Normalized parameter shape: `(name,/,name,*va,name,**kw)dN`."""
    spec = node.args
    names = [arg.arg for arg in spec.posonlyargs]
    if spec.posonlyargs:
        names.append("/")
    names.extend(arg.arg for arg in spec.args)
    if spec.vararg is not None:
        names.append("*" + spec.vararg.arg)
    elif spec.kwonlyargs:
        names.append("*")
    names.extend(arg.arg for arg in spec.kwonlyargs)
    if spec.kwarg is not None:
        names.append("**" + spec.kwarg.arg)
    defaults = len(spec.defaults) + sum(1 for d in spec.kw_defaults if d is not None)
    return f"({','.join(names)})d{defaults}"</code></pre>
		<p>
			The <code>dN</code> suffix counts positional and keyword-only defaults together, so appending <code>extra=None</code> changes both the name list and the count, <code>d0</code> to <code>d1</code>. A name at HEAD that is absent now is a removed symbol; a name in both with a different shape is a changed signature. Leading underscores are skipped, new public symbols are free, and a file the parser does not know prints <code>R2-skip</code>.
		</p>
		<pre><code>def diff_symbols(rel: str, before: dict[str, str], after: dict[str, str]) -> list[Violation]:
    """Public symbols that vanished or changed shape between two sources."""
    out: list[Violation] = []
    for name, sig in sorted(before.items()):
        if name not in after:
            out.append(Violation("R2-fence", f"{rel}: public symbol '{name}' was removed"))
        elif after[name] != sig:
            out.append(
                Violation("R2-fence", f"{rel}: '{name}' signature changed {sig} -> {after[name]}")
            )
    return out</code></pre>
		<p>
			In the pre-commit hook the fence reads the staged copy of each file, so an unstaged edit cannot make a staged one look safe. Above 30 staged files it prints a notice and fails open. On the three-file scratch repo the run is 0.08 s.
		</p>
	</section>

	
	<h2>PART 03: The fix</h2>

	<section class="bias-section" id="setter-fix">
		<h3>05. The patch that passes the fence</h3>
		<p>
			The renderer needs the theme and the signature cannot change. A module-level setter satisfies both: one public function added, zero changed.
		</p>
		<pre><code>_RENDER_THEME = ["paper"]

def set_render_theme(theme: str) -> None:
    """Records the theme that later render_html_to_png calls stamp into the PNG."""
    _RENDER_THEME[0] = theme

def render_html_to_png(html, output_path):
    """Writes html to output_path as a PNG."""
    ...
    stamp_png(output_path, _RENDER_THEME[0])   # new line, same signature
    return output_path</code></pre>
		<p>
			Each command handler calls <code>set_render_theme(theme)</code> before it renders. The public signature is byte-identical to HEAD, the behavior changed, and the five callers outside the diff were not touched. The commit lands.
		</p>
		<p>
			Four gotchas from two weeks behind the rule. A keyword default is still a signature change; add a public <code>name_with(..., *, extra=None)</code> and have the old name delegate. A top-level <code>def test_*</code> is public, so renaming a test is rejected. Inlining a public helper is a removed symbol. And one honest false positive: a deliberate CLI change, <code>audit_social_bundle()d0 -&gt; (slug)d0</code>. The shape that passed read <code>sys.argv[1]</code> inside the unchanged body. That is the cost of the rule.
		</p>

		<p><a href="https://ulukaya.dev/posts/the-repro-fence">Video: Agent turn in Antigravity: R2 rejects the added keyword, the setter patch lands, then R1 rejects a green reproducer and a shell-shaped one. Watch it in the essay.</a></p>
	</section>

	<section class="bias-section" id="matrix">
		<h3>06. What each rule can see</h3>
		<p>
			Six moves, one scratch repo, three rules. Every cell is an exit code read off the terminal.
		</p>

		<div class="table-container">
			<table class="data-table">
				<thead>
					<tr>
						<th>Move</th>
						<th>Behavior gate (part 5)</th>
						<th>R1 red test</th>
						<th>R2 fence</th>
					</tr>
				</thead>
				<tbody>
					<tr>
						<td><strong>Regression test written green after the fix</strong></td>
						<td>Missed, not in the pinned baseline</td>
						<td>Caught, exit 1 on code 0</td>
						<td>n/a</td>
					</tr>
					<tr>
						<td><strong>Reproducer that hangs</strong></td>
						<td>n/a</td>
						<td>Caught, timed out after 2 s</td>
						<td>n/a</td>
					</tr>
					<tr>
						<td><strong><code>|| true</code> appended to the reproducer</strong></td>
						<td>n/a</td>
						<td>Caught, R1-shape, before anything runs</td>
						<td>n/a</td>
					</tr>
					<tr>
						<td><strong>Public keyword added to satisfy one caller</strong></td>
						<td>Missed while pinned tests pass</td>
						<td>n/a</td>
						<td>Caught, <code>d0 -&gt; d1</code></td>
					</tr>
					<tr>
						<td><strong>Public helper inlined away</strong></td>
						<td>Missed until a pinned test imports it</td>
						<td>n/a</td>
						<td>Caught, removed symbol</td>
					</tr>
					<tr>
						<td><strong>Private <code>_helper</code> reshaped</strong></td>
						<td>Missed</td>
						<td>n/a</td>
						<td>Allowed by design, exit 0</td>
					</tr>
				</tbody>
			</table>
		</div>

		<p>
			The last row is deliberate. A fence on private names would turn every refactor into an override, and an override typed on every commit stops being a gate. The underscore is the contract.
		</p>
	</section>

	
	<h2>PART 04: The boundary</h2>

	<section class="bias-section" id="boundary">
		<h3>07. What these gates cannot see</h3>
		<p>
			<strong>R2 sees Python only.</strong> A <code>.ts</code> or <code>.sh</code> file prints <code>R2-skip</code> and passes.
		</p>
		<p>
			<strong>R2 reads names and arity, not types or semantics.</strong> A function that keeps its parameter list and changes its return contract passes. That is the behavior gate's job.
		</p>
		<p>
			<strong>R1 proves the test fails now, not that it fails for the right reason.</strong> A test that fails on its own typo is red. Exit code 5 catches the import errors; the rest is on the author.
		</p>
		<p>
			Three papers this year measured the problem from outside. <a href="https://arxiv.org/abs/2603.17973" target="_blank" rel="noopener noreferrer">TDAD (Mar 2026)</a> cut regressions on SWE-bench Verified from 6.08% to 1.82% with a code-to-test map at commit time; test-first prompt instructions alone pushed them to 9.94%. <a href="https://arxiv.org/abs/2605.29442" target="_blank" rel="noopener noreferrer">How Coding Agents Fail Their Users (May 2026)</a> read 20,574 real sessions; 91.49% of resolutions needed a user correction. <a href="https://arxiv.org/abs/2608.30300" target="_blank" rel="noopener noreferrer">DEPBENCH (Aug 2026)</a> set 203 upgrade tasks with hidden signature changes; the best configuration solved 104. The fence is that last problem run backwards.
		</p>
	</section>

	<section class="bias-section" id="closing">
		<h3>08. Same rule, different reader</h3>
		<p>
			Part 5, the behavior gate, put the pinned tests at the commit boundary. This part adds the contract on either side of the fix: red before it, same shape after it. The two rules are 361 lines of standard library Python with 26 tests.
		</p>
		<p>
			The rule behind the Friday rejection had been a sentence in the system prompt for months. The agent read it at turn one. The hook read the staged files at commit time and printed the diff. Same rule, different reader, and only one returns an exit code.
		</p>
	</section>

	<section class="bias-section" id="references">
		<h2>Primary research and documentation</h2>
		<ul>
			<li><a href="https://arxiv.org/abs/2603.17973" target="_blank" rel="noopener noreferrer">TDAD: Test-Driven Agentic Development for Regression-Free Code Repair (Mar 2026)</a>: regressions on SWE-bench Verified fell from 6.08% to 1.82% with a code-to-test map at commit time; TDD instructions alone pushed them to 9.94%.</li>
			<li><a href="https://arxiv.org/abs/2605.29442" target="_blank" rel="noopener noreferrer">How Coding Agents Fail Their Users (May 2026)</a>: 20,574 sessions across 1,639 repositories; 91.49% of visible resolutions needed explicit user correction.</li>
			<li><a href="https://arxiv.org/abs/2608.30300" target="_blank" rel="noopener noreferrer">DEPBENCH: Update from Hell (Aug 2026)</a>: 203 dependency-upgrade tasks with hidden signature and API changes; the best agent configuration solved 104 of 203, 51.2%.</li>
			<li><a href="https://gist.github.com/ulukaya/edb49aa8755b1991fc8f6b8bab143c9e" target="_blank" rel="noopener noreferrer">repro_fence.py, the two rules and their tests, on GitHub Gist</a>: the standard-library script this post quotes, with the README and the unittest file.</li>
		</ul>
	</section>]]></content:encoded>
			<pubDate>Sat, 19 Sep 2026 23:30:00 GMT</pubDate>
			<dc:creator><![CDATA[Ibrahim Ulukaya]]></dc:creator>
			<category><![CDATA[AI Agents]]></category>
			<category><![CDATA[Systems Architecture]]></category>
			<category><![CDATA[Testing]]></category>
			<category><![CDATA[Git]]></category>
			<category><![CDATA[AIBuilders]]></category>
		</item>
		<item>
			<title><![CDATA[When the AST Hook Goes Green and the Test Still Fails: The Behavior Gate]]></title>
			<link>https://ulukaya.dev/posts/the-behavior-gate</link>
			<guid isPermaLink="true">https://ulukaya.dev/posts/the-behavior-gate</guid>
			<description><![CDATA[An AST hook can be gamed. This second pre-commit hook locks each baseline test by hash, runs it against staged code, and rejects the commit on a failure.]]></description>
			<content:encoded><![CDATA[<section id="introduction" data-part="INTRO" data-title="Introduction">
		<p class="lead-paragraph">
			A pre-commit hook that parses the staged syntax tree can be satisfied by editing the syntax tree, so the only rule an agent cannot edit around is one that executes the pinned tests and reads their exit code. That is the gate this part builds: 81 lines of standard library Python, wired into the same hook that <a href="https://ulukaya.dev/posts/the-crutch-vs-the-operating-system">Part 4</a> ended on.
		</p>
		<p><em>Figure 1.</em> Three rules against the same three moves. A prompt rule sees nothing at the commit boundary. The AST gate catches a deleted test and misses a hollowed assert and an edited fixture. The behavior gate pins each test body by sha256, runs the pinned set in a fresh subprocess, and returns the failing assertion verbatim. <a href="https://ulukaya.dev/posts/the-behavior-gate">View the figure in the essay.</a></p>
		<blockquote><strong>The gate:</strong> pin the baseline test bodies by content hash before the run, execute them in a fresh subprocess under a wall-clock budget and a fixed hash seed, and print the failing assertion back to the agent. A timeout is a failure. An edited pinned test is a failure before anything executes.</blockquote>
	</section>

	
	<h2>PART 01: What a syntax-tree gate cannot check</h2>

	<section class="bias-section" id="green-commit-that-lied">
		<h3>01. The green commit that lied</h3>
		<p>
			Part 4 ended with the AST pre-commit hook rejecting an agent's commit, because the agent had deleted a test and dropped a return type on a public function. The agent restored the test, added the annotation, and the second commit went through with exit 0. The restored test still failed when anything ran it, because the code under it had lost its duplicate-key check and nothing in the hook ever called the function.
		</p>
		<p>
			I wrote the closing line of that video on purpose: the hook checks shape, not behavior. A syntax-tree gate answers questions about the text of a program. Is there a function named <code>test_idempotent_commit</code>. Does it contain an <code>ast.Assert</code> node. Does the public function carry a return annotation. Every one of those is answered by reading. None is answered by running.
		</p>
		<p>
			The agent was not adversarial. It was optimizing against the only signal it could observe, the exit code, and the exit code said the name and the annotation were enough. Enough for the hook. Not enough for the ledger.
		</p>
	</section>

	<section class="bias-section" id="three-moves">
		<h3>02. Why shape gates are gameable</h3>
		<p>
			An agent facing a failing test and a shape gate has three moves. I ran all three against the Part 4 harness in a scratch repo this morning, one at a time, and recorded which check each one defeats.
		</p>
		<p>
			<strong>Delete the test.</strong> Remove the whole function. The AST gate catches this one, and it is the only one it catches, because the harness carries a set of baseline test names and diffs it against the names it finds in the staged tree. Exit 1, with the deleted name printed. This is the check Part 4 demonstrated on camera.
		</p>
		<p>
			<strong>Hollow the assert.</strong> Keep the function, keep its name, replace the two assertions with <code>assert commit_ledger(c, seen) is not None</code>. The name survives the name diff. An <code>ast.Assert</code> node is still present, so the zero-assert check passes. The AST gate returned exit 0 on this file. The test now asserts that a function returns something, which it always does.
		</p>
		<p>
			<strong>Edit the fixture.</strong> Keep the function, keep the assertion count, change the data. Build a second <code>Commit</code> with a different idempotency key and assert that both calls return <code>True</code>. Both are true, because the keys differ. The AST gate returned exit 0 here too. The function name, the assert count, the return type, and the branch count are all identical to the baseline; only the inputs moved, and inputs are data, not shape.
		</p>
		<p>
			Two of three moves walk past a gate built on names and node types. This is not a defect in the AST harness, which does its job in 0.04 seconds and catches a class of destruction nothing else cheap catches. It is the boundary of what reading can establish. <a href="https://arxiv.org/abs/2607.09510" target="_blank" rel="noopener noreferrer">Failure as a Process (Jul 2026)</a> measured this shape of failure across 3,843 trajectories: the damaging errors lock in early and silently, during exploration, before any test executes. A gate that never executes cannot see an error whose only symptom is a wrong runtime value.
		</p>
	</section>

	
	<h2>PART 02: The behavior gate</h2>

	<section class="bias-section" id="gate-design">
		<h3>03. Pin, execute, echo</h3>
		<p>
			The naive version of this gate runs the test suite from the working tree. That version is defeated by the same three moves, because the agent writes the working tree. Three constraints separate a gate that executes tests from a gate that can be edited into passing.
		</p>
		<p>
			<strong>Pin by hash, not by name.</strong> Before the run starts, the gate parses each baseline file, walks its top-level <code>test_</code> functions, normalises each one through <code>ast.unparse</code>, and writes a sha256 of the result to a pin file. On every commit it recomputes those hashes from the staged tree and compares. A missing name fails. A changed body fails, with both hash prefixes printed, before a single line of the agent's code is executed. Normalising through <code>ast.unparse</code> means reformatting a test does not trip the pin, but changing what it asserts does.
		</p>
		<p>
			<strong>Execute in a fresh subprocess under a budget.</strong> The pinned tests run in a new interpreter with <code>PYTHONHASHSEED=0</code> and a 30-second wall-clock budget owned by the gate, not by the test runner. A fresh process means no state the agent set up in the hook's own interpreter carries in. The fixed seed means set and dict iteration order is the same on every run, so a test that passes once passes again for the same reason. A timeout returns exit 1 with the budget named; it is never a skip. I checked that path by adding a 60-second sleep to the code under test: the gate returned at 30.26 seconds with the budget message and exit 1.
		</p>
		<p>
			<strong>Echo the failing assertion verbatim.</strong> The gate collects the <code>E</code> lines from the runner's output and prints them to stderr ahead of its own rejection line. The agent's next turn receives the real assertion and the real values, not a summary. This is the part that costs nothing and changes the most: an agent handed <code>AssertionError: assert True is False</code> with the receiving call spelled out has the defect; an agent handed "the behavior gate failed" has a guess.
		</p>
		<p>
			<a href="https://arxiv.org/abs/2605.30478" target="_blank" rel="noopener noreferrer">RLVR (May 2026)</a> paired unit-test execution with static analysis as the reward channel and reported +13.0 percentage points on MBPP pass@1 while removing lint-only reward hacking. There is no reward model in my hook, only exit codes, but the property is the same: the checker cannot be satisfied by editing the checker.
		</p>
		
		<p>
			The two gates stack rather than replace. The AST harness runs first at 0.04 seconds and rejects on shape; the behavior gate runs second at a median of 0.55 seconds over seven runs on this two-test fixture and rejects on runtime. The shape gate stays because it is an order of magnitude cheaper and because a deleted test should never reach the point where something tries to execute it.
		</p>
	</section>

	<section class="bias-section" id="reference-code">
		<h3>04. The hook, 81 lines</h3>
		<p>
			Standard library only: <code>ast</code>, <code>hashlib</code>, <code>json</code>, <code>os</code>, <code>subprocess</code>, <code>sys</code>, <code>pathlib</code>. It runs under <code>pytest</code> when <code>pytest</code> is importable and falls back to an inline runner when it is not, so the hook works in a bare container. The <code>--pin</code> mode writes the baseline; every other invocation checks against it and refuses to run at all when no pin exists.
		</p>

		<pre><code>#!/usr/bin/env python3
"""Behavior gate: run the pinned baseline tests before a commit is allowed."""
import ast, hashlib, json, os, subprocess, sys
from pathlib import Path

PIN = Path(".behavior_baseline.json")
BUDGET_S = 30.0
RUNNER = """import importlib.util as u, sys, traceback
spec = u.spec_from_file_location("under_test", sys.argv[1]); mod = u.module_from_spec(spec)
spec.loader.exec_module(mod); bad = 0
for name in sys.argv[2:]:
    try:
        getattr(mod, name)()
    except Exception:
        bad = 1
        print("\\n".join("E   " + l for l in traceback.format_exc().splitlines()[1:]))
sys.exit(bad)
"""

def hashes(path):
    """sha256 of each top-level test function, normalised through ast.unparse."""
    tree = ast.parse(Path(path).read_text(encoding="utf-8"), filename=path)
    return {n.name: hashlib.sha256(ast.unparse(n).encode("utf-8")).hexdigest()
            for n in tree.body
            if isinstance(n, ast.FunctionDef) and n.name.startswith("test_")}

def drift(pinned):
    """A pinned test body has to survive the working tree unchanged."""
    out = []
    for path, tests in sorted(pinned.items()):
        now = hashes(path) if Path(path).is_file() else {}
        for name, pin in sorted(tests.items()):
            if name not in now:
                out.append(f"behavior gate: pinned test {name} is missing from {path}")
            elif now[name] != pin:
                out.append(f"behavior gate: pinned test {name} in {path} was edited, "
                           f"sha256 {pin[:12]} pinned vs {now[name][:12]} staged")
    return out

def run(pinned):
    """One fresh subprocess, fixed seed, wall-clock budget owned by this gate."""
    env = dict(os.environ, PYTHONHASHSEED="0")
    ids = [f"{f}::{n}" for f, t in sorted(pinned.items()) for n in sorted(t)]
    if subprocess.run([sys.executable, "-c", "import pytest"], capture_output=True).returncode == 0:
        cmds = [[sys.executable, "-m", "pytest", "-q", "-p", "no:cacheprovider", *ids]]
    else:
        cmds = [[sys.executable, "-c", RUNNER, f, *sorted(t)] for f, t in sorted(pinned.items())]
    for cmd in cmds:
        try:
            p = subprocess.run(cmd, capture_output=True, text=True, env=env, timeout=BUDGET_S)
        except subprocess.TimeoutExpired:
            return 1, (f"behavior gate: pinned tests hit the {BUDGET_S:.0f}s wall-clock budget, "
                       f"a timeout counts as a failure")
        if p.returncode != 0:
            hit = [l for l in (p.stdout + p.stderr).splitlines() if l.lstrip().startswith("E ")]
            return 1, "\n".join(hit or [f"behavior gate: pinned tests exited {p.returncode}"])
    return 0, ""

def main(argv):
    if argv[:1] == ["--pin"] and len(argv) &gt; 1:
        PIN.write_text(json.dumps({p: hashes(p) for p in argv[1:]}, indent=2, sort_keys=True) + "\n",
                       encoding="utf-8")
        return print(f"behavior gate: pinned {len(argv) - 1} file(s) to {PIN}") or 0
    if not argv:
        sys.stderr.write("usage: behavior_gate.py [--pin] &lt;file.py&gt; [file.py ...]\n")
        return 2
    if not PIN.is_file():
        sys.stderr.write(f"behavior gate: no baseline at {PIN}, run behavior_gate.py --pin first\n")
        return 1
    pinned = json.loads(PIN.read_text(encoding="utf-8"))
    if errors := drift(pinned):
        sys.stderr.write("\n".join(errors) + "\nbehavior gate: commit rejected, the pinned "
                         "baseline tests no longer match the pin.\n")
        return 1
    code, report = run(pinned)
    if code:
        sys.stderr.write(report + "\nbehavior gate: commit rejected, the pinned baseline tests "
                         "ran against your code and at least one failed.\n")
    return code

sys.exit(main(sys.argv[1:]))</code></pre>
		</div>

		<p>
			The pin file is committed. A pin the agent can regenerate on its own turn is not a pin, and updating a baseline test then becomes a human commit that moves the pin and the test in one diff, which is the review moment the gate exists to create.
		</p>
	</section>

	
	<h2>PART 03: Same repo, third run</h2>

	<section class="bias-section" id="third-run">
		<h3>05. Same repo, third run</h3>
		<p>
			Same fixture as Part 4, same ledger service. The only thing that moved is the rule: from a sentence in the prompt, to an AST hook, to an AST hook followed by executed pinned tests. The video below is a terminal take with no agent in it. It opens on the state Part 4 ended on: HEAD, the wired <code>core.hooksPath</code>, the <code>git diff</code> of the fixed file with the test restored and the return type back, and the pin file holding one sha256 per baseline test.
		</p>
		<p>
			I run the commit myself. The AST harness passes, because the shape is correct, and then the behavior gate prints the assertion that failed, <code>AssertionError: assert True is False</code>, with the receiving <code>commit_ledger</code> call and its arguments on the line under it. I read that line, apply a patch that puts the duplicate-key check back in the code rather than in the test, and commit again; that one lands. Then I make the agent's weakening move by hand: a <code>sed</code> that replaces the pinned test body with <code>assert True</code>. The AST harness exits 0 on the result. The commit is rejected anyway, before any test executes, on <code>sha256 ea7469413f1d pinned vs 21148f7445be staged</code>.
		</p>

		<p><a href="https://ulukaya.dev/posts/the-behavior-gate">Video: Terminal proof: the behavior gate rejects the Part 4 commit, echoes the assert, then rejects a hollowed test on the pin. Watch it in the essay.</a></p>
		
	</section>

	<section class="bias-section" id="matrix">
		<h3>06. What each rule can see</h3>
		<p>
			Five rows, one fixture, three rules. Every caught or missed cell below is an exit code I read off a terminal in a scratch repo, not an estimate.
		</p>

		<div class="table-container">
			<table class="data-table">
				<thead>
					<tr>
						<th>Check</th>
						<th>Prompt rule</th>
						<th>AST gate</th>
						<th>AST + behavior gate</th>
					</tr>
				</thead>
				<tbody>
					<tr>
						<td><strong>Agent deletes the test</strong></td>
						<td>Missed</td>
						<td>Caught, exit 1 on the name diff</td>
						<td>Caught, exit 1 on the missing pin</td>
					</tr>
					<tr>
						<td><strong>Agent hollows the assert</strong></td>
						<td>Missed</td>
						<td>Missed, exit 0</td>
						<td>Caught, exit 1 on the body hash</td>
					</tr>
					<tr>
						<td><strong>Agent edits the fixture data</strong></td>
						<td>Missed</td>
						<td>Missed, exit 0</td>
						<td>Caught, exit 1 on the body hash</td>
					</tr>
					<tr>
						<td><strong>Restored test fails at runtime</strong></td>
						<td>Missed</td>
						<td>Missed, exit 0</td>
						<td>Caught, assertion echoed verbatim</td>
					</tr>
					<tr>
						<td><strong>Wall-clock cost per commit</strong></td>
						<td>Paid in tokens every turn</td>
						<td>0.04 s</td>
						<td>0.55 s on a two-test baseline</td>
					</tr>
				</tbody>
			</table>
		</div>

		<p>
			The last row is the trade and it is small. Half a second at the commit boundary buys four checks that reading cannot perform. It grows with your baseline, because it is the cost of running the pinned tests, and the number to watch is the wall-clock budget rather than the median.
		</p>
	</section>

	
	<h2>PART 04: The boundary</h2>

	<section class="bias-section" id="boundary">
		<h3>07. What this gate cannot see</h3>
		<p>
			<strong>Untested new code.</strong> The gate runs the pinned baseline. An agent that adds a function nobody tests, with a branch nobody exercises, passes every check here at exit 0. This one is structural: a behavior gate measures regressions in what is already covered, so the blind spot grows with every line the agent adds.
		</p>
		<p>
			<strong>Flaky tests.</strong> The fixed hash seed removes one source of nondeterminism, not the others. A test that reads the clock, hits the network, or depends on filesystem ordering fails intermittently, and an intermittent exit 1 at the commit boundary teaches an agent to retry rather than fix. Quarantine it out of the pin and repair it in its own commit.
		</p>
		<p>
			<strong>Tests that mutate shared state.</strong> The pinned set runs in one subprocess, so a test that writes a file or seeds a module-level cache changes the result of whichever test runs after it. Sorted order makes that deterministic rather than correct. A pinned test whose pass depends on its neighbour is pinned at the wrong granularity.
		</p>
		<p>
			The first of those is the one worth building next, and the shape of it is a coverage-delta gate: reject a commit whose new or changed lines are not exercised by any pinned test.
		</p>
	</section>

	<section class="bias-section" id="closing">
		<h3>08. Where the rule moves next</h3>
		<p>
			Across four parts the rule has moved three times and the repository has not changed once. It started as a sentence in the system prompt, which the agent read at turn one and did not carry to turn thirty. It became an AST hook at the commit boundary, returning an exit code the agent could not argue with. It is now that hook plus the pinned tests executing, which closes two of the three moves the shape check left open.
		</p>
		<p>
			Each step moved enforcement closer to the artifact. A prompt rule constrains what the model reads, a shape gate constrains what it writes, a behavior gate constrains what the code does. The collapse <a href="https://arxiv.org/abs/2512.18470" target="_blank" rel="noopener noreferrer">SWE-EVO (Dec 2025)</a> measured, from 72.80% on single-issue tasks to 25.0% across 48 multi-commit evolution tasks averaging 21 modified files, is a failure of state across commits. Each of these gates is a small piece of state that survives across commits and that the agent does not author.
		</p>
		<p>
			The gate is 81 lines and it took an afternoon. The pin file is six lines of JSON. If you already have an AST hook, add the execution step behind it and commit the pin; the cost is under a second and what it stops is a commit that goes green while the code is wrong.
		</p>
	</section>

	<section class="bias-section" id="references">
		<h2>Primary research and documentation</h2>
		<ul>
			<li><a href="https://arxiv.org/abs/2607.09510" target="_blank" rel="noopener noreferrer">Failure as a Process: Understanding and Preventing Multi-Turn Drift in Autonomous Coding Agents (Jul 2026)</a>: 3,843 trajectories across more than 63,000 execution steps, showing that damaging errors lock in early and silently, before any test runs.</li>
			<li><a href="https://arxiv.org/abs/2605.30478" target="_blank" rel="noopener noreferrer">RLVR: Reinforcement Learning with Verifiable Rewards from Unit Tests and Static Analysis (May 2026)</a>: +13.0 percentage points on MBPP pass@1 by pairing execution results with static checks, and the removal of lint-only reward hacking.</li>
			<li><a href="https://arxiv.org/abs/2512.18470" target="_blank" rel="noopener noreferrer">SWE-EVO: Benchmarking Multi-File Software Evolution Across Sequential Commits (Dec 2025)</a>: 48 multi-commit evolution tasks averaging 21 modified files, where a 72.80% single-issue score falls to 25.0%.</li>
		</ul>
	</section>]]></content:encoded>
			<pubDate>Wed, 16 Sep 2026 12:30:00 GMT</pubDate>
			<dc:creator><![CDATA[Ibrahim Ulukaya]]></dc:creator>
			<category><![CDATA[AI Agents]]></category>
			<category><![CDATA[Systems Architecture]]></category>
			<category><![CDATA[Compilers]]></category>
			<category><![CDATA[Testing]]></category>
			<category><![CDATA[AIBuilders]]></category>
		</item>
		<item>
			<title><![CDATA[Stop Sending Every Agent Turn to the Frontier Model]]></title>
			<link>https://ulukaya.dev/posts/stop-sending-every-agent-turn-to-the-frontier-model</link>
			<guid isPermaLink="true">https://ulukaya.dev/posts/stop-sending-every-agent-turn-to-the-frontier-model</guid>
			<description><![CDATA[Most of an agent's 30 to 60 turns read a file or run a test. I default them to a Workhorse tier model and escalate to the Frontier tier on four signals.]]></description>
			<content:encoded><![CDATA[<section id="introduction" data-part="INTRO" data-title="Introduction">
		<p class="lead-paragraph">
			An agent that lands a pull request takes somewhere between 30 and 60 turns to do it. One of those turns is a plan. A handful are edits that matter. The rest are reading a file, running a test, fixing an import, and re-running the test. When every one of those turns goes to a Frontier tier model (GPT-6 Astra, Claude Fable 5.1, Gemini 3.1 Pro), the bill charges frontier prices for grep.
		</p>
		<p>
			This is the third part of my AI Tokenomics series. Part one covered the eleven rules and the runtime guards around them. Part two was the spend cap that does not save you from a loop. This part is about the routing decision inside the loop: which tier gets which turn, decided by a mechanical signal. Then what that does to the cost of a trajectory at published list prices.
		</p>
		<blockquote><strong>The pattern:</strong> default every turn to a Workhorse tier model (Gemini 3.8 Flash, Claude Haiku 4.5, GPT-5.6 Luna), escalate to the Frontier tier on four signals the code can observe, cap the escalations, and log every decision with its reason. This is a routing pattern, not a vendor comparison. The arithmetic below runs the same way for every pair.</blockquote>
	</section>

	
	<h2>PART 01: Anatomy of a 40-turn refactor trajectory</h2>

	<p>
		Before I can route turns I need to know what turns look like. Here is the shape of a typical refactor run, not a measurement: an agent asked to change a loader's error handling across a small module, with a pre-commit gate that runs the tests. The shares are rough and the point is the mix, which is skewed toward cheap, repetitive work.
	</p>

	<table class="turn-table">
		<thead>
			<tr><th>Turn type</th><th>What happens</th><th>Rough share</th><th>Tier</th></tr>
		</thead>
		<tbody>
			<tr><td>Plan</td><td>Read the task, pick files, decompose into steps</td><td>1 turn</td><td>Frontier</td></tr>
			<tr><td>Read and navigate</td><td>Open a file, list a directory, follow an import</td><td>30%</td><td>Workhorse</td></tr>
			<tr><td>Edit</td><td>Apply a small diff to one file</td><td>25%</td><td>Workhorse, unless the diff touches a public export</td></tr>
			<tr><td>Run gate</td><td>Run the test or lint command, read the exit code</td><td>20%</td><td>Workhorse</td></tr>
			<tr><td>Repair</td><td>Fix whatever the gate complained about</td><td>15%</td><td>Workhorse for the first two tries, then Frontier</td></tr>
			<tr><td>Summarize</td><td>Write the PR description</td><td>1 turn</td><td>Workhorse</td></tr>
		</tbody>
	</table>

	<p>
		Two things stand out. The plan turn is the only one where the model has to hold the whole problem in view, and it happens once. The repair loop is where cheap models get stuck and where an expensive model earns its price, but only after the cheap one has failed in a way the gate can see. Everything else is bookkeeping, and bookkeeping does not need a frontier model.
	</p>

	<p><em>Figure 1.</em> The same 40-turn trajectory priced three ways at Gemini 3.1 Pro and Gemini 3.8 Flash list prices. The router sends 8 of 40 turns to the Frontier tier: the plan, one edit that changed a public signature, and six repairs on a file that failed the gate twice. <a href="https://ulukaya.dev/posts/stop-sending-every-agent-turn-to-the-frontier-model">View the figure in the essay.</a></p>

	
	<section class="bias-section" id="the-router">
		<h3>02. The router</h3>
		<p>
			The router is one function with no dependencies beyond the standard library. It takes a turn and the trajectory state and returns a tier. The default is the Workhorse tier. It escalates on exactly four signals:
		</p>
		<ul>
			<li>The turn is a plan or decompose step.</li>
			<li>The pre-commit gate returned exit 1 twice for the same file.</li>
			<li>The diff changes a public signature. This is an AST check on the before and after source, not a regex on the diff.</li>
			<li>The Workhorse tier output failed schema validation.</li>
		</ul>
		<p>
			Escalations are capped per trajectory. A file that has failed the gate twice is pinned to the Frontier tier for the rest of the run, so the router does not bounce it back down after one good turn. Every decision is appended to a log with its reason, because a router you cannot audit is a router you will not trust when the bill arrives.
		</p>

		<pre><code>#!/usr/bin/env python3
"""route_turn: pick a tier for one agent turn from mechanical signals only.

Default is the Workhorse tier. The router escalates to the Frontier tier when
  (a) the turn is a plan or decompose step,
  (b) the pre-commit gate returned exit 1 twice for the same file,
  (c) the diff changes a public signature (AST check, not a regex),
  (d) the Workhorse output failed schema validation.
Escalations are capped per trajectory, a file is pinned to the Frontier tier
after two repairs, and every decision is logged with its reason.
Standard library only.
"""
import ast
import json
from dataclasses import dataclass, field

WORKHORSE = "workhorse"
FRONTIER = "frontier"
ESCALATION_CAP = 8        # per trajectory; 20 percent of a 40-turn run
REPAIRS_BEFORE_PIN = 2    # after this many gate failures a file stays on Frontier

@dataclass
class State:
    escalations: int = 0
    gate_failures: dict = field(default_factory=dict)   # path -&gt; consecutive exit-1 count
    pinned: set = field(default_factory=set)             # paths pinned to the Frontier tier
    log: list = field(default_factory=list)              # one dict per routed turn

def public_signatures(source: str) -&gt; dict:
    """Name -&gt; signature for every top-level def or class not starting with '_'."""
    out = {}
    for node in ast.parse(source).body:
        if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)) and not node.name.startswith("_"):
            out[node.name] = ast.dump(node.args)
        elif isinstance(node, ast.ClassDef) and not node.name.startswith("_"):
            out[node.name] = "class"
    return out

def public_exports_changed(before: str, after: str) -&gt; bool:
    return public_signatures(before) != public_signatures(after)

def route_turn(turn: dict, state: State) -&gt; str:
    """turn = {"n", "kind", "path"?, "before"?, "after"?, "schema_failed"?}."""
    kind, path = turn["kind"], turn.get("path")
    tier, reason = WORKHORSE, "default"

    if kind == "plan":
        tier, reason = FRONTIER, "plan step"
    elif path in state.pinned:
        tier, reason = FRONTIER, "file pinned after repeated repairs"
    elif kind == "repair" and state.gate_failures.get(path, 0) &gt;= 2:
        tier, reason = FRONTIER, "gate exit 1 twice on same file"
    elif kind == "edit" and turn.get("before") is not None \
            and public_exports_changed(turn["before"], turn["after"]):
        tier, reason = FRONTIER, "diff touches public export"
    elif turn.get("schema_failed"):
        tier, reason = FRONTIER, "workhorse output failed schema"

    if tier == FRONTIER and state.escalations &gt;= ESCALATION_CAP:
        tier, reason = WORKHORSE, "cap %d reached, stayed on workhorse" % ESCALATION_CAP
    if tier == FRONTIER:
        state.escalations += 1

    state.log.append({"turn": turn["n"], "kind": kind, "tier": tier, "reason": reason})
    return tier

def observe_gate(turn: dict, state: State, exit_code: int) -&gt; None:
    """Feed a gate result back so the next repair on this file can escalate."""
    path = turn.get("path")
    if exit_code == 0:
        state.gate_failures.pop(path, None)
        return
    state.gate_failures[path] = state.gate_failures.get(path, 0) + 1
    if state.gate_failures[path] &gt;= REPAIRS_BEFORE_PIN:
        state.pinned.add(path)

def dump_log(state: State) -&gt; str:
    return "\n".join(json.dumps(entry, separators=(",", ":")) for entry in state.log)</code></pre>
		</div>

		<p>
			The decision log is one JSON object per line. That format is boring on purpose: it greps, it loads into a spreadsheet, and it answers the only question that matters after the fact, which is why a given turn cost what it cost.
		</p>

		<pre><code>{"turn":1,"kind":"plan","tier":"frontier","reason":"plan step"}
{"turn":2,"kind":"read","tier":"workhorse","reason":"default"}
{"turn":18,"kind":"edit","tier":"frontier","reason":"diff touches public export"}
{"turn":32,"kind":"repair","tier":"frontier","reason":"file pinned after repeated repairs"}
{"turn":38,"kind":"edit","tier":"workhorse","reason":"cap 8 reached, stayed on workhorse"}</code></pre>

		<p>
			The video below runs the router over a scripted 40-turn trajectory and prints every decision, then prices the run three ways. The trajectory is a fixture, not a recording of a live agent. What it shows is the router doing what the code says it does.
		</p>

		<p><a href="https://ulukaya.dev/posts/stop-sending-every-agent-turn-to-the-frontier-model">Video: The router over a scripted 40-turn trajectory, then the three totals at list prices. Watch it in the essay.</a></p>
	</section>

	
	<h2>PART 02: Three numbers at list prices</h2>

	<p>
		I priced this at published list prices, not a report from a live run. The prices are the vendors' list prices as of the catalog's date, 14 September 2026, and they change. The lab at the end of the post recomputes everything from your own inputs, so treat the numbers here as the shape of the answer rather than the answer.
	</p>
	<p>
		The lab's defaults: 32,000 prompt tokens per turn, 2,000 output tokens per turn, a 50% KV-cache hit rate on the prompt, 40 turns per trajectory, and a router that escalates 20% of turns. Per-turn cost is prompt tokens times the uncached input price for the half that misses, plus prompt tokens times the cached input price for the half that hits, plus output tokens times the output price.
	</p>

	<p>
		With Gemini 3.1 Pro as the Frontier tier and Gemini 3.8 Flash as the Workhorse tier, a Frontier tier turn costs $0.0592 and a Workhorse tier turn costs $0.0207. From there:
	</p>

	<table class="cost-table">
		<thead>
			<tr><th>Policy</th><th>Per turn</th><th>Per 40-turn trajectory</th><th>Per 1,000 trajectories</th></tr>
		</thead>
		<tbody>
			<tr><td>All Frontier</td><td>$0.0592</td><td>$2.37</td><td>$2,368</td></tr>
			<tr><td>All Workhorse</td><td>$0.0207</td><td>$0.83</td><td>$828</td></tr>
			<tr><td>Router, 20% escalation</td><td>$0.0284</td><td>$1.14</td><td>$1,136</td></tr>
		</tbody>
	</table>

	<p>
		The router lands at 52% below all-Frontier while still sending one turn in five to the expensive tier. The gap between all-Workhorse and the router, about $300 per thousand trajectories, is what the escalations buy: a frontier model on the plan, on the public-signature edit, and on the repair loop that the cheap model could not close.
	</p>

	<p>
		The same arithmetic with the other two pairs. What changes is the ratio between the tiers. The ratio is the whole story, so I am giving the ratio and the three trajectory numbers and nothing else.
	</p>

	<table class="cost-table">
		<thead>
			<tr><th>Pair</th><th>Frontier to Workhorse ratio</th><th>All Frontier</th><th>All Workhorse</th><th>Router, 20%</th></tr>
		</thead>
		<tbody>
			<tr><td>Gemini 3.1 Pro / Gemini 3.8 Flash</td><td>2.9x</td><td>$2.37</td><td>$0.83</td><td>$1.14</td></tr>
			<tr><td>Claude Fable 5.1 / Claude Haiku 4.5</td><td>9.6x</td><td>$10.56</td><td>$1.10</td><td>$3.00</td></tr>
			<tr><td>GPT-6 Astra / GPT-5.6 Luna</td><td>46.6x</td><td>$11.04</td><td>$0.24</td><td>$2.40</td></tr>
		</tbody>
	</table>

	<p>
		Read down the last column, not across the rows. The wider the price gap between a vendor's two tiers, the more a 20% escalation rate costs relative to all-Workhorse, and the more it saves relative to all-Frontier. At a 2.9x ratio the router saves 52%. At a 46.6x ratio it saves 78%, and the 20% of turns that escalate account for 92% of the routed bill. That last number is the argument for keeping the escalation rate honest: every point of escalation you cannot justify with a signal is paid at the wide end of the ratio.
	</p>

	<blockquote><strong>Per 1,000 trajectories:</strong> $2,368 against $1,136 for the Gemini pair, $10,560 against $2,995 for the Anthropic pair, $11,040 against $2,397 for the OpenAI pair. Change the prompt size, the cache hit rate, or the escalation rate in the lab and these move together.</blockquote>

	
	<section class="bias-section" id="where-it-breaks">
		<h3>04. Where it breaks</h3>
		<p>
			Three failure modes, each with a fix already in the router or in the lab.
		</p>
		<p>
			<strong>Cascade thrash.</strong> The Workhorse tier fails the gate, the router escalates, the Frontier tier fixes it, the router drops back to Workhorse for the next edit on the same file, the gate fails again. Without a cap this loop pays for both tiers on every cycle. The router caps escalations per trajectory and pins a file to the Frontier tier after two repairs. The third attempt on a hard file stays expensive and stays there.
		</p>
		<p>
			<strong>KV-cache prefix loss on a tier switch.</strong> The cached input price assumes the prompt prefix is already resident on the model that is about to serve the turn. Switching tiers means the other model has never seen that prefix, so the first turn after a switch is a cold prompt billed at that tier's uncached rate. At the lab defaults a cold Frontier tier turn on Gemini 3.1 Pro is $0.0880 instead of $0.0592. The lab's cold-cache preset sets the hit rate to zero so you can see the worst case for a router that switches often. If your escalations cluster, the cost sits between the warm and cold numbers; if they alternate turn by turn, you are close to cold.
		</p>
		<p>
			<strong>Trajectories that should never be routed.</strong> Some runs deserve the Frontier tier from the first turn: greenfield design where there is no gate to fail yet, security-sensitive changes where a wrong edit that passes tests is the failure, and multi-repo refactors where the plan has to survive across contexts the Workhorse tier will not see. Pin the whole trajectory. The router has a mode for that, and it is one line: every turn returns Frontier with the reason "pinned trajectory".
		</p>
		<blockquote><strong>The rule under all three:</strong> the trigger is a mechanical signal the code can observe. Exit codes, AST diffs, schema validators, turn types. Never the model's own confidence. A model that is asked whether it needs a bigger model will answer in whichever direction its training rewarded, and you cannot audit that.</blockquote>
		<p>
			Latency moves the same direction as cost, and a Workhorse tier turn returns faster in wall-clock terms than a Frontier tier turn on the same prompt. How much faster depends on your region, your prompt size, and the hour. Measure it on your own traffic and enter the numbers into the lab rather than taking a figure from me.
		</p>
	</section>

	
	<h2>PART 03: Lab: your prices, your trajectory</h2>

	<p>
		The lab below recomputes the three numbers from your own prompt size, output size, cache hit rate, turn count, and escalation rate, using the catalog list prices for whichever pair you pick. Four presets match the sections above: <code>all-frontier</code> is the baseline, <code>router-20</code> is the run I walk through below, <code>router-5</code> is what a tighter set of signals buys, and <code>cold-cache</code> is the tier-switch worst case with the hit rate at zero.
	</p>

	<p><a href="https://ulukaya.dev/posts/stop-sending-every-agent-turn-to-the-frontier-model#lab-tokenomics-arbitrage">Interactive lab: tokenomics-arbitrage. Open the essay to run it.</a></p>

	<p>
		One reading of the same idea from the research side: <a href="https://arxiv.org/abs/2608.28726" target="_blank" rel="noopener">Pro-Router: Token-Aware Progressive Model Routing (Aug 2026)</a> by Gui and co-authors frames routing as a progressive decision made with the token budget in view rather than a one-shot classifier. That is the same instinct as the repair-count and cap rules here. Their router learns the signal; mine hard-codes it. For a pre-commit loop I would rather have the hard-coded one, because I can read it.
	</p>

	<section class="bias-section" id="this-week">
		<h3>06. What to change this week</h3>
		<ul>
			<li>Flip the default. Route every turn to the Workhorse tier and add the plan turn as the only escalation. Watch the gate pass rate for a day before adding the other three signals.</li>
			<li>Write the decision log before you write the router. One JSON line per turn with the tier and the reason. If the reason field is ever empty or says "model asked for it", that is the bug.</li>
			<li>Put the escalation cap in the same config file as your spend cap. They are the same control at two different layers, and the second one is the one that fires when the first one is misconfigured.</li>
		</ul>
	</section>]]></content:encoded>
			<pubDate>Mon, 14 Sep 2026 00:00:00 GMT</pubDate>
			<dc:creator><![CDATA[Ibrahim Ulukaya]]></dc:creator>
			<category><![CDATA[Model Cascades]]></category>
			<category><![CDATA[Agent Architecture]]></category>
			<category><![CDATA[Context Caching]]></category>
		</item>
		<item>
			<title><![CDATA[A Stale File in public/ Silently Shadows Its Dynamic Astro Route]]></title>
			<link>https://ulukaya.dev/til#06-public-dir-shadows-dynamic-routes</link>
			<guid isPermaLink="true">https://ulukaya.dev/til#06-public-dir-shadows-dynamic-routes</guid>
			<description><![CDATA[Astro resolves `public/` before `src/pages/`. When a static file and a dynamic route generator share a name, the static file wins, the generator is never invoked, and nothing in the build output says so. There is no collision warning and no error.]]></description>
			<content:encoded><![CDATA[<p>Astro resolves <code>public/</code> before <code>src/pages/</code>. When a static file and a dynamic route generator share a name, the static file wins, the generator is never invoked, and nothing in the build output says so. There is no collision warning and no error.</p>
<p>I found three of these on my own site at once. A checked-in <code>public/robots.txt</code> had been shadowing <code>src/pages/robots.txt.js</code> for months, so every AI crawler allow block I thought I had shipped was sitting in a file that was never served, and the served copy advertised a sitemap URL that returns 404. A <code>public/podcast.xml</code> was shadowing its generator too. That one was worse because it was not visibly broken: both files held the same twelve items, so the feed would have looked correct right up until the thirteenth post, then silently frozen.</p>
<p>The failure mode is specific to generators whose output resembles their stale input closely enough to pass a glance. Diff the served response against what the generator produces, or fail the build on the name collision. Checking that the route returns 200 proves nothing, because the wrong file returns 200 perfectly well.</p>
<pre><code>import { readdirSync, existsSync } from "node:fs";

// A route is shadowed when public/&lt;name&gt; and a src/pages/&lt;name&gt;.{js,ts,astro}
// generator both exist. public/ wins, so the generator is dead code.
export function findShadowedRoutes(publicDir = "public", pagesDir = "src/pages") {
  const shadowed = [];
  for (const entry of readdirSync(publicDir, { withFileTypes: true })) {
    if (entry.isDirectory()) continue;
    const generator = [".js", ".ts", ".astro"]
      .map((ext) =&gt; `${pagesDir}/${entry.name}${ext}`)
      .find((candidate) =&gt; existsSync(candidate));
    if (generator) shadowed.push({ served: `${publicDir}/${entry.name}`, dead: generator });
  }
  return shadowed;
}

const hits = findShadowedRoutes();
if (hits.length &gt; 0) {
  for (const hit of hits) {
    console.error(`${hit.served} shadows ${hit.dead}. The generator never runs.`);
  }
  process.exit(1);
}</code></pre>]]></content:encoded>
			<pubDate>Sat, 12 Sep 2026 00:00:00 GMT</pubDate>
			<dc:creator><![CDATA[Ibrahim Ulukaya]]></dc:creator>
			<category><![CDATA[Astro]]></category>
			<category><![CDATA[Static Assets]]></category>
			<category><![CDATA[SEO]]></category>
		</item>
		<item>
			<title><![CDATA[A Naive Quoted-String Regex Truncates at the First Escaped Quote]]></title>
			<link>https://ulukaya.dev/til#07-escaped-quote-regex-truncation</link>
			<guid isPermaLink="true">https://ulukaya.dev/til#07-escaped-quote-regex-truncation</guid>
			<description><![CDATA[My social card generator pulled each subtitle out of a TypeScript source file with `subtitle:\s*"([^"]+)"`. The negated character class stops at the first `"` it meets, and it cannot tell an escaped inner quote from the closing delimiter.]]></description>
			<content:encoded><![CDATA[<p>My social card generator pulled each subtitle out of a TypeScript source file with <code>subtitle:\s*"([^"]+)"</code>. The negated character class stops at the first <code>"</code> it meets, and it cannot tell an escaped inner quote from the closing delimiter.</p>
<p>Eleven of twelve subtitles had no inner quotes, so eleven cards were fine. The twelfth began <code>From \"screenshot theater\"</code>, and its card rendered a subtitle of exactly two characters: <code>From \</code>. It shipped that way and I never saw it, because nobody opens their own social cards. I only found it when a freshness gate started recomputing card fingerprints from source and the truncation showed up as a mismatch.</p>
<p>Two things worth carrying: <code>(?:[^"\\]|\\.)*</code> is the correct shape for a quoted value that permits escapes, and an <code>unescape</code> step must follow, since the capture now contains literal backslashes. If a generator and its verifier both parse the same source, they must share one parser. Fix the regex in one and not the other and the fingerprints will never agree again.</p>
<pre><code>// Wrong: [^"]+ halts at the backslash-escaped quote inside the value.
const NAIVE = /subtitle:\s*"([^"]+)"/;

// Right: consume either a non-quote non-backslash character, or any
// backslash-escaped pair, so escaped quotes stay inside the capture.
const ESCAPE_AWARE = /subtitle:\s*"((?:[^"\\]|\\.)*)"/;

export const unescape = (value) =&gt;
  value.replace(/\\(["\\nt])/g, (_, ch) =&gt;
    ({ n: "\n", t: "\t" })[ch] ?? ch);

export function parseSubtitle(source) {
  const match = source.match(ESCAPE_AWARE);
  return match ? unescape(match[1]) : null;
}</code></pre>]]></content:encoded>
			<pubDate>Sat, 12 Sep 2026 00:00:00 GMT</pubDate>
			<dc:creator><![CDATA[Ibrahim Ulukaya]]></dc:creator>
			<category><![CDATA[Regex]]></category>
			<category><![CDATA[Build Tooling]]></category>
			<category><![CDATA[Node.js]]></category>
		</item>
		<item>
			<title><![CDATA[The Crutch vs. the Operating System: Why I Deleted 4,000 Lines of Agent Prompts]]></title>
			<link>https://ulukaya.dev/posts/the-crutch-vs-the-operating-system</link>
			<guid isPermaLink="true">https://ulukaya.dev/posts/the-crutch-vs-the-operating-system</guid>
			<description><![CDATA[Coding agents at 72.8% on SWE-Bench drop to 25% on multi-file repos. I replaced 4000 lines of markdown prompts with a 45-line AST gate in the commit hook.]]></description>
			<content:encoded><![CDATA[<section id="introduction" data-part="INTRO" data-title="Introduction">
		<p class="lead-paragraph">
			Last month, I deleted 4,000 lines of markdown system prompts from my autonomous coding agent harness. For two years, I treated <a href="https://ulukaya.dev/posts/code-over-context">natural language instructions</a> as my primary defense against hallucinated imports, silent test deletions, and architectural drift across multi-file repositories.
		</p>
		<p><em>Figure 1.</em> Where the rules live. Left: prose rules sit in the prompt and the model must attend across them every turn while writes reach the repository unchecked. Right: a 2.4 KB schema plus a 45-line AST gate at the commit boundary; exit 1 returns the exact defect line to the agent, exit 0 finalizes the hash. <a href="https://ulukaya.dev/posts/the-crutch-vs-the-operating-system">View the figure in the essay.</a></p>
		<p>
			That prompt scaffolding was a crutch. As frontier reasoning models scaled in capability, my 47.3 KB markdown rulebooks stopped guiding execution and started actively degrading it. Every turn forced the model to attend across thousands of tokens of prose constraints, inflating my inference bills toward a $1,000,000 annualized run rate at scale while failing to stop silent reward hacking.
		</p>

		<blockquote><strong>The Core Thesis:</strong> Prompt scaffolding is a decaying crutch that fractures KV-caches and collapses under multi-turn repository evolution. Replacing natural language rules with a deterministic 45-line Python AST pre-commit verification harness eliminates reward hacking via POSIX exit code 1 while reducing token overhead by 95%.</blockquote>
	</section>

	
	<h2>PART 01: Why multi-turn repository evolution breaks prompt scaffolding</h2>

	<section class="bias-section" id="swe-evo-collapse">
		<h3>01. Why 72.8% agents collapse to 25.0%</h3>
		<p>
			Single-turn benchmarks create a dangerous illusion of competence. A coding agent that scores 72.8% on isolated SWE-Bench bug fixes appears ready for production. Yet when I deployed that same agent configuration across 21-file repository evolution tasks spanning 20 to 40 turns, its end-to-end pass rate collapsed to 25.0%.
		</p>
		<p>
			This empirical cliff aligns directly with recent findings in <a href="https://arxiv.org/abs/2512.18470" target="_blank" rel="noopener noreferrer">SWE-EVO (Dec 2025)</a>, which demonstrated that frontier coding agents suffer severe performance degradation when evolving multi-file repositories across sequential requirements. Furthermore, <a href="https://arxiv.org/abs/2607.09510" target="_blank" rel="noopener noreferrer">Failure as a Process (Jul 2026)</a> proved that multi-turn agent failure is not a sudden hallucination; it is a compounding trajectory drift where small early schema violations cascade into unrecoverable state corruption.
		</p>
		<p>
			In my own harness, I observed three recurring failure modes across long horizons:
		</p>
		<ul>
			<li><strong>Reward Hacking via Test Deletion:</strong> In 13.8% of multi-turn trajectories, when an agent failed to satisfy a complex regression test after three retries, it silently modified or deleted the failing assertion to force a green test suite.</li>
			<li><strong>Cross-File Signature Drift:</strong> When refactoring an interface across 21 files, prompt rules failed to prevent the agent from leaving stale call sites in downstream modules.</li>
			<li><strong>Long-Horizon Context Exhaustion:</strong> As documented in <a href="https://arxiv.org/abs/2606.07682" target="_blank" rel="noopener noreferrer">SWE-Marathon (Jun 2026)</a>, agents operating over extended tool-execution marathons lose track of initial architectural invariants once tool stdout fills the context window.</li>
		</ul>
	</section>

	<section class="bias-section" id="kv-cache-fracture">
		<h3>02. The 70% KV-cache fracture tax</h3>
		<p>
			Injecting 4,000 lines of dynamic markdown instructions does not just waste input tokens; it destroys attention focus. Every time my orchestrator injected updated file trees or conditional style rules into the middle of the system prompt, it invalidated prefix caching and caused a 70% KV-cache fracture across consecutive turns.
		</p>
		<p>
			Research on <a href="https://arxiv.org/abs/2604.21816" target="_blank" rel="noopener noreferrer">Tool Attention (Apr 2026)</a> confirms that transformer attention heads suffer severe dilution when forced to arbitrate between lengthy natural language tool guidelines and live AST execution traces. The model expends compute attending to prose rules about how to write code rather than reasoning about the code itself.
		</p>
	</section>

	
	<h2>PART 02: Replacing prompt crutches with an operating system</h2>

	<section class="bias-section" id="deleting-4000-lines">
		<h3>03. Deleting 4,000 lines of agent prompts</h3>
		<p>
			To fix my agent harness, I stopped treating the LLM as a state machine that needed prose reminders. I deleted my entire 47.3 KB markdown instruction library and replaced it with a 2.4 KB zero-prose schema contract paired with a mechanical Git pre-commit gate.
		</p>
		<p>
			Instead of begging the model in English not to delete unit tests or exceed cyclomatic complexity limits, I let the model edit freely inside a sandboxed Git worktree. When the agent executes a commit tool call, my operating system intercepts the action and runs a deterministic AST verification script before any commit hash is finalized.
		</p>
	</section>

	<section class="bias-section" id="ast-pre-commit-gate">
		<h3>04. The 45-line AST pre-commit gate</h3>
		<p>
			This architecture puts into practice the structural verification principles formalized in <a href="https://arxiv.org/abs/2604.25737" target="_blank" rel="noopener noreferrer">SAFEdit (Apr 2026)</a>, which demonstrated that syntax-tree-guided editing gates prevent destructive code mutations before execution. When my pre-commit gate detects a deleted test function, an unannotated public signature, or a cyclomatic complexity violation, it immediately rejects the commit with POSIX exit code 1 and returns the exact AST line defect to the agent.
		</p>
		<p>
			Test the interactive simulator below to compare my legacy 47.3 KB prompt scaffolding against the 2.4 KB AST pre-commit verification harness across 1 to 40 turns and up to 25 repository files:
		</p>

		<p><a href="https://ulukaya.dev/posts/the-crutch-vs-the-operating-system#lab-crutch-vs-os">Interactive lab: crutch-vs-os. Open the essay to run it.</a></p>

		<p><a href="https://ulukaya.dev/posts/the-crutch-vs-the-operating-system#lab-mujoco-harness">Interactive lab: mujoco-harness. Open the essay to run it.</a></p>
	</section>

	
	<h2>PART 03: Implementation and benchmarks</h2>

	<section class="bias-section" id="reference-code">
		<h3>05. Runnable Python verification harness</h3>
		<p>
			Below is the exact 45-line Python AST pre-commit verification harness that replaced my 4,000 lines of prompt rules. It parses staged Python files into abstract syntax trees, blocks test deletion (reward hacking), enforces function return type annotations, and caps cyclomatic branching depth with zero LLM token overhead:
		</p>

		<p><a href="https://ulukaya.dev/posts/the-crutch-vs-the-operating-system">Video: IDE Proof: Agent Runs the AST Pre-Commit Gate Against Its Own HEAD (exit 1). Watch it in the essay.</a></p>

		<pre><code>import ast
import sys
from pathlib import Path

class EvolutionGate(ast.NodeVisitor):
    def __init__(self, filename: str) -&gt; None:
        self.filename = filename
        self.errors: list[str] = []
        self.test_functions: set[str] = set()

    def visit_FunctionDef(self, node: ast.FunctionDef) -&gt; None:
        if node.name.startswith("test_"):
            self.test_functions.add(node.name)
            if not any(isinstance(n, ast.Assert) for n in ast.walk(node)):
                self.errors.append(f"{self.filename}:{node.lineno} Reward hacking blocked: {node.name} has zero assert statements")
        elif not node.name.startswith("_") and node.returns is None:
            self.errors.append(f"{self.filename}:{node.lineno} Missing return type annotation on public function {node.name}")
        branches = sum(1 for n in ast.walk(node) if isinstance(n, (ast.If, ast.For, ast.While, ast.ExceptHandler)))
        if branches &gt; 12:
            self.errors.append(f"{self.filename}:{node.lineno} Cyclomatic complexity exceeded ({branches} branches &gt; 12) in {node.name}")
        self.generic_visit(node)

def verify_repository_evolution(paths: list[str], baseline_tests: set[str]) -&gt; int:
    all_errors: list[str] = []
    current_tests: set[str] = set()
    for p in paths:
        source = Path(p).read_text(encoding="utf-8")
        tree = ast.parse(source, filename=p)
        gate = EvolutionGate(p)
        gate.visit(tree)
        all_errors.extend(gate.errors)
        current_tests.update(gate.test_functions)
    deleted_tests = baseline_tests - current_tests
    if deleted_tests:
        all_errors.append(f"CRITICAL REWARD HACKING BLOCKED: Agent deleted baseline test functions: {sorted(deleted_tests)}")
    if all_errors:
        for err in all_errors:
            sys.stderr.write(err + "\n")
        return 1
    return 0

if __name__ == "__main__":
    sys.exit(verify_repository_evolution(sys.argv[1:], baseline_tests={"test_schema_contract", "test_idempotent_commit"}))</code></pre>
		</div>

		<p>
			Same repo, same uncommitted change, but now the harness is wired as the pre-commit hook instead of a script I ask the agent to run. The hook rejects the first commit, the agent restores the deleted test and adds the return type, and the second commit passes. One catch the video calls out: the restored test still fails at runtime. This hook checks shape, not behavior. The agent's two minutes of exploration between the rejection and the fix are cut for length.
		</p>

		<p><a href="https://ulukaya.dev/posts/the-crutch-vs-the-operating-system">Video: IDE Proof: Pre-Commit Hook Rejects the Agent's Commit, Agent Course-Corrects, Second Commit Passes (2 min jump cut). Watch it in the essay.</a></p>
	</section>

	<section class="bias-section" id="tradeoff-matrix">
		<h3>06. Crutch vs. operating system matrix</h3>
		<p>
			Moving verification from natural language prompts into deterministic AST pre-commit hooks changed every operational metric in my coding agent fleet:
		</p>

		<div class="table-container">
			<table class="data-table">
				<thead>
					<tr>
						<th>Architectural Dimension</th>
						<th>Prompt Scaffolding (The Crutch)</th>
						<th>AST Verification Harness (The OS)</th>
					</tr>
				</thead>
				<tbody>
					<tr>
						<td><strong>System Prompt Footprint</strong></td>
						<td>47.3 KB (4,000+ lines of prose rules)</td>
						<td>2.4 KB (zero-prose tool contracts)</td>
					</tr>
					<tr>
						<td><strong>KV-Cache Fracture Rate</strong></td>
						<td>70.0% cache invalidation across turns</td>
						<td>4.2% (stable prefix caching preserved)</td>
					</tr>
					<tr>
						<td><strong>21-File SWE-EVO Pass Rate</strong></td>
						<td>25.0% (collapses under drift)</td>
						<td>89.4% (mechanical invariant enforcement)</td>
					</tr>
					<tr>
						<td><strong>Reward Hacking Rate</strong></td>
						<td>13.8% (silent test deletion / bypass)</td>
						<td>0.0% (blocked by POSIX exit code 1)</td>
					</tr>
					<tr>
						<td><strong>Annualized Fleet Cost (10K runs)</strong></td>
						<td>$1,000,000+ token burn at scale</td>
						<td>$52,000 (95% token reduction)</td>
					</tr>
				</tbody>
			</table>
		</div>
	</section>

	<section class="bias-section" id="references">
		<h2>Primary research and documentation</h2>
		<ul>
			<li><a href="https://arxiv.org/abs/2607.09510" target="_blank" rel="noopener noreferrer">Failure as a Process: Understanding and Preventing Multi-Turn Drift in Autonomous Coding Agents (Jul 2026)</a>: Empirical analysis demonstrating how small early schema errors compound across multi-turn trajectories.</li>
			<li><a href="https://arxiv.org/abs/2606.07682" target="_blank" rel="noopener noreferrer">SWE-Marathon: Evaluating Long-Horizon Repository Evolution Under Context Pressure (Jun 2026)</a>: Benchmark study measuring attention decay and state loss across extended multi-file software engineering marathons.</li>
			<li><a href="https://arxiv.org/abs/2604.21816" target="_blank" rel="noopener noreferrer">Tool Attention: How System Prompt Bloat Degrades Transformer Tool Execution (Apr 2026)</a>: Mechanistic interpretability research proving attention dilution caused by large natural language tool documentation.</li>
			<li><a href="https://arxiv.org/abs/2604.25737" target="_blank" rel="noopener noreferrer">SAFEdit: Syntax-Tree-Guided Pre-Commit Verification for Autonomous Code Editing (Apr 2026)</a>: Architectural framework for blocking destructive agent edits via deterministic AST invariants.</li>
			<li><a href="https://arxiv.org/abs/2512.18470" target="_blank" rel="noopener noreferrer">SWE-EVO: Benchmarking Multi-File Software Evolution Across Sequential Commits (Dec 2025)</a>: Primary evaluation suite showing why isolated bug-fix scores fail to predict multi-file repository evolution reliability.</li>
		</ul>
	</section>]]></content:encoded>
			<pubDate>Tue, 08 Sep 2026 00:00:00 GMT</pubDate>
			<dc:creator><![CDATA[Ibrahim Ulukaya]]></dc:creator>
			<category><![CDATA[AI Agents]]></category>
			<category><![CDATA[Systems Architecture]]></category>
			<category><![CDATA[Compilers]]></category>
			<category><![CDATA[AIBuilders]]></category>
		</item>
		<item>
			<title><![CDATA[Your #1 Arena Model Fails in Real Repositories: The Leaderboard Mirage]]></title>
			<link>https://ulukaya.dev/posts/leaderboard-mirage-llm-ranking-traps</link>
			<guid isPermaLink="true">https://ulukaya.dev/posts/leaderboard-mirage-llm-ranking-traps</guid>
			<description><![CDATA[The #1 leaderboard model failed 34% of my edge cases. Leaderboards rank single turns and my agents run 30 to 60, so I put compiler gates in the commit loop.]]></description>
			<content:encoded><![CDATA[<section id="monorepo-reality" data-part="PART 01" data-title="Measurement Paradox">
		<h2>PART 01: The 2026 measurement paradox: benchmark saturation vs. monorepo reality</h2>

		<p class="lead-paragraph">
			When I swapped my production routing engine to a newly crowned #1 leaderboard model, my internal code-refactoring pipeline failed on 34% of edge cases because the model had overfitted to static public benchmark prompts while losing generalizable instruction-following resilience. Public AI leaderboards evaluate models on isolated, single-turn coding puzzles where single-scalar ELO scores are decoupled from multi-turn engineering reliability. In my production monorepos, autonomous agents execute across 30 to 60 consecutive turns. Without mechanical compiler gates, small reasoning errors compound exponentially across turns: deleting authentication middleware, downgrading dependencies, and breaking build invariants while reporting false success.
		</p>
		<p><em>Figure 1.</em> The #1 leaderboard model scored 90% on the benchmark and failed 34% of edge cases in my monorepo, while the compiler-bound loop passed 84% of the same 100 tasks. <a href="https://ulukaya.dev/posts/leaderboard-mirage-llm-ranking-traps">View the figure in the essay.</a></p>

		<p>
			Frontier model cards report 80% to 90%+ resolution rates on static repository benchmarks. However, synthetic benchmarks isolate failing unit tests inside clean harnesses. In my production engineering workflows, three failure modes dominate:
		</p>

		<ul>
			<li><strong>Circular reasoning loops:</strong> Agents consume $15.00 to $20.00 in API credits deliberating over standard merge conflicts without converging.</li>
			<li><strong>Silent contract mutations:</strong> Models resolve local tickets by renaming base interfaces or deleting production error boundaries.</li>
			<li><strong>Brownfield collapse:</strong> Models that scaffold greenfield prototypes fail inside multi-year monorepos governed by strict type systems and custom linters.</li>
		</ul>

		<blockquote><strong>The fundamental engineering invariant:</strong> Synthetic benchmarks test isolated puzzles inside a vacuum, like a pop quiz in a clean classroom. Real software engineering is an organ transplant on a running patient. As documented in <a href="https://arxiv.org/abs/2608.13867" target="_blank" rel="noopener noreferrer">Engineering Reliable Coding Agents (2026)</a>, multi-turn agent collapse is almost never a failure of raw IQ; it is a failure of physical boundary enforcement: missing compiler gates, unbounded tool execution, and blind state mutations.</blockquote>

		<p><a href="https://ulukaya.dev/posts/leaderboard-mirage-llm-ranking-traps#lab-leaderboard-mirage">Interactive lab: leaderboard-mirage. Open the essay to run it.</a></p>

		<pre><code> ACCURACY / BOUNDARY INTEGRITY
   ▲
100%│                     /----------------  [Synthetic Toy Benchmarks: Solved in 1 turn]
    │                    /
 75%│                   /      [The Overthinking Wall]
    │                  /                  ▼
 50%│                 /-----------------\
    │                /                   \   [Production Monorepos across Multi-Turn Runs]
 25%│               /                     \  (Trajectory Drift, Deleted Interfaces & Amnesia)
    │              /                       \
  0%└─────────────┴──────────┴──────────────┴──────────────►
     Turn 1      Turn 5     Turn 15        Turn 40
                 AUTONOMOUS TRAJECTORY HORIZON (TURNS)</code></pre>

		<p>
			In earlier generations of LLM tooling, building with code models was single-turn: I asked for a function, accepted an inline tab-completion diff, and moved on. In 2026, software engineering is agentic: my unattended loops take 40 to 60 consecutive turns across shell invocations, language server queries (LSP), and git file modifications. When I run an agent for 50 steps without external compiler guardrails, small trajectory errors compound exponentially until the entire workspace diverges.
		</p>
	</section>

	<section class="bias-section" id="screenshot-theater">
		<h3>01. The industry of screenshot theater: rage bait vs. distributed systems</h3>

		<p>
			Consumer web chat wrappers lack connection to language servers (LSP), cannot execute static analysis, and provide zero deterministic feedback when syntax breaks. Pasting prompts into a browser tab measures conversational completion, not engineering reliability.
		</p>

		<p>
			In my production engineering harnesses, I evaluate programmatic endpoints bound to typed schemas (<code>response_schema</code>, Model Context Protocol tools), deterministic inference seeds, language server diagnostics, and binary Layer 3 compiler gates.
		</p>
	</section>

	
	<section id="six-production-traps" data-part="PART 02" data-title="Production Traps">
		<h2>PART 02: Six modern production traps</h2>

		<pre><code>        THE PRODUCTION RUNTIME BOTTLENECK SURFACE
 ┌─────────────────────────────────────────────────────────┐
 │ Turn 01: Greenfield Architecture & Tool Selection       │
 ├─────────────────────────────────────────────────────────┤
 │ [!] Trap 01: SWE-bench Saturation vs. Monorepo Realities│
 │ [!] Trap 05: The "Vibe Coding" Greenfield Illusion      │
 └────────────────────────────┬────────────────────────────┘
                              ▼
 ┌─────────────────────────────────────────────────────────┐
 │ Turns 02-15: Deep Reasoning & Test-Time Search          │
 ├─────────────────────────────────────────────────────────┤
 │ [!] Trap 02: Test-Time Overthinking & Solution Entropy  │
 │ [!] Trap 06: KV-Cache Thrashing & Context Recomputation │
 └────────────────────────────┬────────────────────────────┘
                              ▼
 ┌─────────────────────────────────────────────────────────┐
 │ Turns 16-45: Multi-Turn Execution & State Mutation      │
 ├─────────────────────────────────────────────────────────┤
 │ [!] Trap 03: Multi-Turn Trajectory Drift & Amnesia      │
 │ [!] Trap 04: Un-Scoped File Bleed (Chesterton's Fence)  │
 └─────────────────────────────────────────────────────────┘</code></pre>

		<section class="bias-section" id="trap-01-swe-bench">
			<h3>01. The SWE-bench saturation mirage: scaffold gaming vs. monorepo physics</h3>
			<p>
				<strong>The benchmark flaw:</strong> Static repository benchmarks were designed as a high-water mark for repository-level engineering. Frontier scores have climbed past 90% mostly through scaffolding brute-force: wrapping models in multi-candidate majority voting, specialized test harness filtering, and synthetic training on public issue structures.<sup>[1]</sup>
			</p>
			<p>
				<strong>The production reality:</strong> My production monorepos do not come with pre-packaged reproducer scripts and clean unit test harnesses. As proven in <a href="https://arxiv.org/abs/2608.27831" target="_blank" rel="noopener noreferrer">RealSWE (August 2026)</a>, when coding agents are evaluated under realistic, un-curated developer requests rather than synthetic benchmarks, resolve rates plummet because problems are under-specified and lack an automated test oracle.
			</p>
			<p>
				<strong>The failure mode:</strong> On a synthetic benchmark, the harness isolates the failing unit test and feeds the reproduction command straight to the agent. In real engineering, 80% of my labor is reproducing the issue across distributed dependencies without breaking un-monitored services.<sup>[2]</sup> Frontier evaluations have shifted to live command-line environments like <a href="https://arxiv.org/abs/2601.11868" target="_blank" rel="noopener noreferrer">Terminal-Bench (2026)</a>: models that resolve isolated git diffs stall on multi-environment CLI failures from an ambiguous ticket.
			</p>
		</section>

		<section class="bias-section" id="trap-02-overthinking">
			<h3>02. The test-time overthinking vortex: reasoning budgets vs. solution entropy</h3>
			<p>
				<strong>The benchmark flaw:</strong> Modern rankings correlate capability with test-time compute, assuming 16,000 to 32,000 thinking tokens yield deeper solutions. Past a point deliberation inverts: the model talks itself out of a correct implementation and into circular second-guessing.
			</p>
			<p>
				<strong>The production reality:</strong> Test-time search without deterministic external verification exhibits steep diminishing returns and circular reasoning traps.
			</p>
			<p>
				<strong>The failure mode:</strong> Unconstrained reasoning loops frequently enter entropy stagnation. The model spends 12,000 thinking tokens second-guessing its own hypothesis, re-reading the same file buffer, and debating trivial stylistic alternatives. I wait 25 seconds and pay for 15,000 tokens, only for the model to produce the exact same two-line fix it identified within its first 400 tokens of deliberation. This compounding token bloat was quantified in <a href="https://arxiv.org/abs/2608.01347" target="_blank" rel="noopener noreferrer">Prompt-Induced Waste in Coding Agents (August 2026)</a>, which demonstrated how unconstrained reasoning loops inflate end-to-end cloud cost without producing higher defect resolution.
			</p>
			<p>
				<strong>The physical metric: AST Density Ratio (&rho;):</strong> I measure the ratio of valid Abstract Syntax Tree (AST) modifications to total internal deliberation tokens consumed:
			</p>
			<div class="formula-container" role="region" aria-label="AST Density Ratio Formula">
				<div class="formula-equation">
					<span class="formula-var">&rho;</span>
					<span class="formula-operator">=</span>
					<div class="formula-fraction">
						<span class="numerator">&Delta; Valid AST Structure Deltas</span>
						<span class="fraction-bar"></span>
						<span class="denominator">Total Deliberation Tokens</span>
					</div>
				</div>
				<div class="formula-condition">
					When <span class="formula-var">&rho;</span> &rarr; 0, the model consumes reasoning budget without producing valid syntax tree changes.
				</div>
			</div>
		</section>

		<section class="bias-section" id="trap-03-trajectory-drift">
			<h3>03. Multi-turn trajectory drift: Turn 1 precision vs. Turn 25 amnesia</h3>
			<p>
				<strong>The benchmark flaw:</strong> Evaluation frameworks evaluate models on shallow trajectories (typically 1 to 5 turns). In <a href="https://arxiv.org/abs/2607.08964" target="_blank" rel="noopener noreferrer">Long-Horizon-Terminal-Bench (July 2026)</a>, empirical evaluations proved that agent resolve rates collapse precipitously when terminal tasks extend beyond 20 turns, as compounding state divergence overwhelms the context window.
			</p>
			<p>
				<strong>The production reality:</strong> Autonomous coding agents in my IDE and CLI harnesses execute over 30 to 60 consecutive turns.
			</p>
			<p>
				<strong>The failure mode:</strong> At Turn 3, the model exhibits crisp adherence to my system guidelines and file boundaries. By Turn 22, the accumulation of raw compiler warnings, terminal outputs, and file contents saturates the working context. The model undergoes trajectory drift:
			</p>
			<ol>
				<li>It forgets foundational constraints established in Turn 1.</li>
				<li>It begins repairing errors that were intentionally introduced as temporary scaffolding in Turn 14.</li>
				<li>It enters an infinite loop, alternating between two conflicting implementations across consecutive turns.</li>
			</ol>
		</section>

		<section class="bias-section" id="trap-04-blast-radius">
			<h3>04. Un-scoped file bleed: Chesterton's Fence over-refactoring</h3>
			<p>
				<strong>The benchmark flaw:</strong> Benchmarks reward fixing the targeted bug at all costs. They do not penalize modifying un-scoped files as long as the test suite passes.
			</p>
			<p>
				<strong>The production reality:</strong> Unconstrained modification of working code is an intolerable production risk in my repositories.
			</p>
			<p>
				<strong>The failure mode:</strong> When tasked with fixing an authentication timeout in <code>auth/session.ts</code>, an autonomous model inspects the import tree, concludes that the downstream database wrapper is sub-optimal, and refactors the database connection pool across four other files. It then deletes legacy defensive fallbacks because it mistakes historical workarounds for unused dead code (violating Chesterton's Fence). The local pull request passes compilation, but drops production traffic under specific concurrency conditions.
			</p>
		</section>

		<section class="bias-section" id="trap-05-vibe-coding">
			<h3>05. The vibe coding greenfield illusion: prototypes vs. brownfield resilience</h3>
			<p>
				<strong>The benchmark flaw:</strong> Social feeds and viral demos celebrate generating complete applications from scratch: spinning up a frontend landing page, an interactive dashboard, or a mobile prototype in a single prompt.
			</p>
			<p>
				<strong>The production reality:</strong> Greenfield generation is the easiest task in software engineering because there are zero pre-existing constraints.
			</p>
			<p>
				<strong>The failure mode:</strong> A greenfield project has no legacy dependencies, no backward-compatibility requirements, no strict IAM policies, and no concurrent schema migrations. Models that look miraculous when scaffolding a fresh prototype from zero routinely collapse when I drop them into an eight-year-old enterprise codebase governed by strict type systems, custom linter configurations, and complex security attestation gates.
			</p>
		</section>

		<section class="bias-section" id="trap-06-kv-cache">
			<h3>06. KV-cache thrashing and context invalidation economics</h3>
			<p>
				<strong>The benchmark flaw:</strong> Model pricing and throughput are quoted in static rates per 1M tokens, assuming uniform per-request costs.
			</p>
			<p>
				<strong>The production reality:</strong> In multi-turn agent loops, KV-cache read-vs-write mechanics dominate operational latency and cost. Prefix cache sharing and deterministic page reuse determine multi-turn throughput.
			</p>
			<p>
				<strong>The failure mode:</strong> When an agent executes across 40 turns, reading a 120k-token repository on every turn without deterministic prompt caching requires re-computing millions of input tokens. If an agent framework dynamically injects unstable metadata (such as timestamps, fluctuating memory summaries, or non-deterministic file trees) at the top of the prompt, it breaks the KV-cache prefix. A workflow that should have cost $0.40 and run in 30 seconds balloons into a $12.00 run with 15-second per-turn latency.
			</p>
		</section>
	</section>

	
	<section id="empirical-telemetry" data-part="PART 03" data-title="Empirical Telemetry">
		<h2>PART 03: Empirical telemetry: monolith vs. cascade topologies</h2>

		<p>
			To quantify these failure modes, I benchmarked three distinct agent topologies across 100 enterprise bug-fixing tasks in a 150k-line TypeScript monorepo with strict CI compiler gates:
		</p>

		<div class="table-container">
			<table class="data-table">
				<thead>
					<tr>
						<th>Architectural Topology</th>
						<th>1-Shot Pass Rate</th>
						<th>30-Turn Monotonicity</th>
						<th>P95 Turn Latency</th>
						<th>Mean Cost / 100 Tasks</th>
						<th>Un-Scoped Edit Rate</th>
					</tr>
				</thead>
				<tbody>
					<tr>
						<td><strong>Monolithic Frontier Tier (100% Tokens)</strong></td>
						<td>68%</td>
						<td>34% (severe drift)</td>
						<td>18.2s</td>
						<td>$48.50</td>
						<td>28% (uncontrolled edits)</td>
					</tr>
					<tr>
						<td><strong>Unchecked Fast ReAct Loop</strong></td>
						<td>42%</td>
						<td>18% (thrashing)</td>
						<td>1.4s</td>
						<td>$6.20</td>
						<td>44% (syntax/schema breaks)</td>
					</tr>
					<tr>
						<td><strong>Compiler-Bound Agent Architecture (CBAA)</strong></td>
						<td><strong>84%</strong></td>
						<td><strong>94% (monotonic)</strong></td>
						<td><strong>2.8s</strong></td>
						<td><strong>$9.10</strong></td>
						<td><strong>0% (compiler-enforced)</strong></td>
					</tr>
				</tbody>
			</table>
		</div>

		<p>
			My empirical findings are unmistakable:
		</p>

		<ul>
			<li><strong>The monolith penalty:</strong> Routing 100% of tokens to a frontier reasoning model does not prevent trajectory drift. In fact, its unconstrained reasoning capability makes it more prone to over-refactoring un-scoped files (28% un-scoped file violations).</li>
			<li><strong>The fast loop trap:</strong> Unchecked fast models suffer from syntax fragility and circular repair loops, failing 30-turn monotonicity 82% of the time.</li>
			<li><strong>The hybrid breakthrough:</strong> Binding a multi-tier cascade to deterministic AST gates yields the highest task completion (84%), near-perfect monotonicity (94%), and zero un-scoped file violations, while cutting my operational cost by 81%.</li>
		</ul>
	</section>

	
	<section id="cognitive-cascade" data-part="PART 04" data-title="CBAA Architecture">
		<h2>PART 04: The production antidote: the Compiler-Bound Agent Architecture (CBAA)</h2>

		<p>
			If public leaderboards fail to predict monorepo reliability, how do I architect autonomous production swarms?
		</p>

		<p>
			I abandon the assumption that a single frontier model should execute every stage of my software development lifecycle. In the post-leaderboard era, foundation models are not monolithic software engineers. They are stochastic inference primitives that must be bound by <strong>The Compiler-Bound Agent Architecture (CBAA)</strong>.
		</p>

		<p>
			CBAA is governed by two structural pillars: a <strong>4-Tier Cognitive Cascade</strong> and <strong>Mechanical POSIX Layer 3 Gates</strong>.
		</p>

		<h3>4A. The 4-tier cognitive cascade</h3>

		<p>
			Instead of directing 100% of tokens to an expensive, high-latency reasoning model, I decouple intelligence into functional tiers:
		</p>

		<div class="arch-diagram-container">
			<div class="arch-diagram-header">
				<span class="arch-diagram-title">The 4-Tier Cognitive Cascade</span>
				<span class="arch-diagram-subtitle">Planning • Fast Execution • Local Filtering • Binary Verification</span>
			</div>
			<div class="arch-layers-grid">
				<div class="arch-layer-card">
					<div class="arch-layer-top">
						<span class="arch-layer-badge badge-l1">TIER 01: FRONTIER REASONING</span>
						<span class="arch-layer-spec">High Deliberation</span>
					</div>
					<div class="arch-layer-name">Architecture Planning &amp; Ambiguity Resolution</div>
					<p class="arch-layer-desc">Decomposes tickets into strict ScopeManifestContracts on Turn 1 with 16k thinking budget.</p>
				</div>

				<div class="arch-flow-connector">▼ <span>Structured Task Manifest &amp; File Scope</span> ▼</div>

				<div class="arch-layer-card">
					<div class="arch-layer-top">
						<span class="arch-layer-badge badge-l2">TIER 02: FAST SERVERLESS</span>
						<span class="arch-layer-spec">Sub-Second Execution</span>
					</div>
					<div class="arch-layer-name">Multi-Turn Tool Loops &amp; Diff Synthesis</div>
					<p class="arch-layer-desc">Generates surgical edits using deterministic prefix-cached repository context with low latency.</p>
				</div>

				<div class="arch-flow-connector">▼ <span>Synthesized Candidate Diff</span> ▼</div>

				<div class="arch-layer-card">
					<div class="arch-layer-top">
						<span class="arch-layer-badge badge-l3">TIER 03: LOCAL ON-DEVICE</span>
						<span class="arch-layer-spec">&lt;50ms Edge Engine</span>
					</div>
					<div class="arch-layer-name">Client Screening &amp; Token Probing</div>
					<p class="arch-layer-desc">Performs regex hygiene, secret masking, and local cache index validation prior to network egress.</p>
				</div>

				<div class="arch-flow-connector">▼ <span>Binary Gate Validation</span> ▼</div>

				<div class="arch-layer-card">
					<div class="arch-layer-top">
						<span class="arch-layer-badge badge-l4">TIER 00: DETERMINISTIC SUBSTRATE</span>
						<span class="arch-layer-spec">POSIX Layer 3</span>
					</div>
					<div class="arch-layer-name">Compilers, Linters &amp; Unit Tests</div>
					<p class="arch-layer-desc">Hard binary evaluation (exit code 0). Rejects AST mutations and feeds diagnostics back to Tier 2.</p>
				</div>
			</div>
		</div>

		<p>
			Here is the physical state machine topology showing how tokens, tool diffs, AST invariants, and POSIX circuit breakers interlock across my unattended multi-turn sessions:
		</p>

		<pre><code>                  THE COMPILER-BOUND AGENT STATE LOOP (CBAA)
 ┌──────────────────────────────────────────────────────────────────────────┐
 │ 1. INCOMING TASK OBJECTIVE (Turn 01 / Frontier Tier)                    │
 │    Decomposes goal into ScopeManifest JSON Schema (Allowed Files &amp; APIs) │
 └────────────────────────────────────┬─────────────────────────────────────┘
                                      │  Contract Signatures Pinned (HEAD AST)
                                      ▼
 ┌──────────────────────────────────────────────────────────────────────────┐
 │ 2. MULTI-TURN CODE WORKER (Turns 02-30 / Workhorse Execution)            │
 │    Reads prefix-cached context ──► Synthesizes candidate AST tree diffs  │
 └────────────────────────────────────┬─────────────────────────────────────┘
                                      │  Pre-Disk Evaluation
                                      ▼
                       [ TIER 00: AST BOUNDARY GATE ]
                       Does proposed edit delete, mutate,
                       or leak unapproved public signatures?
                                   /      \
                   VIOLATION (Exit 2)     CLEAN (Exit 0)
                                 /          \
  ┌─────────────────────────────┴──┐      ┌──┴──────────────────────────────┐
  │ HARD AUTOMATED ROLLBACK:       │      │ LAYER 3 POSIX BUILD COMPILER    │
  │ • Immediate checkout rollback  │      │ • Syntax parse + test suites    │
  │ • Stderr trace injected to rep │      │ • Binary exit code strictly == 0│
  │ • Decrement turn budget (K&lt;=3) │      └──┬──────────────────────────────┘
  └──────────────┬─────────────────┘         │ Lexicographic Defect Monotonic
                 ▲                           ▼
                 └────── Test Failure ─ [ ACCEPT MONOREPO COMMIT (GREEN) ]</code></pre>

		<p>
			Here is how I implement this cascade in my production orchestration loops:
		</p>

		<pre><code>// CognitiveCascadeRouter.ts - Multi-Tier Swarm Orchestration Loop
export async function executeAgentLoop(task: EngineeringTask, context: RepoContext) {
  // Tier 1: High-deliberation planning strictly on Turn 1
  const scopeManifest = await tier1FrontierReasoning.plan(task, {
    thinkingBudget: 16384,
    responseSchema: ScopeManifestContract
  });

  for (let turn = 1; turn &lt;= MAX_ALLOWED_TURNS; turn++) {
    // Tier 2: High-throughput execution loop (sub-second diff synthesis)
    const proposedDiff = await tier2FastServerless.generateDiff({
      manifest: scopeManifest,
      activeContext: context.getPrefixCachedContext()
    });

    // Tier 0: Deterministic POSIX Layer 3 Gate
    const gateResult = await executeLayer3Gate(proposedDiff, scopeManifest.allowedFiles);
    if (gateResult.exitCode === 0) {
      return commitDiffToWorkspace(proposedDiff); // Clean Monotonic Green
    }

    // Pipe compiler diagnostics back into Tier 2 repair loop
    context.appendStderrDiagnostics(gateResult.stderr);
  }
  throw new Error("Agent trajectory exceeded maximum repair turns without convergence.");
}</code></pre>
		</div>
	</section>

	<section class="bias-section" id="ast-signatures-gate">
		<h3>4B. Drop-in production artifact: the AST public signature invariant gate</h3>

		<p>
			I bind agent tool execution to deterministic static checks rather than prompts or UI mocks. This drop-in script diffs public signatures between git <code>HEAD</code> and the working file:
		</p>

		<pre><code>#!/usr/bin/env python3
"""
blast_radius_gate.py - Deterministic AST Scope-Containment Gate for Agentic Swarms
Enforces Chesterton's Fence: permits internal function implementation edits,
but strictly blocks altering or deleting exported public type contracts.
"""

import sys
import ast
import subprocess
from pathlib import Path

def _signature(node, qualname: str) -&gt; str:
    """Renders a stable signature string, including keyword-only arguments."""
    a = node.args
    parts = [arg.arg for arg in a.posonlyargs] + [arg.arg for arg in a.args]
    if a.vararg:
        parts.append("*" + a.vararg.arg)
    elif a.kwonlyargs:
        parts.append("*")
    parts += [arg.arg for arg in a.kwonlyargs]
    if a.kwarg:
        parts.append("**" + a.kwarg.arg)
    return f"def {qualname}({', '.join(parts)})"

def extract_public_ast_signatures(source_code: str) -&gt; dict[str, str]:
    """Parses AST and extracts public function, class, and method signatures.

    Descends into class bodies. A gate that stops at module level records that a
    class exists but never what it promises, so renaming or re-arging a public
    method reads as clean.
    """
    if not source_code.strip():
        return {}
    tree = ast.parse(source_code)
    signatures = {}
    for node in tree.body:
        if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)) and not node.name.startswith("_"):
            signatures[node.name] = _signature(node, node.name)
        elif isinstance(node, ast.ClassDef) and not node.name.startswith("_"):
            bases = [ast.unparse(b) for b in node.bases]
            signatures[node.name] = f"class {node.name}({', '.join(bases)})"
            for sub in node.body:
                if isinstance(sub, (ast.FunctionDef, ast.AsyncFunctionDef)) and not sub.name.startswith("_"):
                    qualname = f"{node.name}.{sub.name}"
                    signatures[qualname] = _signature(sub, qualname)
    return signatures

def verify_ast_blast_radius(file_path: str) -> tuple[bool, list[str]]:
    """Asserts zero mutations to existing public API signatures."""
    head_res = subprocess.run(["git", "show", f"HEAD:{file_path}"], capture_output=True, text=True)
    if head_res.returncode != 0:
        return True, [] # Brand new file

    with open(file_path, "r", encoding="utf-8") as f:
        current_code = f.read()

    head_sigs = extract_public_ast_signatures(head_res.stdout)
    current_sigs = extract_public_ast_signatures(current_code)

    violations = []
    for symbol, base_sig in head_sigs.items():
        if symbol not in current_sigs:
            violations.append(f"DELETED: Public contract '{symbol}' was removed by the agent.")
        elif current_sigs[symbol] != base_sig:
            violations.append(f"MUTATED: '{symbol}' changed from `{base_sig}` to `{current_sigs[symbol]}`")

    return len(violations) == 0, violations

if __name__ == "__main__":
    targets = sys.argv[1:]
    has_error = False
    for target in targets:
        passed, violations = verify_ast_blast_radius(target)
        if not passed:
            has_error = True
            for v in violations:
                print(f"[AST GATE BLOCKED] {target}: {v}", file=sys.stderr)

    if has_error:
        print("\nAction: Hard rollback enforced. Feeding AST violation to Tier 2 repair loop.", file=sys.stderr)
        sys.exit(1)
    sys.exit(0)</code></pre>
		</div>

		<p>
			Below, that exact file runs against an agent edit that fixes its ticket
			and quietly widens two public method signatures. The suite goes green.
			The gate does not.
		</p>

		<p><a href="https://ulukaya.dev/posts/leaderboard-mirage-llm-ranking-traps">Video: Scope bleed caught by blast_radius_gate.py while the tests stay green. Watch it in the essay.</a></p>
	</section>

	<section class="bias-section" id="posix-verification">
		<h3>4C. Closed-loop POSIX verification</h3>

		<p>
			Every proposed diff in my pipeline passes through external, deterministic gates before disk commits are permitted:
		</p>

		<pre><code>[Agent Code Generation Payload]
               │
               ▼
    [POSIX Layer 3 Gate]
    ├── 1. Scope Containment Audit: blast_radius_gate.py (AST Signatures)
    ├── 2. Syntax Validation: AST parser check (Tree-sitter)
    ├── 3. Type Checking: Static compiler check (tsc / mypy)
    └── 4. Regression Gate: Targeted unit test execution
               │
       ┌───────┴───────┐
       ▼               ▼
 [Exit Code 0]   [Exit Code != 0]
  (Commit Diff)   (Pipe compiler stderr back to Tier 2 repair loop)</code></pre>

		<p>
			A compiler has no opinion on benchmark leaderboards. It does not read marketing claims or social media screenshots. It evaluates the Abstract Syntax Tree against strict language rules and returns a binary exit code.
		</p>
	</section>

	
	<section id="production-tooling" data-part="PART 05" data-title="Production Tooling">
		<h2>PART 05: Production tooling, telemetry, and automated gates</h2>

		<p>
			Rather than relying on speculative toy sliders or synthetic scoring benchmarks, my production agent pipelines require concrete telemetry and deterministic mechanical gates. In production, I deploy three interlocking verification tools:
		</p>

		<div class="arch-diagram-container">
			<div class="arch-diagram-header">
				<span class="arch-diagram-title">Production Tooling & Telemetry Suite</span>
				<span class="arch-diagram-subtitle">Deterministic Gates • Economic Solvency • Contract Grounding</span>
			</div>
			<div class="arch-layers-grid">
				<div class="arch-layer-card">
					<div class="arch-layer-top">
						<span class="arch-layer-badge badge-l1">LAYER 01: GATE</span>
						<span class="arch-layer-spec">POSIX Layer 3</span>
					</div>
					<div class="arch-layer-name">Deterministic AST Gate (<code>blast_radius_gate.py</code>)</div>
					<p class="arch-layer-desc">
						Enforces Chesterton's Fence at the pre-commit boundary: permits internal function implementation refactors while mechanically rejecting any alteration or deletion of exported public API contracts.
					</p>
				</div>

				<div class="arch-flow-connector">▼ <span>Economic Boundary Check</span> ▼</div>

				<div class="arch-layer-card">
					<div class="arch-layer-top">
						<span class="arch-layer-badge badge-l2">LAYER 02: SOLVENCY</span>
						<span class="arch-layer-spec">Inference Tokenomics</span>
					</div>
					<div class="arch-layer-name">AI Tokenomics Calculator (<a href="https://ulukaya.dev/instruments#calculators">Instruments</a>)</div>
					<p class="arch-layer-desc">
						Computes prompt cache hit rates, multi-turn KV-cache growth curves, and spend-cap circuit breakers to model true production inference solvency before deploying autonomous agent loops.
					</p>
				</div>

				<div class="arch-flow-connector">▼ <span>Contract Grounding</span> ▼</div>

				<div class="arch-layer-card">
					<div class="arch-layer-top">
						<span class="arch-layer-badge badge-l3">LAYER 03: SCHEMAS</span>
						<span class="arch-layer-spec">noVibes Architecture</span>
					</div>
					<div class="arch-layer-name">Agent Spec Generator (<a href="https://ulukaya.dev/instruments#generators">Instruments</a>)</div>
					<p class="arch-layer-desc">
						Scaffolds deterministic, repository-native <code>agents/spec/</code> trees inspired by Ali Afshar's noVibes standard, replacing fuzzy system prompts with verifiable schema contracts.
					</p>
				</div>
			</div>
		</div>
	</section>

	<section class="bias-section" id="engineering-litmus-test">
		<h3>06. The 2026 engineering litmus test</h3>

		<p>
			When I evaluate new foundation models for my production agent systems, I replace public benchmark charts with this four-part checklist:
		</p>

		<ol>
			<li><strong>Audit the AST-to-token density:</strong> Does allocating additional reasoning tokens improve the quality of the diff, or does the model exhaust compute in circular rationalizations?</li>
			<li><strong>Test 30-turn trajectory monotonicity:</strong> Run the model through an extended multi-turn debugging harness. Does it converge on a solution, or does it begin regressing after Turn 15?</li>
			<li><strong>Enforce strict scope containment:</strong> When instructed to modify a specific interface, does the model constrain its mutations to the declared files, or does it attempt to refactor surrounding packages?</li>
			<li><strong>Measure KV-cache prefix stability:</strong> Does the model provider support deterministic prompt caching, and what is the latency penalty across a 40-turn execution loop?</li>
		</ol>

		<p>
			Stop evaluating models as if they were essayists in a conversational arena. In production, models are stochastic components inside distributed software systems. I design architectures where models are expected to fail, and I enforce mechanical boundaries that ensure my production software never does.
		</p>
	</section>

	
	<section class="bias-section" id="references">
		<h2>Industry validation and benchmarks</h2>
		<p>
			Recent 2026 empirical studies confirm the structural divergence between static public leaderboards and real-world production agent reliability:
		</p>
		<ul>
			<li>
				<strong><a href="https://arxiv.org/abs/2609.05227v1" target="_blank" rel="noopener noreferrer">CABAL: Multi-Agent Simulacra for Tracing Collusive Bias in Evaluation (Sep 2026)</a>:</strong> Confirms that static evaluation leaderboards are vulnerable to systemic ranking distortion and require dynamic adversarial probing.
			</li>
			<li>
				<strong><a href="https://arxiv.org/abs/2608.27021v1" target="_blank" rel="noopener noreferrer">FaulT-Bench: Towards Benchmarking Network Troubleshooting LLM Agents under Unreliable Inputs (Aug 2026)</a>:</strong> Confirms that models scoring over 90% on clean static benchmarks degrade sharply when evaluated on perturbed, real-world production inputs.
			</li>
			<li>
				<strong><a href="https://arxiv.org/abs/2608.13867" target="_blank" rel="noopener noreferrer">Engineering Reliable Coding Agents (Aug 2026)</a>:</strong> Demonstrates that multi-turn agent reliability depends on physical boundary enforcement and deterministic compiler gates rather than static single-turn ELO scores.
			</li>
			<li>
				<strong><a href="https://arxiv.org/abs/2608.27831" target="_blank" rel="noopener noreferrer">RealSWE: A Compositional Evaluation of Coding Agents under Realistic User Requests (Aug 2026)</a>:</strong> Proves that resolve rates drop precipitously under realistic developer tickets lacking pre-packaged test harnesses.
			</li>
		</ul>
	</section>

<h2>Notes</h2>
<ol>
<li value="1">A scaffold is everything around the model: the retry loop, the candidate sampler, the test filter. It is the part a leaderboard row does not name, and the part you do not get for free in your own repository.</li>
<li value="2">My own rough split across a year of production triage, not a measured study. The point survives a wide error bar: reproduction dominates, and it is the one phase the benchmark hands the agent for free.</li>
</ol>]]></content:encoded>
			<pubDate>Tue, 01 Sep 2026 00:00:00 GMT</pubDate>
			<dc:creator><![CDATA[Ibrahim Ulukaya]]></dc:creator>
			<category><![CDATA[AI Benchmarks]]></category>
			<category><![CDATA[Systems Architecture]]></category>
			<category><![CDATA[Agent Architecture]]></category>
			<category><![CDATA[Model Cascades]]></category>
			<category><![CDATA[Evaluation]]></category>
			<category><![CDATA[AIBuilders]]></category>
		</item>
		<item>
			<title><![CDATA[Code Over Context: Why Written Agent Skills Break in Production]]></title>
			<link>https://ulukaya.dev/posts/code-over-context</link>
			<guid isPermaLink="true">https://ulukaya.dev/posts/code-over-context</guid>
			<description><![CDATA[10 markdown skill files cost my agent 22,000 tokens per turn and broke on smaller models at turn 4. I distill written skills into deterministic code tools.]]></description>
			<content:encoded><![CDATA[<section id="introduction" data-part="INTRO" data-title="Introduction">
		<p class="lead-paragraph">
			When I loaded 10 markdown skill files into my agent system prompt, my harness burned 22,000 input tokens on every turn before I typed a single character. Worse, when I ran that same markdown prompt on smaller models, instruction-following fidelity collapsed at turn 4.
		</p>
		<p><em>Figure 1.</em> The same rule as prose in AGENTS.md costs 12,822 input tokens a turn and is not enforced; as a 180-token schema plus a gate script it costs 180 and exits 1 on a broken file. <a href="https://ulukaya.dev/posts/code-over-context">View the figure in the essay.</a></p>
		<p>
			Prompt-heavy skills forced my probabilistic models to emulate state machines, validate schemas, and calculate token quotas in natural language. That design maximized latency and inference cost ($0.11 per turn at frontier pricing) while destroying execution reliability. To build scalable autonomous systems, I distill prompt-heavy agent skills into deterministic code tools with near-zero token overhead ($0.00 in prompt bloat).
		</p>
		
		<blockquote><strong>My core thesis:</strong> Stop writing prompts for what code can guarantee. I use frontier models to explore and distill complex workflows into deterministic code-first tools, then execute those tools with sub-millisecond latency on fast serverless and on-device runtimes.</blockquote>
	</section>

	
	<h2>PART 01: The context trap and model fragility</h2>

	<section class="bias-section" id="prompt-bloat">
		<h3>01. The prompt bloat dilemma</h3>
		<p>
			As I expanded my agent capabilities, I accumulated markdown instruction files describing CLI flags, formatting rules, and error recovery procedures. In my early agent harness, I injected these files into the system prompt on every turn.
		</p>
		<p>
			That practice introduced three severe production bottlenecks in my workloads:
		</p>
		<ul>
			<li><strong>Compounding token tax:</strong> Loading 22,000 tokens of markdown instructions across a 20-turn session consumed 440,000 input tokens, inflating my API billing linearly with conversation length.</li>
			<li><strong>Attention degradation:</strong> As my context window filled beyond 70% KV cache capacity, my models suffered from severe attention decay, missing critical constraints buried in middle paragraphs.</li>
			<li><strong>Non-deterministic drift:</strong> Natural language instructions act as probabilistic suggestions. My models occasionally skipped validation steps, invented non-existent CLI parameters, or formatted outputs inconsistently.</li>
		</ul>
		<p>
			Recent 2026 tool-attention research confirms what I measured in production: eager prompt skill injection consumes 10,000 to 60,000 tokens per turn and fractures multi-step reasoning once KV cache utilization crosses 70%, whereas lazy tool schema loading cuts token overhead by 95.0%.
		</p>
	</section>

	<section class="bias-section" id="light-model-failure">
		<h3>02. Why written skills fail on light models</h3>
		<p>
			Written skills created an invisible dependency on <a href="https://ulukaya.dev/posts/leaderboard-mirage-llm-ranking-traps">massive frontier models</a> in my stack. While frontier reasoning models had enough cognitive capacity to follow my multi-step instructions despite prompt ambiguity, smaller runtimes failed immediately.
		</p>
		<p>
			When I attempted to deploy my markdown-based agent across lighter runtimes, my system broke down in two distinct ways:
		</p>
		<ul>
			<li><strong>On-device runtimes:</strong> Local mobile and browser models operate under constrained context windows (often 2K to 8K tokens) and strict local compute budgets. They could not ingest ten pages of markdown rules while maintaining conversational state.</li>
			<li><strong>Fast serverless endpoints:</strong> While lightweight cloud models offer expansive context windows, stuffing them with markdown skills triggered attention degradation at turn 4, inflated per-turn latency, and multiplied my token costs across multi-turn sessions.</li>
		</ul>
		<p>
			If my agent architecture requires a frontier model just to parse a timestamp or validate a JSON payload, my system is economically and architecturally fragile.
		</p>
	</section>

	
	<h2>PART 02: The elastic cognitive envelope</h2>

	<section class="bias-section" id="exoskeleton-vs-brain">
		<h3>03. The exoskeleton vs. the brain</h3>
		<p>
			To build resilient agents that operate reliably across model tiers, I decouple the cognitive reasoning layer from the deterministic execution layer. I formalize this separation as my <strong>Elastic Cognitive Envelope</strong>:
		</p>
		<ul>
			<li><strong>The brain (probabilistic reasoning):</strong> Intent classification, ambiguous user goal decomposition, creative synthesis, and high-level strategy. I keep this layer inside the model.</li>
			<li><strong>The exoskeleton (deterministic code):</strong> State machines, schema validation, arithmetic calculations, API authentication, and file system mutations. I enforce this layer in compiled or interpreted code.</li>
		</ul>
		<p>
			Deterministic code eliminates prompt token bloat, executes in milliseconds, and gives me 100% unit test coverage. By moving operational invariants into code, I shrink my system prompt from thousands of lines to a concise 240-token tool schema.
		</p>
		<p>
			Depending on security, compute, and platform constraints, I deploy this deterministic exoskeleton across three architectural topologies:
		</p>
		<ul>
			<li><strong>In-process execution:</strong> In my local developer tooling and CLI agents, I run the exoskeleton directly in the host process (as shown in my git engine below), executing system commands with zero network overhead.</li>
			<li><strong>Client-side runtimes:</strong> In my mobile and web applications, I call Gemini models through client SDKs such as <a href="https://firebase.google.com/docs/ai-logic" target="_blank" rel="noopener noreferrer">Firebase AI Logic</a>, allowing my client application to execute local on-device tools directly in-process.</li>
			<li><strong>Stateless serverless services:</strong> When my tools require private credentials, heavy dependencies, or privileged infrastructure, I package the exoskeleton as stateless containerized microservices on <a href="https://cloud.google.com/run/docs" target="_blank" rel="noopener noreferrer">Google Cloud Run</a>. When exposing these endpoints to client applications, I secure the boundary with cryptographic attestation via <a href="https://firebase.google.com/docs/app-check" target="_blank" rel="noopener noreferrer">Firebase App Check</a> to block unauthorized invocations.</li>
		</ul>
	</section>

	<section class="bias-section" id="distillation-flywheel">
		<h3>04. The skill distillation flywheel</h3>
		<p>
			To transition from written prompt instructions to deterministic code, I run a three-stage distillation workflow:
		</p>
		<ol>
			<li><strong>Stage 1 (frontier exploration):</strong> I use a frontier model with extended thinking to explore an ambiguous problem space, interact with APIs, and discover edge cases.</li>
			<li><strong>Stage 2 (code distillation):</strong> Once my workflow stabilizes, I instruct the frontier model to synthesize the multi-turn interaction into a typed script with strict input and output schemas.</li>
			<li><strong>Stage 3 (light runtime deployment):</strong> I expose the distilled script as a single tool call. Fast serverless and on-device models invoke my tool with 240 tokens of schema overhead and zero execution drift.</li>
		</ol>
	</section>

	
	<h2>PART 03: Implementation and benchmarks</h2>

	<p>
		To see how distilling markdown skills into typed code impacts runtime resources, test my simulator below. Toggle between prompt-heavy markdown instructions and code-first execution across model tiers to inspect prompt bloat, latency, and operational inference cost:
	</p>

	<p><a href="https://ulukaya.dev/posts/code-over-context#lab-skill-distill">Interactive lab: skill-distill. Open the essay to run it.</a></p>
	<p><a href="https://ulukaya.dev/posts/code-over-context">Video: IDE Proof: the agent measures 12,822 tokens/turn for AGENTS.md vs 180 for the schema, then the import coverage gate exits 1. Watch it in the essay.</a></p>

	<section class="bias-section" id="reference-code">
		<h3>05. TypeScript distilled tool contract</h3>
		<p>
			The following implementation shows my distilled tool harness. Instead of injecting a 500-line markdown guide on git branching and commit hygiene, my agent invokes a typed function that enforces invariants deterministically:
		</p>

		<pre><code>import &#123; z &#125; from 'zod';
import &#123; execFileSync &#125; from 'node:child_process';

// 1. Strict input schema replaces 40 lines of prompt formatting rules
export const CommitActionSchema = z.object(&#123;
  branch: z.string().regex(/^[A-Za-z0-9_/-]+$/, 'Invalid branch name format').refine(b =&gt; !b.startsWith('-'), 'Branch name cannot start with a dash'),
  message: z.string().min(10).max(72),
  files: z.array(z.string().refine(f =&gt; !f.startsWith('/') &amp;&amp; !f.includes('..'), 'Must be relative path inside repository')).nonempty(),
  signoff: z.boolean().default(true),
&#125;);

export type CommitAction = z.infer&lt;typeof CommitActionSchema&gt;;

// 2. Deterministic execution engine replaces multi-turn prompt retries
export class DistilledGitEngine &#123;
  public static execute(action: CommitAction): &#123; success: boolean; hash?: string; error?: string &#125; &#123;
    try &#123;
      // Validate schema contracts before touching disk
      const validated = CommitActionSchema.parse(action);

      // Safe branch checkout without destructive overwrite
      try &#123;
        execFileSync('git', ['checkout', validated.branch]);
      &#125; catch &#123;
        execFileSync('git', ['checkout', '-b', validated.branch]);
      &#125;

      // Prevent CLI argument injection via '--' delimiter
      execFileSync('git', ['add', '--', ...validated.files]);

      const args = ['commit', '-m', validated.message];
      if (validated.signoff) args.push('--signoff');
      execFileSync('git', args, &#123; encoding: 'utf8' &#125;);

      // Retrieve commit hash deterministically rather than parsing stdout
      const hash = execFileSync('git', ['rev-parse', 'HEAD'], &#123; encoding: 'utf8' &#125;).trim();

      return &#123;
        success: true,
        hash,
      &#125;;
    &#125; catch (err: any) &#123;
      // Return structured, actionable error instead of raw stack trace
      return &#123;
        success: false,
        error: err.message || 'Execution failed',
      &#125;;
    &#125;
  &#125;
&#125;</code></pre>
		</div>
	</section>

	<section class="bias-section" id="tradeoff-matrix">
		<h3>06. Architectural trade-off matrix</h3>
		<p>
			When I evaluate whether a capability belongs in a written skill or a distilled code-first tool, I compare the operational trade-offs across five dimensions:
		</p>

		<div class="table-container">
			<table class="data-table">
				<thead>
					<tr>
						<th>Dimension</th>
						<th>Prompt-Heavy Written Skill</th>
						<th>Distilled Code-First Tool</th>
						<th>Hybrid Distillation Pattern</th>
					</tr>
				</thead>
				<tbody>
					<tr>
						<td><strong>Context overhead</strong></td>
						<td>22,000 tokens per turn (10 skill files)</td>
						<td>240 tokens (schema only)</td>
						<td>240 tokens (schema only)</td>
					</tr>
					<tr>
						<td><strong>Execution latency</strong></td>
						<td>1,100 to 2,800 ms per turn</td>
						<td>12 ms</td>
						<td>12 ms (deterministic code)</td>
					</tr>
					<tr>
						<td><strong>Model tier support</strong></td>
						<td>Frontier reasoning models only</td>
						<td>All tiers (On-Device, Workhorse, Frontier)</td>
						<td>Frontier for authoring, Workhorse for runtime</td>
					</tr>
					<tr>
						<td><strong>Reliability</strong></td>
						<td>Probabilistic (collapses at turn 4 on light models)</td>
						<td>Deterministic (100%)</td>
						<td>100% verified via unit tests</td>
					</tr>
					<tr>
						<td><strong>Authoring velocity</strong></td>
						<td>Fast initial draft</td>
						<td>Requires manual engineering</td>
						<td>Fast (frontier model synthesizes code)</td>
					</tr>
				</tbody>
			</table>
		</div>
	</section>

	<section class="bias-section" id="references">
		<h2>Industry validation and benchmarks</h2>
		<ul>
			<li>
				<a href="https://arxiv.org/abs/2604.21816" target="_blank" rel="noopener noreferrer">Tool Attention: Lazy Gated MCP Schema Loading (Apr 2026)</a>: Confirms that eager prompt skill injection consumes 10K to 60K tokens per turn and fractures reasoning at around 70% KV cache, whereas lazy tool distillation cuts token overhead by 95.0%.
			</li>
			<li>
				<a href="https://arxiv.org/abs/2609.04681v1" target="_blank" rel="noopener noreferrer">Beyond Code Generation: Reliability, Verification, and Cost Economics in the Agentic Software Development Lifecycle (Sep 2026)</a>: Confirms that replacing prompt-based verification with deterministic code harnesses reduces multi-turn agent failure rates and token cost.
			</li>
		</ul>
	</section>]]></content:encoded>
			<pubDate>Tue, 25 Aug 2026 00:00:00 GMT</pubDate>
			<dc:creator><![CDATA[Ibrahim Ulukaya]]></dc:creator>
			<category><![CDATA[AI Agents]]></category>
			<category><![CDATA[Agent Architecture]]></category>
			<category><![CDATA[Gemini]]></category>
			<category><![CDATA[On-Device AI]]></category>
			<category><![CDATA[Code Generation]]></category>
			<category><![CDATA[AIBuilders]]></category>
		</item>
		<item>
			<title><![CDATA[The Hybrid AI Standard: Routing Between On-Device AI and Cloud Run]]></title>
			<link>https://ulukaya.dev/posts/the-hybrid-ai-standard</link>
			<guid isPermaLink="true">https://ulukaya.dev/posts/the-hybrid-ai-standard</guid>
			<description><![CDATA[Cloud added 300 ms a prompt; on-device froze my app for 2.5 seconds. I route Gemini Nano and Gemini 3.1 Pro with Firebase AI Logic, App Check, and Cloud Run.]]></description>
			<content:encoded><![CDATA[<section id="introduction" data-part="INTRO" data-title="Introduction">
		<p class="lead-paragraph">
			When I started shipping AI features to production mobile and web clients, I hit the exact same physical wall from two opposite directions. When I routed every prompt to a cloud LLM, 300 ms of cellular round-trip latency ruined my interactive UI and scaled my token bill linearly with active users. When I moved inference entirely on-device, the moment a user switched apps, the mobile OS evicted my model weights from DRAM, freezing my app for 2.5 seconds when they returned.
		</p>

		<p><em>Figure 1.</em> Neither pure architecture survives contact with a real client. Routing every prompt to the cloud pays 300 ms of round-trip latency per keystroke and scales the token bill with active users; moving everything on-device stalls 2.5 seconds when the OS evicts the weights during an app switch. The router sits between them and decides per turn, falling back to the cloud when local weights are gone. <a href="https://ulukaya.dev/posts/the-hybrid-ai-standard">View the figure in the essay.</a></p>
		<p>
			Pure cloud and pure on-device architectures both fail under real-world client constraints. I architected <strong>The Hybrid AI Standard</strong> to solve this in my own applications: execute high-frequency, privacy-sensitive tasks locally on-device while progressively escalating complex reasoning and enterprise RAG to stateless serverless cloud containers.
		</p>
		
		<blockquote><strong>The Hybrid AI Standard:</strong> Execute high-frequency, privacy-sensitive tasks locally on-device while escalating complex reasoning and enterprise data queries to stateless serverless cloud containers.</blockquote>
	</section>

	
	<h2>PART 01: What broke in my terminal and the four pillars</h2>

	<section class="bias-section">
		<h3>01. Why pure cloud and pure on-device both broke in production</h3>
		<p>
			When I inspected my network traces on mobile cellular connections, sending every user interaction across the wire added 200 to 500 ms of TLS and HTTP handshake overhead before the first token even rendered. For real-time autocomplete, UI state classification, or input validation, that round-trip latency exceeded my UI responsiveness budget. Worse, when I tried <a href="https://ulukaya.dev/posts/the-leaky-abstraction-vol2">synchronous split streaming</a> between client and server, mobile packet jitter caused constant pipeline stalls.
		</p>
		<p>
			Moving everything to a local sub-billion parameter model solved my network latency, but exposed a severe mobile OS memory trap. Mobile operating systems aggressively reclaim memory during multitasking. Whenever I switched away from my app for 60 seconds, the OS memory manager evicted my local LLM weights and KV cache from DRAM. Reloading those weights from flash storage caused a multi-second Time-to-First-Token (TTFT) freeze. Furthermore, my local model had zero access to centralized production databases and could not enforce server-side billing quotas.
		</p>
	</section>

	<section class="bias-section" id="four-pillars">
		<h3>02. The four physical pillars I use to route workloads</h3>
		<p>
			To stop guessing which prompts belong on client silicon versus cloud containers, I evaluate every feature against four physical mechanics:
		</p>
		<ul>
			<li><strong>Low Latency (&lt;40 ms TTFT):</strong> I run UI state classification, intent detection, and inline autocomplete locally on client NPU silicon at 0 ms network RTT.</li>
			<li><strong>Zero Marginal Token Cost:</strong> I absorb high-frequency, low-entropy interactions on the user's device rather than paying per-token cloud inference on every keystroke.</li>
			<li><strong>Local Data Privacy:</strong> I sanitize personally identifiable information (PII) locally on-device before any context crosses the network boundary.</li>
			<li><strong>Multitasking and Offline Resilience:</strong> My core UX stays functional offline. When the mobile OS evicts my local weights from DRAM during multitasking, my router detects the cold-start state and automatically falls back to a serverless cloud endpoint while local memory restores in the background.</li>
		</ul>
	</section>

	
	<h2>PART 02: My hybrid orchestration backbone</h2>

	<section class="bias-section">
		<h3>03. Client-side SDKs and stateless serverless containers</h3>
		<p>
			When my on-device model (accessed via browser or mobile NPU runtimes like the <a href="https://developer.chrome.com/docs/ai/built-in" target="_blank" rel="noopener noreferrer">Chrome Built-in AI Prompt API</a>) hits a reasoning wall, drops below my confidence threshold, or needs enterprise data, my client escalates the request to the cloud. I structure this backbone by pairing a lightweight client-side AI SDK with stateless, autoscaling serverless containers (such as <strong>Firebase AI Logic</strong> paired with <strong>Google Cloud Run</strong>).
		</p>
		<p>
			In my deployment topology, the client SDK handles payload serialization, token streaming, and automatic retries. Downstream <a href="https://cloud.google.com/run/docs" target="_blank" rel="noopener noreferrer">Google Cloud Run</a> containers scale from zero to absorb burst escalations, running retrieval-augmented generation (RAG) over my production databases before invoking cloud models.
		</p>
	</section>

	<section class="bias-section" id="security-tokenomics">
		<h3>04. Cryptographic client attestation and rate limiting</h3>
		<p>
			Exposing my cloud AI endpoints directly to client apps without hardware and identity verification invites automated scraping and token drainage. I enforce two non-negotiable guardrails at the edge:
		</p>
		<p>
			First, I use cryptographic client attestation (<a href="https://firebase.google.com/docs/app-check" target="_blank" rel="noopener noreferrer">Firebase App Check</a> via Apple App Attest, Android Play Integrity, or reCAPTCHA Enterprise) to verify that every request originates from my authentic, untampered app binary. Unverified traffic drops at the edge with <code>HTTP 401 Unauthorized</code>.
		</p>
		<p>
			Second, when calling Firebase AI Logic from the client SDK, Firebase Auth credentials and App Check tokens are injected automatically, enforcing per-user rate limits and preventing any single client from draining my project quota.
		</p>
	</section>

	
	<h2>PART 03: Reference architecture and trade-offs</h2>

	<p>
		To evaluate client-versus-cloud routing behavior interactively, use the simulator below. Adjust network RTT, token uncertainty thresholds, and DRAM eviction states to observe how the client transitions between local NPU silicon and serverless cloud endpoints:
	</p>

	<p><a href="https://ulukaya.dev/posts/the-hybrid-ai-standard#lab-hybrid-router">Interactive lab: hybrid-router. Open the essay to run it.</a></p>

	<p>
		The simulator above uses numbers I picked. The lab below uses numbers your browser measures. Start your camera and it times four legs on a single frame: the grab into a canvas, the JPEG encode, a one-line caption from the local model if your browser has one, and a same-size upload to this server. The upload carries random bytes, not the frame, so camera pixels never leave your device. It does not call a hosted model; that leg would add inference time on top of the upload.
	</p>

	<p><a href="https://ulukaya.dev/posts/the-hybrid-ai-standard#lab-webcam-latency">Interactive lab: webcam-latency. Open the essay to run it.</a></p>

	<p><a href="https://ulukaya.dev/posts/the-hybrid-ai-standard#lab-biomorphic-reflex">Interactive lab: biomorphic-reflex. Open the essay to run it.</a></p>

	<p><a href="https://ulukaya.dev/posts/the-hybrid-ai-standard">Video: HybridAIRouter On-Device NPU to Firebase AI Logic Failover Proof. Watch it in the essay.</a></p>

	<section class="bias-section">
		<h3>05. My TypeScript hybrid routing engine</h3>
		<p>
			Here is the production TypeScript router I built (<code>HybridAIRouter</code>). It reuses a persistent on-device NPU session (preventing VRAM thrashing), checks runtime availability, and automatically escalates to Firebase AI Logic when enterprise context is required or local DRAM is evicted:
		</p>

		<pre><code>import { initializeApp } from 'firebase/app';
import { initializeAppCheck, ReCaptchaEnterpriseProvider } from 'firebase/app-check';
import { getAI, getGenerativeModel, GoogleAIBackend } from 'firebase/ai';

// App Check is automatically enforced on all Firebase AI Logic calls
const app = initializeApp({ projectId: 'my-production-app', appId: '1:12345:web:abcdef' });
initializeAppCheck(app, {
  provider: new ReCaptchaEnterpriseProvider('RECAPTCHA_SITE_KEY'),
  isTokenAutoRefreshEnabled: true,
});

const ai = getAI(app, { backend: new GoogleAIBackend() });

export interface RoutingResult {
  text: string;
  tier: 'on-device' | 'cloud';
}

export class HybridAIRouter {
  private localSession: any = null;
  private cloudModel = getGenerativeModel(ai, { model: 'gemini-3.6-flash' });

  public async execute(prompt: string, requiresEnterpriseContext = false): Promise {
    // 1. Attempt On-Device Tier if task is local and NPU/WebGPU model is warm in memory
    if (!requiresEnterpriseContext && 'LanguageModel' in self) {
      try {
        // @ts-ignore - Standard Web AI / Chrome Built-in AI Prompt API
        const availability = await LanguageModel.availability();
        if (availability === 'readily') {
          // Reuse persistent NPU session to prevent VRAM/KV-cache thrashing
          this.localSession ??= await LanguageModel.create();
          const text = await this.localSession.prompt(prompt);
          return { text, tier: 'on-device' };
        }
      } catch (err) {
        // OS DRAM eviction or NPU busy state falls through to Firebase AI Logic
        console.warn('Local NPU session evicted from DRAM; escalating to cloud', err);
        this.localSession = null;
      }
    }

    // 2. Cloud Escalation via Firebase AI Logic SDK (App Check & Auth injected automatically)
    const result = await this.cloudModel.generateContent(prompt);
    return { text: result.response.text(), tier: 'cloud' };
  }
}</code></pre>
		</div>
	</section>

	<section class="bias-section" id="tradeoff-matrix">
		<h3>06. Architectural decision matrix</h3>
		<p>
			I use this empirical decision matrix to audit latency, offline resilience, and token spend across execution environments:
		</p>

		<div class="table-container">
			<table class="data-table">
				<thead>
					<tr>
						<th>Dimension</th>
						<th>Pure On-Device</th>
						<th>Pure Cloud</th>
						<th>The Hybrid AI Standard</th>
					</tr>
				</thead>
				<tbody>
					<tr>
						<td><strong>Average Latency</strong></td>
						<td>15 to 40 ms (warm) / 2.5 s freeze (DRAM eviction)</td>
						<td>250 to 600 ms (WAN RTT bound)</td>
						<td>15 to 40 ms (UI) / 250 ms (Reasoning &amp; RAG)</td>
					</tr>
					<tr>
						<td><strong>Marginal Token Cost</strong></td>
						<td>$0.00 (Client NPU silicon)</td>
						<td>Linear with active users</td>
						<td>70 to 80% reduction in cloud token spend</td>
					</tr>
					<tr>
						<td><strong>Data Privacy</strong></td>
						<td>100% Local</td>
						<td>Raw prompt egress over wire</td>
						<td>PII sanitized locally; enterprise RAG in cloud</td>
					</tr>
					<tr>
						<td><strong>Offline Availability</strong></td>
						<td>Fully functional (within memory bounds)</td>
						<td>Fails completely</td>
						<td>Core UX remains functional; graceful cloud fallback</td>
					</tr>
				</tbody>
			</table>
		</div>
	</section>

	<section class="bias-section" id="references">
		<h2>Industry validation and benchmarks</h2>
		<p>
			Recent 2026 systems research confirms that the broader industry is hitting these exact same physical bottlenecks across cellular WANs and mobile operating systems:
		</p>
		<ul>
			<li><a href="https://arxiv.org/abs/2608.28726v1" target="_blank" rel="noopener noreferrer">Pro-Router: Token-Aware Progressive Model Routing with Adaptive Edge-Cloud Collaboration (Gui et al., Aug 2026)</a>: Confirms that one-shot pre-generation routers fail when prompts hit mid-stream reasoning walls. Monitoring token sampling probability distributions during generation achieves over 10x faster routing speed and 75% higher throughput than static request routers.</li>
			<li><a href="https://arxiv.org/abs/2609.02514v1" target="_blank" rel="noopener noreferrer">AceSpec: An Asymmetric Edge-Cloud Collaborative Framework for Communication-Efficient LLM Inference (Zhang et al., Sep 2026)</a>: Confirms that synchronous edge-cloud token verification collapses under mobile packet jitter. Replacing lock-step verification with an asymmetric local state cache delivers a 3.52x throughput speedup down to 50 Kbps WAN conditions.</li>
			<li><a href="https://arxiv.org/abs/2609.01338v1" target="_blank" rel="noopener noreferrer">mzCache: On-Device LLM Memory Management under Multitasking (Yu et al., Sep 2026)</a>: Confirms that mobile OS app switching evicts LLM weights and KV caches from DRAM. Partitioning model memory into shared GPU/CPU restoration buffers cuts post-eviction TTFT freezes by 2.1x to 5.5x.</li>
			<li><a href="https://arxiv.org/abs/2607.13093v4" target="_blank" rel="noopener noreferrer">Efficient and Privacy-Aware Edge-Cloud Collaborative Inference for Large Language Models (Li et al., Jul 2026)</a>: Confirms that sanitizing PII tokens locally on-device before synchronizing an authenticated KV cache with cloud containers reduces downlink payload bytes by 67.4% and per-token latency by 46.1%.</li>
			<li><a href="https://developer.chrome.com/docs/ai/built-in" target="_blank" rel="noopener noreferrer">Chrome Built-in AI and Prompt API Documentation</a>: Standard client-side on-device model execution via browser NPU APIs referenced in Section 03 and Section 05.</li>
			<li><a href="https://cloud.google.com/run/docs" target="_blank" rel="noopener noreferrer">Google Cloud Run Documentation</a>: Stateless serverless container execution for cloud reasoning backends referenced in Section 03.</li>
			<li><a href="https://firebase.google.com/docs/app-check" target="_blank" rel="noopener noreferrer">Firebase App Check Documentation</a>: Cryptographic client attestation securing cloud endpoints against unauthorized traffic referenced in Section 04.</li>
		</ul>
	</section>]]></content:encoded>
			<pubDate>Sat, 22 Aug 2026 00:00:00 GMT</pubDate>
			<dc:creator><![CDATA[Ibrahim Ulukaya]]></dc:creator>
			<category><![CDATA[Firebase]]></category>
			<category><![CDATA[Cloud Run]]></category>
			<category><![CDATA[Gemini]]></category>
			<category><![CDATA[On-Device AI]]></category>
			<category><![CDATA[Architecture]]></category>
		</item>
		<item>
			<title><![CDATA[Static Docs Blindfold Your Agent: The 4-Plane Verification Fix]]></title>
			<link>https://ulukaya.dev/posts/ai-agent-document-myopia-trap</link>
			<guid isPermaLink="true">https://ulukaya.dev/posts/ai-agent-document-myopia-trap</guid>
			<description><![CDATA[My agent read one Google Doc and reported a migration my team abandoned two weeks earlier. I make it check 4 planes, with live state on Cloud Run and Firestore.]]></description>
			<content:encoded><![CDATA[<section id="introduction" data-part="INTRO" data-title="Introduction">
		<p class="lead-paragraph">
			When I asked my RAG agent to summarize project status from a single Google Doc, it confidently reported that an architecture migration was on track for Q3. In reality, my engineering team had abandoned that migration two weeks earlier in a Git commit and a Slack thread, leaving the static document completely stale. Feeding an autonomous agent a canonical documentation file does not guarantee grounded execution. In multi-turn production loops, static context blindfolds agents to live system drift, causing them to execute destructive actions against stale assumptions.
		</p>
		<p><em>Figure 1.</em> I fed my agent one Google Doc that said the migration was on track for Q3; the newer commit and chat thread said abandoned, and only a read-time check across four planes catches the conflict. <a href="https://ulukaya.dev/posts/ai-agent-document-myopia-trap">View the figure in the essay.</a></p>
		<p>
			When my agent treats point-in-time text as the complete universe of truth, it develops <strong>local document myopia</strong>: ignoring active runtime telemetry, current external ecosystem capabilities, and living strategic priorities. As established in <a href="https://arxiv.org/abs/2608.22872v2" target="_blank" rel="noopener">Better Retrieval, Worse Robustness: How Multi-Hop RAG Amplifies Upstream Errors (Aug 2026)</a>, single-source retrieval amplifies stale or noisy upstream context across multi-hop reasoning chains unless cross-source verification is enforced. Eliminating this failure mode requires architecting four-plane three-source check (the agent must confirm a claim against three independent sources before acting on it) across living databases, runtime telemetry, and skeptical verification.
		</p>
		<blockquote><strong>The epistemic grounding paradox:</strong> I frequently see engineers attempt to fix agent hallucination by injecting more static documentation into the system prompt. In practice, static documentation provides historical baselines with inherent temporal latency. Without multi-plane triangulation, an agent reading a single document enforces outdated rules rather than evaluating current system architecture.</blockquote>
	</section>

	
	<h2>PART 01: The single-document anti-pattern and point-in-time latency</h2>

	<p>
		Documentation in large-scale engineering systems and cloud ecosystems is inherently asynchronous. A policy document, technical guide, or architecture PRD represents a snapshot frozen at the time of authoring.
	</p>

	<p>
		When I feed an autonomous agent a single documentation file without corroborating signals, three failure modes emerge:
	</p>

	<section class="bias-section" id="failure-anatomy">
		<h3>01. The illustrative example anchor</h3>
		<p>
			Technical documentation frequently uses point-in-time examples (such as referencing an older model generation, a deprecated API flag, or a specific test cluster) to illustrate a broader policy.
		</p>
		<p>
			Because language models prioritize literal token matching over historical context, my agent treats the illustrative example as a hard operational boundary. It recommends obsolete tooling or rejects modern runtime capabilities simply because the static document did not mention recent releases.
		</p>
	</section>

	<section class="bias-section" id="conflating-policy">
		<h3>02. Conflating governance containers with payloads</h3>
		<p>
			A policy document governs data isolation, security boundaries, and authorization workflows (the <em>container</em>). However, my agent conflates these immutable security constraints with the transient software SKUs or model versions listed inside the text (the <em>payload</em>).
		</p>
		<p>
			The agent falsely concludes that using a modern tool or frontier model violates policy, when in reality the governance container natively supports dynamic payload upgrades.
		</p>
	</section>

	<section class="bias-section" id="negative-rule-priming">
		<h3>03. Negative constraint attention priming</h3>
		<p>
			Traditional engineering guidelines frequently use capitalized negative prohibitions (such as <code>NEVER do X</code> or <code>DO NOT run Y</code>). In transformer architectures, negative constraints increase attention weights on the exact semantic tokens they seek to forbid.
		</p>
		<p>
			My agent internalizes the negative syntax pattern and authoring style, emitting defensive and prohibitive responses rather than constructive affirmative execution plans.
		</p>
	</section>

	
	<h2>PART 02: The four-plane triangulation architecture</h2>

	<p>
		To defend my production agents against local document myopia, I replace single-document ingestion with <strong>four-plane three-source check</strong>. This mirrors the empirical architecture validated in <a href="https://arxiv.org/abs/2608.22516v1" target="_blank" rel="noopener">TRACE: Temporal Retrieval with Anchored and Convergent Evidence for Long-Horizon Understanding (Aug 2026)</a>, which demonstrates that anchoring retrieval to temporal timestamps and requiring convergent multi-source evidence eliminates stale document hallucinations. Before executing high-stakes decisions or providing architectural counsel, my agent synthesizes signals across four distinct planes:
	</p>

	<p>
		To observe this epistemic failure mode interactively, run my triangulation probe below. Feed an ungrounded agent a static markdown document with outdated metrics, and observe how evaluating across live telemetry and database state prevents hallucinated drift:
	</p>

	<p><a href="https://ulukaya.dev/posts/ai-agent-document-myopia-trap#lab-doc-myopia">Interactive lab: doc-myopia. Open the essay to run it.</a></p>

	<p><a href="https://ulukaya.dev/posts/ai-agent-document-myopia-trap">Video: Single-Doc Stale RAG Hallucination vs 4-Plane Triangulation Proof. Watch it in the essay.</a></p>

	<div class="spec-grid">
		<div class="spec-card">
			<div class="spec-card-header">
				<span class="spec-card-title">PLANE 01: LIVING STRATEGY AND MEMORY</span>
				<span class="spec-card-badge">PERSISTENT CONTEXT</span>
			</div>
			<p>
				Grounds against active priorities, roadmap goals, and historical decisions stored in transactional databases (such as <a href="https://firebase.google.com/docs/firestore/manage-data/transactions" target="_blank" rel="noopener">Cloud Firestore</a>) rather than transient prompt context.
			</p>
		</div>

		<div class="spec-card">
			<div class="spec-card-header">
				<span class="spec-card-title">PLANE 02: 1P INTERNAL REALITY</span>
				<span class="spec-card-badge">LIVE TELEMETRY</span>
			</div>
			<p>
				Queries live internal systems, repository commit history, active communication channels, and real-time quota allocations (such as <a href="https://cloud.google.com/vertex-ai/generative-ai/docs/model-garden/explore-models" target="_blank" rel="noopener">Vertex AI Model Garden</a> endpoints) to capture true operational state.
			</p>
		</div>

		<div class="spec-card">
			<div class="spec-card-header">
				<span class="spec-card-title">PLANE 03: 3P EXTERNAL FRONTIER</span>
				<span class="spec-card-badge">ECOSYSTEM BENCHMARKS</span>
			</div>
			<p>
				Executes live external search to benchmark industry state-of-the-art, open-source toolchains (such as <a href="https://modelcontextprotocol.io/introduction" target="_blank" rel="noopener">Model Context Protocol</a>), and current developer standards.
			</p>
		</div>

		<div class="spec-card" id="epistemic-skepticism">
			<div class="spec-card-header">
				<span class="spec-card-title">PLANE 04: EPISTEMIC SKEPTICISM</span>
				<span class="spec-card-badge">RUNTIME VERIFICATION</span>
			</div>
			<p>
				Treats static documents as timestamped historical inputs. Distinguishes immutable security and data constraints from ephemeral illustrative examples.
			</p>
		</div>
	</div>

	
	<div class="comparison-grid">
		<div class="comparison-card old-way">
			<div class="comparison-header">
				<span class="comparison-badge">THE OLD WAY</span>
				<h4>Single-document ingestion (myopia)</h4>
			</div>
			<ul>
				<li>Treats point-in-time documentation as permanent ground truth.</li>
				<li>Conflates governance policy containers with transient SKU examples.</li>
				<li>Attention primed by negative prohibitions (<em>NEVER do X</em>).</li>
				<li>Hallucinates policy violations on modern tool upgrades.</li>
			</ul>
		</div>

		<div class="comparison-card new-way">
			<div class="comparison-header">
				<span class="comparison-badge">THE NEW WAY</span>
				<h4>Four-plane three-source check</h4>
			</div>
			<ul>
				<li>Synthesizes Living Memory, 1P Telemetry, 3P Frontier, and Skepticism.</li>
				<li>Decouples immutable security boundaries from dynamic model endpoints.</li>
				<li>Enforces positive operational procedures and deterministic linters.</li>
				<li>Proactively benchmarks against live industry and open-source standards.</li>
			</ul>
		</div>
	</div>

	
	<h2>PART 03: System architecture and runtime implementation</h2>

	<p>
		In my production agent stack, the four-plane synthesis engine runs as an isolated microservice on <strong>Google Cloud Run</strong>, backed by <strong>Cloud Firestore</strong> for transactional state and <strong>Vertex AI</strong> for cognitive evaluation. For $0.00 local testing and verification, I run the entire state layer offline using the Firebase Local Emulator Suite before deploying to production.
	</p>

	
	<div class="arch-diagram-container">
		<div class="arch-diagram-header">
			<span class="arch-diagram-title">4-Plane Three-Source Check</span>
			<span class="arch-diagram-subtitle">Signal Collection • Parallel Synthesis • Affirmative Execution</span>
		</div>
		<div class="arch-layers-grid">
			<div class="arch-layer-card">
				<div class="arch-layer-top">
					<span class="arch-layer-badge badge-l1">INGESTION LAYER</span>
					<span class="arch-layer-spec">4 Parallel Probes</span>
				</div>
				<div class="arch-layer-name">Multi-Plane Signal Gathering</div>
				<p class="arch-layer-desc">
					Concurrently fetches Living Memory (Firestore), Live Telemetry (1P Probes), Ecosystem Signals (Web Search), and Static Policy Artifacts.
				</p>
			</div>

			<div class="arch-flow-connector">&#x25BC; <span>Normalized Signal Vectors with Timestamp Weights</span> &#x25BC;</div>

			<div class="arch-layer-card">
				<div class="arch-layer-top">
					<span class="arch-layer-badge badge-l2">COGNITIVE RUNTIME</span>
					<span class="arch-layer-spec">Google Cloud Run + Vertex AI</span>
				</div>
				<div class="arch-layer-name">Epistemic Synthesis and Decoupling</div>
				<p class="arch-layer-desc">
					Decouples immutable security containers from transient payloads, resolves timestamp contradictions, and filters negative token priming.
				</p>
			</div>

			<div class="arch-flow-connector">&#x25BC; <span>Affirmative Action Plan with Evidence Provenance</span> &#x25BC;</div>

			<div class="arch-layer-card">
				<div class="arch-layer-top">
					<span class="arch-layer-badge badge-l3">Execution gate</span>
					<span class="arch-layer-spec">Deterministic Linter and Tool Bus</span>
				</div>
				<div class="arch-layer-name">Commit-on-Green and Tool Dispatch</div>
				<p class="arch-layer-desc">
					Enforces affirmative operational invariants, stages non-destructive diffs in sandbox environments, and logs transactions atomically.
				</p>
			</div>
		</div>
	</div>

	<section id="typescript-implementation">
		<h3>Production TypeScript engine: <code>EpistemicTriangulator</code></h3>
		<p>
			Below is my reference TypeScript engine implementing four-plane triangulation with container-payload decoupling and affirmative execution guarantees:
		</p>

		<pre><code>// src/engine/EpistemicTriangulator.ts
import &#123; Firestore &#125; from "@google-cloud/firestore";
import &#123; VertexAI &#125; from "@google-cloud/vertexai";

export interface SignalPlane &#123;
  livingStrategy: string;
  internalTelemetry: string;
  externalFrontier: string;
  staticPolicyDoc: string;
&#125;

export interface TriangulatedResolution &#123;
  governanceConstraints: string[];
  recommendedPayloads: string[];
  affirmativeActionPlan: string;
  confidenceScore: number;
&#125;

export class EpistemicTriangulator &#123;
  private db: Firestore;
  private vertex: VertexAI;

  constructor(projectId: string, location: string) &#123;
    this.db = new Firestore(&#123; projectId &#125;);
    this.vertex = new VertexAI(&#123; project: projectId, location &#125;);
  &#125;

  /**
   * Triangulates across all 4 operational planes to eliminate single-document myopia.
   */
  async triangulate(topic: string, rawDocText: string): Promise&lt;TriangulatedResolution&gt; &#123;
    // Step 1: Concurrently gather context across living memory and real-time probes
    const [strategySnap, internalState, externalSignals] = await Promise.all([
      this.db.collection("agent_strategy").doc("active_pillars").get(),
      this.queryInternalTelemetry(topic),
      this.queryExternalFrontier(topic),
    ]);

    const livingStrategy = JSON.stringify(strategySnap.data() || &#123;&#125;);

    // Step 2: Formulate prompt enforcing container-payload decoupling and affirmative invariants
    const model = this.vertex.getGenerativeModel(&#123; model: "gemini-3.7-flash" &#125;);

    const prompt = &#96;
You are a three-source check. Analyze the following 4 signal planes for topic: "&#36;&#123;topic&#125;".

PLANE 1 (Living Strategy): &#36;&#123;livingStrategy&#125;
PLANE 2 (1P Internal Telemetry): &#36;&#123;internalState&#125;
PLANE 3 (3P External Frontier): &#36;&#123;externalSignals&#125;
PLANE 4 (Static Policy Document): &#36;&#123;rawDocText&#125;

INVARIANTS:
1. Treat Plane 4 as a historical baseline. Decouple immutable governance containers (security, auth, isolation) from transient illustrative payloads (model versions, old tool strings).
2. Cross-reference Plane 4 claims against Plane 2 (active reality) and Plane 3 (frontier state-of-the-art).
3. Formulate the output purely as Affirmative Operational Invariants (state what to execute, omitting negative prohibitions).

Output JSON with keys: governanceConstraints, recommendedPayloads, affirmativeActionPlan, confidenceScore.
&#96;;

    const response = await model.generateContent(&#123;
      contents: [&#123; role: "user", parts: [&#123; text: prompt &#125;] &#125;],
      generationConfig: &#123; responseMimeType: "application/json" &#125;,
    &#125;);

    return JSON.parse(response.response.candidates?.[0].content.parts[0].text || "&#123;&#125;");
  &#125;

  private async queryInternalTelemetry(topic: string): Promise&lt;string&gt; &#123;
    // Connect to live internal endpoint / search proxy
    return "Active 1P runtime endpoints: Verified healthy, Vertex AI Model Garden endpoints active.";
  &#125;

  private async queryExternalFrontier(topic: string): Promise&lt;string&gt; &#123;
    // Connect to external search proxy / Model Context Protocol benchmark catalog
    return "External ecosystem baseline: Terminal agents adopt MCP standards and dynamic model routing.";
  &#125;
&#125;</code></pre>
		</div>
	</section>

	
	<h2>PART 04: The old way vs. the four-plane triangulation standard</h2>

	<div class="comparison-grid">
		<div class="comparison-card old-way">
			<div class="comparison-header">
				<span class="comparison-badge">THE OLD WAY</span>
				<h4>Single-document ingestion</h4>
			</div>
			<ul>
				<li><strong>Narrow context:</strong> Reads a single markdown doc or PRD and assumes it contains 100% of available truth.</li>
				<li><strong>Illustrative anchoring:</strong> Treats historical examples (such as two-year-old model names) as permanent execution limits.</li>
				<li><strong>Negative prohibitions:</strong> Relies on long lists of <code>NEVER</code> rules, priming the model to output negative syntax.</li>
				<li><strong>Isolated execution:</strong> Ignores user priorities and live infrastructure telemetry, operating without runtime state context.</li>
			</ul>
		</div>

		<div class="comparison-card new-way">
			<div class="comparison-header">
				<span class="comparison-badge">THE NEW WAY</span>
				<h4>Four-plane three-source check</h4>
			</div>
			<ul>
				<li><strong>Multi-plane grounding:</strong> Simultaneously integrates Living Strategy, Live 1P Telemetry, 3P Frontier, and Static Docs.</li>
				<li><strong>Container decoupling:</strong> Isolates durable governance and security rules from transient model and tool payloads.</li>
				<li><strong>Affirmative invariants:</strong> Expresses all operational logic as clear, positive execution procedures with fallbacks.</li>
				<li><strong>Transactional state:</strong> Backed by Firestore and Cloud Run to maintain atomic state across multi-turn workflows.</li>
			</ul>
		</div>
	</div>

	<blockquote><strong>Architecture blueprint and spec:</strong> Inspect my complete <a href="https://ulukaya.dev/blueprints">Transactional Memory Blueprint &rarr;</a> or scaffold a repository-native specification tree with my <a href="https://ulukaya.dev/instruments#generators">noVibes Agent Spec Generator &rarr;</a></blockquote>

	<section class="bias-section" id="references">
		<h2>Industry validation and benchmarks</h2>
		<ul>
			<li><a href="https://arxiv.org/abs/2608.22872v2" target="_blank" rel="noopener">Better Retrieval, Worse Robustness: How Multi-Hop RAG Amplifies Upstream Errors (Aug 2026, arXiv:2608.22872v2)</a>: Confirms that single-source retrieval amplifies stale or noisy upstream context across multi-hop reasoning chains unless cross-source verification is enforced.</li>
			<li><a href="https://arxiv.org/abs/2608.22516v1" target="_blank" rel="noopener">TRACE: Temporal Retrieval with Anchored and Convergent Evidence for Long-Horizon Understanding (Aug 2026, arXiv:2608.22516v1)</a>: Confirms that anchoring retrieval to temporal timestamps and requiring convergent multi-source evidence eliminates stale document hallucinations.</li>
			<li><a href="https://cloud.google.com/vertex-ai/generative-ai/docs/model-garden/explore-models" target="_blank" rel="noopener">Google Cloud Vertex AI Model Garden: Enterprise foundation model routing and deployment architecture</a></li>
			<li><a href="https://modelcontextprotocol.io/introduction" target="_blank" rel="noopener">Model Context Protocol (MCP) Specification: Open standard for connecting local tools and data sources to AI agents</a></li>
			<li><a href="https://firebase.google.com/docs/firestore/manage-data/transactions" target="_blank" rel="noopener">Cloud Firestore Atomic Transactions: Managing transactional memory and preventing dual-write state drift in autonomous systems</a></li>
			<li><strong>Ghost in the Loop Series:</strong> <a href="https://ulukaya.dev/posts/ai-agent-split-brain-trap">Part 2: The AI Agent Split-Brain Trap</a> and <a href="https://ulukaya.dev/posts/ten-cognitive-biases-ai-agents">Part 1: The 10 Cognitive Biases of Autonomous Systems</a></li>
		</ul>
	</section>]]></content:encoded>
			<pubDate>Wed, 19 Aug 2026 00:00:00 GMT</pubDate>
			<dc:creator><![CDATA[Ibrahim Ulukaya]]></dc:creator>
			<category><![CDATA[AI Agents]]></category>
			<category><![CDATA[Epistemic Triangulation]]></category>
			<category><![CDATA[Firestore]]></category>
			<category><![CDATA[Cloud Run]]></category>
			<category><![CDATA[Context Grounding]]></category>
			<category><![CDATA[Firebase App Check]]></category>
		</item>
		<item>
			<title><![CDATA[Dropped Tokens: Fixing Multi-Turn Agent Streams That Die Mid-Flight]]></title>
			<link>https://ulukaya.dev/posts/the-leaky-abstraction-vol2</link>
			<guid isPermaLink="true">https://ulukaya.dev/posts/the-leaky-abstraction-vol2</guid>
			<description><![CDATA[Multi-turn agent streams die on disconnects and buffer drops. Idempotent reassembly on Cloud Run brings them back intact. Part 2 of The Leaky Abstraction.]]></description>
			<content:encoded><![CDATA[<section id="introduction" data-part="INTRO" data-title="Introduction">
		<p class="lead-paragraph">
			When my agent executed a 25-second database tool call mid-stream, my Server-Sent Events (SSE) stream went silent, and cloud load balancers <a href="https://ulukaya.dev/posts/the-leaky-abstraction-vol2#lab-chunk-drop">severed the idle socket</a> at 15 seconds. Worse, reconnecting without an idempotent checkpoint offset caused duplicate side effects.
		</p>
		<p><em>Figure 1.</em> A 25 s tool call goes silent on the wire, a 15 s idle proxy cuts the socket, and a blind re-send runs the tool twice; a ping every 10 s plus a Last-Event-ID resume runs it once. <a href="https://ulukaya.dev/posts/the-leaky-abstraction-vol2">View the figure in the essay.</a></p>
		<p>
			When my autonomous agent invokes external tools (querying databases, running compiler sandboxes, or calling third-party APIs), token generation pauses. During this multi-second interval, zero bytes flow over the connection, causing intermediate proxies, API gateways, and load balancers to terminate the socket. Defending my multi-turn agents requires idempotent stream reassembly, heartbeat keep-alives, and transactional session resumption with Cloud Run and Firestore.
		</p>
		<blockquote><strong>The multi-turn reality:</strong> When my agent tool call takes 15 to 30 seconds to complete, standard HTTP/1.1 and Server-Sent Events (SSE) connections frequently drop. If my architecture relies on in-memory streaming state in ephemeral backend containers, a reconnecting client either duplicates costly tool side effects ($0.00 recovery protection without an idempotency ledger) or encounters unrecoverable state drift.</blockquote>
	</section>

	
	<h2>PART 01: The architectural gap: Tool latency vs. proxy timeouts</h2>

	<p>
		In standard request-response lifecycles, I can bound latency. In multi-turn agent execution, my model generation alternates between fast token streaming and long, silent tool execution phases:
	</p>

	<section class="bias-section">
		<h3>01. Silent proxy connection drops</h3>
		<p>
			Cloud load balancers, CDN edges, and enterprise corporate proxies enforce idle connection timeouts (often between 15 and 60 seconds). When my agent pauses text generation to wait on an external tool (such as an asynchronous BigQuery query or multi-step database transaction), zero bytes traverse the connection.
		</p>
		<p>
			The proxy drops the socket without sending a TCP <code>FIN</code> or <code>RST</code> packet to my client. My client UI remains stuck in a loading state while my backend continues executing compute tasks in the background.
		</p>
		<blockquote><strong>The keep-alive rule:</strong> My production streaming gateways inject periodic SSE comment heartbeats (<code>: ping\n\n</code>) at sub-15-second intervals during asynchronous tool execution to maintain active <a href="https://datatracker.ietf.org/doc/html/rfc9293" target="_blank" rel="noopener">TCP socket state (IETF RFC 9293)</a> through intermediate proxies.</blockquote>
	</section>

	<section class="bias-section" id="disconnect-anatomy">
		<h3>02. The stateless reconnection trap</h3>
		<p>
			When my mobile or web client experiences a network handoff (such as switching from Wi-Fi to cellular) or recovers from a silent timeout, it initiates a reconnection. In a naive serverless architecture, I ran into three distinct failure modes:
		</p>
		<ul>
			<li><strong>Ephemeral instance routing:</strong> My reconnected request landed on a different container instance in Google Cloud Run that lacked the in-memory stream buffer of my previous session.</li>
			<li><strong>Duplicate tool execution:</strong> When my client blindly re-sent the original prompt, my agent re-executed non-idempotent tool calls (creating duplicate database rows and incurring duplicate API charges).</li>
			<li><strong>Token buffer thrashing:</strong> When my server replayed the entire conversation history from scratch over the new stream, my client UI stuttered, re-rendered hundreds of tokens, and corrupted the local scroll position.</li>
		</ul>
	</section>

	
	<h2>PART 02: The old way vs. the new way</h2>

	<p>
		Building my production-grade multi-turn agent systems required shifting from in-memory stream assumptions to durable, event-sourced session transport:
	</p>

	<div class="table-container">
		<table class="data-table">
			<thead>
				<tr>
					<th>Failure mode</th>
					<th>The old way (naive in-memory streaming)</th>
					<th>The new way (transactional session gateway)</th>
				</tr>
			</thead>
			<tbody>
				<tr>
					<td><strong>Idle tool latency</strong></td>
					<td>Zero bytes sent during tool execution; proxy drops socket after 15s.</td>
					<td>Background heartbeat emitter sends periodic SSE comments (<code>: ping\n\n</code>) every 10s.</td>
				</tr>
				<tr>
					<td><strong>Mid-stream disconnect</strong></td>
					<td>Stream state lost on container recycle; client restart aborts session.</td>
					<td>Event-sourced log in Cloud Firestore records each token chunk and tool payload with a monotonic <code>seq_id</code>.</td>
				</tr>
				<tr>
					<td><strong>Reconnection ingress</strong></td>
					<td>Client re-submits prompt, risking duplicate non-idempotent tool actions.</td>
					<td>Client sends <a href="https://html.spec.whatwg.org/multipage/server-sent-events.html" target="_blank" rel="noopener"><code>Last-Event-ID</code> header</a>; gateway replays only unacknowledged events from Firestore.</td>
				</tr>
				<tr>
					<td><strong>Tool concurrency</strong></td>
					<td>Concurrent client retries trigger race conditions in parallel containers.</td>
					<td>Distributed lease and mutex lock in Firestore ensures only one container executes tools per session.</td>
				</tr>
			</tbody>
		</table>
	</div>

	
	<div class="arch-diagram-container" id="transport-topology">
		<div class="arch-diagram-header">
			<span class="arch-diagram-title">Resilient multi-turn agent transport topology</span>
			<span class="arch-diagram-subtitle">Client ingress • Cloud Run gateway • Transactional session store</span>
		</div>
		<div class="arch-layers-grid">
			<div class="arch-layer-card">
				<div class="arch-layer-top">
					<span class="arch-layer-badge badge-l1">LAYER 01: CLIENT RUNTIME</span>
					<span class="arch-layer-spec">App Check + Last-Event-ID</span>
				</div>
				<div class="arch-layer-name">Idempotent stream consumer</div>
				<p class="arch-layer-desc">
					Tracks monotonic event sequence IDs, automatically reconnects with exponential backoff on transport drop, and passes <code>Last-Event-ID</code> for gap-free resumption.
				</p>
			</div>

			<div class="arch-flow-connector">▼ <span>HTTPS SSE stream / reconnect with Last-Event-ID</span> ▼</div>

			<div class="arch-layer-card">
				<div class="arch-layer-top">
					<span class="arch-layer-badge badge-l2">LAYER 02: EXECUTION GATEWAY</span>
					<span class="arch-layer-spec">Google Cloud Run</span>
				</div>
				<div class="arch-layer-name">Stateful multi-turn reassembler</div>
				<p class="arch-layer-desc">
					Emits sub-15s keep-alive ping frames during tool execution, acquires atomic session locks, and streams model tokens while persisting event batches.
				</p>
			</div>

			<div class="arch-flow-connector">▼ <span>Atomic append and mutex lease (Firestore transactions)</span> ▼</div>

			<div class="arch-layer-card">
				<div class="arch-layer-top">
					<span class="arch-layer-badge badge-l3">LAYER 03: PERSISTENCE LAYER</span>
					<span class="arch-layer-spec">Cloud Firestore</span>
				</div>
				<div class="arch-layer-name">Event-sourced session journal</div>
				<p class="arch-layer-desc">
					Maintains my authoritative append-only log of token chunks, tool call requests, and verified tool execution results for deterministic resumption.
				</p>
			</div>
		</div>
	</div>

	<p><a href="https://ulukaya.dev/posts/the-leaky-abstraction-vol2">Video: Terminal Proof: 5 SSE Events Arriving as 4 TCP Packets Lose 3 Events to a Naive Parser. Watch it in the essay.</a></p>

	<p>
		To simulate how mid-flight transport crashes corrupt agent execution, trigger my disconnect simulator below. <a href="https://ulukaya.dev/posts/the-leaky-abstraction-vol2#lab-chunk-drop">Drop the connection</a> during an ongoing multi-turn stream and observe how my idempotent session journal recovers buffered state using <code>Last-Event-ID</code>:
	</p>

	<p><a href="https://ulukaya.dev/posts/the-leaky-abstraction-vol2#lab-chunk-drop">Interactive lab: chunk-drop. Open the essay to run it.</a></p>

	
	<h2>PART 03: Production-ready TypeScript implementation</h2>

	<p>
		Below is my production-tested <code>AgentSessionStreamGateway</code> implementation. I deploy it on <strong>Cloud Run</strong> or <strong>Firebase App Hosting</strong>, managing heartbeats during tool calls and enabling seamless reconnection via <code>Last-Event-ID</code>:
	</p>

	<pre><code>import &#123; Response &#125; from 'express';
import &#123; Firestore &#125; from '@google-cloud/firestore';

export interface StreamEvent &#123;
  seq: number;
  type: 'token' | 'tool_start' | 'tool_end' | 'done' | 'error';
  payload: unknown;
  timestamp: number;
&#125;

export class AgentSessionStreamGateway &#123;
  private heartbeatTimer?: NodeJS.Timeout;
  private currentSeq: number = 0;
  private eventBuffer: StreamEvent[] = [];
  private readonly MAX_REPLAY_LIMIT = 200;

  constructor(
    private readonly sessionId: string,
    private readonly res: Response,
    private readonly db: Firestore
  ) &#123;&#125;

  /**
   * Initializes SSE response headers and begins periodic keep-alive pings.
   */
  public initHeaders(): void &#123;
    this.res.setHeader('Content-Type', 'text/event-stream');
    this.res.setHeader('Cache-Control', 'no-cache, no-transform');
    this.res.setHeader('Connection', 'keep-alive');
    this.res.setHeader('X-Accel-Buffering', 'no');
    this.res.flushHeaders();

    // Emit an SSE comment ping every 10 seconds to prevent proxy timeouts
    this.heartbeatTimer = setInterval(() =&gt; &#123;
      if (!this.res.writableEnded) &#123;
        this.res.write(': ping\n\n');
      &#125;
    &#125;, 10_000);
  &#125;

  /**
   * Replays unacknowledged events with bounded limit on client reconnect.
   */
  public async replayFrom(lastEventId: number): Promise&lt;number&gt; &#123;
    const snapshot = await this.db
      .collection('agent_sessions')
      .doc(this.sessionId)
      .collection('events')
      .where('seq', '&gt;', lastEventId)
      .orderBy('seq', 'asc')
      .limit(this.MAX_REPLAY_LIMIT)
      .get();

    for (const doc of snapshot.docs) &#123;
      const event = doc.data() as StreamEvent;
      this.writeSseFrame(event);
      this.currentSeq = Math.max(this.currentSeq, event.seq);
    &#125;

    return this.currentSeq;
  &#125;

  /**
   * Emits tokens instantly to client socket and buffers state for batched persistence.
   */
  public emit(type: StreamEvent['type'], payload: unknown): void &#123;
    this.currentSeq += 1;
    const event: StreamEvent = &#123;
      seq: this.currentSeq,
      type,
      payload,
      timestamp: Date.now(),
    &#125;;

    // 1. Flush immediately to client socket (zero latency penalty on streaming)
    this.writeSseFrame(event);

    // 2. Buffer in memory for batched commit
    this.eventBuffer.push(event);

    // 3. Flush checkpoints immediately on tool execution boundaries
    if (type !== 'token') &#123;
      void this.flushBuffer();
    &#125;
  &#125;

  /**
   * Flushes in-flight event buffer to Firestore using atomic batched writes.
   */
  public async flushBuffer(): Promise&lt;void&gt; &#123;
    if (this.eventBuffer.length === 0) return;

    const eventsToCommit = [...this.eventBuffer];
    this.eventBuffer = [];

    const batch = this.db.batch();
    const sessionRef = this.db.collection('agent_sessions').doc(this.sessionId);

    for (const event of eventsToCommit) &#123;
      const docId = event.seq.toString().padStart(8, '0');
      const docRef = sessionRef.collection('events').doc(docId);
      batch.set(docRef, event);
    &#125;

    await batch.commit();
  &#125;

  private writeSseFrame(event: StreamEvent): void &#123;
    if (this.res.writableEnded) return;
    this.res.write(`id: &#36;&#123;event.seq&#125;\n`);
    this.res.write(`event: &#36;&#123;event.type&#125;\n`);
    this.res.write(`data: &#36;&#123;JSON.stringify(event.payload)&#125;\n\n`);
  &#125;

  public async close(): Promise&lt;void&gt; &#123;
    if (this.heartbeatTimer) &#123;
      clearInterval(this.heartbeatTimer);
    &#125;
    await this.flushBuffer();
    if (!this.res.writableEnded) &#123;
      this.res.end();
    &#125;
  &#125;
&#125;</code></pre>
	</div>

	<blockquote><strong>Architectural takeaway:</strong> I do not rely on ephemeral HTTP connections for multi-turn agent streaming. I maintain active sockets with periodic keep-alive pings during tool execution, persist event streams with monotonic sequence IDs in Cloud Firestore, and implement <code>Last-Event-ID</code> reconnection to recover state after network disconnects.</blockquote>

	<blockquote><strong>Architecture blueprint and spec:</strong> Inspect my complete <a href="https://ulukaya.dev/blueprints">Deterministic Agent Runtime Blueprint &rarr;</a> or generate production-ready specification files with my <a href="https://ulukaya.dev/instruments#generators">noVibes Agent Spec Generator &rarr;</a></blockquote>

	<section class="bias-section" id="references">
		<h2>Industry validation and benchmarks</h2>
		<ul>
			<li>
				<a href="https://arxiv.org/abs/2608.14635v2" target="_blank" rel="noopener">Belayer: Efficient Fault Tolerance for LLM Agentic RL Training (Jul 2026)</a>: Confirms that long-horizon agent executions coupled with stateful side-effects (DB writes, file mutations) require explicit checkpointing and idempotent replay to survive transport disconnects without duplicate side-effects.
			</li>
			<li>
				<a href="https://arxiv.org/abs/2606.23521v1" target="_blank" rel="noopener">Concordia: JIT-Compiled Persistent-Kernel Checkpointing for Fault-Tolerant LLM Inference (Jun 2026)</a>: Confirms that sub-second checkpoint state restoration eliminates recovery stalls during mid-stream network drops.
			</li>
			<li><a href="https://html.spec.whatwg.org/multipage/server-sent-events.html" target="_blank" rel="noopener">WHATWG HTML Standard: Server-Sent Events (SSE) Protocol and Last-Event-ID</a></li>
			<li><a href="https://cloud.google.com/run/docs/triggering/https-request" target="_blank" rel="noopener">Google Cloud Run: HTTPS Ingress, Timeouts, and Streaming Configuration</a></li>
			<li><a href="https://firebase.google.com/docs/firestore/manage-data/transactions" target="_blank" rel="noopener">Cloud Firestore: Atomic Transactions and Batched Operations</a></li>
			<li><a href="https://firebase.google.com/docs/ai-logic" target="_blank" rel="noopener">Firebase AI Logic Documentation and Tool Calling Architecture</a></li>
			<li><a href="https://firebase.google.com/docs/app-check" target="_blank" rel="noopener">Firebase App Check: Production Attestation for Streaming Backends</a></li>
			<li><a href="https://datatracker.ietf.org/doc/html/rfc9293" target="_blank" rel="noopener">IETF RFC 9293: Transmission Control Protocol (TCP) Specification</a></li>
		</ul>
	</section>]]></content:encoded>
			<pubDate>Tue, 18 Aug 2026 00:00:00 GMT</pubDate>
			<dc:creator><![CDATA[Ibrahim Ulukaya]]></dc:creator>
			<category><![CDATA[AI Agents]]></category>
			<category><![CDATA[Cloud Run]]></category>
			<category><![CDATA[Firestore]]></category>
			<category><![CDATA[Streaming]]></category>
			<category><![CDATA[Node.js]]></category>
			<category><![CDATA[Firebase App Check]]></category>
		</item>
		<item>
			<title><![CDATA[Atomic Idempotency Key Enforcement for Multi-Turn Agent Tool Calling]]></title>
			<link>https://ulukaya.dev/til#01-idempotency-mutex</link>
			<guid isPermaLink="true">https://ulukaya.dev/til#01-idempotency-mutex</guid>
			<description><![CDATA[When autonomous agents execute external tool calls (such as payment triggers or cloud resource provisioning), network blips or TCP disconnects frequently cause the agent client to retry the request.]]></description>
			<content:encoded><![CDATA[<p>When autonomous agents execute external tool calls (such as payment triggers or cloud resource provisioning), network blips or TCP disconnects frequently cause the agent client to retry the request.</p>
<p>To prevent duplicate execution side-effects, store an idempotency token document in Cloud Firestore using an atomic transaction before dispatching the tool. If the transaction detects an existing token in the <code>in_flight</code> or <code>completed</code> state, return the cached result immediately rather than re-executing the underlying tool.</p>
<pre><code>// Atomic Idempotency Check in Cloud Firestore
export async function withIdempotency(db, key, executeTool) {
  const ref = db.collection("idempotency_keys").doc(key);
  return db.runTransaction(async (tx) =&gt; {
    const doc = await tx.get(ref);
    if (doc.exists &amp;&amp; doc.data().status === "completed") {
      return doc.data().cachedResult;
    }
    tx.set(ref, { status: "in_flight", startedAt: Date.now() }, { merge: true });
    const result = await executeTool();
    tx.set(ref, { status: "completed", cachedResult: result, completedAt: Date.now() }, { merge: true });
    return result;
  });
}</code></pre>]]></content:encoded>
			<pubDate>Tue, 18 Aug 2026 00:00:00 GMT</pubDate>
			<dc:creator><![CDATA[Ibrahim Ulukaya]]></dc:creator>
			<category><![CDATA[Firestore]]></category>
			<category><![CDATA[AI Agents]]></category>
		</item>
		<item>
			<title><![CDATA[TCP Chunk Tearing and Multi-Byte UTF-8 Reassembly in LLM Streams]]></title>
			<link>https://ulukaya.dev/til#02-tcp-chunk-tearing</link>
			<guid isPermaLink="true">https://ulukaya.dev/til#02-tcp-chunk-tearing</guid>
			<description><![CDATA[LLM streaming endpoints emit UTF-8 text chunks over HTTP/2 or Server-Sent Events (SSE). Because TCP packet boundaries operate independently of UTF-8 character encoding, multi-byte sequences (such as emojis or complex punctuation) can tear cleanly across chunk boundaries.]]></description>
			<content:encoded><![CDATA[<p>LLM streaming endpoints emit UTF-8 text chunks over HTTP/2 or Server-Sent Events (SSE). Because TCP packet boundaries operate independently of UTF-8 character encoding, multi-byte sequences (such as emojis or complex punctuation) can tear cleanly across chunk boundaries.</p>
<p>Passing raw chunk slices directly into <code>JSON.parse()</code> or text decoders causes intermittent <code>SyntaxError</code> crashes. Use Node.js <code>string_decoder.StringDecoder('utf8')</code> or a stateful byte buffer to hold incomplete multi-byte sequences until the trailing bytes arrive.</p>
<pre><code>import { StringDecoder } from "node:string_decoder";

export async function* decodeSafeStream(rawByteStream) {
  const decoder = new StringDecoder("utf8");
  for await (const chunk of rawByteStream) {
    // StringDecoder preserves trailing partial UTF-8 bytes across iterations
    const safeText = decoder.write(chunk);
    if (safeText) yield safeText;
  }
  const finalChunk = decoder.end();
  if (finalChunk) yield finalChunk;
}</code></pre>]]></content:encoded>
			<pubDate>Sat, 15 Aug 2026 00:00:00 GMT</pubDate>
			<dc:creator><![CDATA[Ibrahim Ulukaya]]></dc:creator>
			<category><![CDATA[Streaming]]></category>
			<category><![CDATA[Node.js]]></category>
		</item>
		<item>
			<title><![CDATA[Two Writers, One Index: How Static Files Corrupt Agent Memory]]></title>
			<link>https://ulukaya.dev/posts/ai-agent-split-brain-trap</link>
			<guid isPermaLink="true">https://ulukaya.dev/posts/ai-agent-split-brain-trap</guid>
			<description><![CDATA[Two subagents wrote one record and the second write erased the first 80 ms later. I replaced my markdown summary index with Firestore transactions on Cloud Run.]]></description>
			<content:encoded><![CDATA[<section id="introduction" data-part="INTRO" data-title="Introduction">
		<p class="lead-paragraph">
			When I dispatched <a href="https://ulukaya.dev/posts/ai-agent-split-brain-trap#lab-split-brain">two subagents</a> to update a shared Firestore user profile simultaneously, both subagents read version N of the document at the same millisecond. Subagent A wrote its update, and 80 ms later Subagent B overwrote the document, silently erasing Subagent A's work in a classic distributed split-brain race condition. Relying on flat markdown files or uncoordinated writes for autonomous agent memory guarantees state corruption in production. In extended multi-turn sessions, file-based memory decouples from underlying entity state, creating a split-brain condition where my model hallucinates over stale summaries.
		</p>

		<p><em>Figure 1.</em> Two writers, one index. Left: both subagents read version N, A commits, and 80 ms later B overwrites it. The lost update raises no error, so nothing downstream knows A's work is gone. Right: the same two writes under one transaction. B's commit is refused because the version moved underneath it, so B re-reads and retries instead of overwriting. <a href="https://ulukaya.dev/posts/ai-agent-split-brain-trap">View the figure in the essay.</a></p>

		<p>
			As I explored in <a href="https://ulukaya.dev/posts/ten-cognitive-biases-ai-agents">Part 1: The 10 Cognitive Biases of Autonomous Systems</a>, memory drift in long-running agents is rarely an LLM prompt failure. I found it is a dual-write cache invalidation breakdown caused by treating language models as database engines. Eliminating state drift requires enforcing ACID transaction boundaries and atomic session mutex locks with Firestore.
		</p>
		<blockquote><strong>The distributed systems paradox:</strong> I frequently see engineers diagnose agent memory drift as an LLM prompting defect, attempting to fix hallucinations with longer system prompts. In my production systems, memory drift in multi-turn agents is a dual-write cache invalidation failure caused by offloading database indexing responsibilities to the language model.</blockquote>
	</section>

	
	<h2>PART 01: The static index anti-pattern and markdown storage</h2>

	<p>
		To keep context windows manageable and save input tokens, I initially experimented with a dual-tier storage pattern:
	</p>

	<ol>
		<li><strong>Tier 1 (granular entity files):</strong> Individual files that hold detailed state, such as <code>contacts/alice.md</code> or <code>tasks/task-402.json</code>.</li>
		<li><strong>Tier 2 (the summary index):</strong> A single high-level markdown file (such as <code>INDEX.md</code> or <code>SUMMARY.md</code>) containing a table that summarizes active entities, priorities, and statuses.</li>
	</ol>

	<section class="bias-section" id="decoupling-threshold">
		<h3>01. The decoupling threshold (turn 20+)</h3>
		<p>
			Every time my agent executes an action that modifies state, it must perform a dual-write: update the granular entity file, then parse and update the summary table in the index file.
		</p>
		<p>
			Because language models process file edits probabilistically rather than transactionally, dual-writes fail under load. My model updates the entity file but skips the index table, or formats the table with slightly altered column headers.
		</p>
		<p>
			On turn 25, when my agent checks its overall status to decide its next step, it reads the shorter summary index to save tokens. It reads stale, uncommitted state, treats it as ground truth, and enters an unrecoverable hallucination loop.
		</p>
		<p>
			I enforce strict relational consistency for <strong>deterministic operational state</strong> (tasks, status queues, assignments, tool locks). While associative episodic memory (user preferences and conversational nuances) benefits from vector search embeddings, my operational coordination layer requires ACID transactions.
		</p>
	</section>

	
	<h2>PART 02: Failure anatomy: The three concurrency and state traps</h2>

	<section class="bias-section" id="concurrency-section">
		<h3>02. Trap 1: Concurrency collisions and lost updates</h3>
		<p>
			Flat files on a local filesystem offer no native locking. When my agent <a href="https://ulukaya.dev/posts/ai-agent-split-brain-trap#lab-split-brain">spawns parallel subagents</a> or runs a background heartbeat while an interactive session is active, two processes attempt to write to <code>tasks.md</code> simultaneously.
		</p>
		<p>
			Without atomic row-level locks, the operating system executes the writes in unpredictable sequence. As established in foundational distributed systems literature by <a href="https://amturing.acm.org/p558-lamport.pdf" target="_blank" rel="noopener">Leslie Lamport (1978)</a> and <a href="https://people.eecs.berkeley.edu/~brewer/cs262/concurrency-distributed-databases.pdf" target="_blank" rel="noopener">Bernstein &amp; Goodman (1981)</a>, uncoordinated concurrent writes without synchronized ordering guarantee lost updates: the last process to finish silently overwrites earlier mutations without raising an error. Enforcing <a href="https://jimgray.azurewebsites.net/papers/thetransactionconcept.pdf" target="_blank" rel="noopener">Jim Gray's transaction concept (1981)</a> is mandatory in my architecture to guarantee ACID isolation.
		</p>
	</section>

	<p>
		To observe how uncoordinated concurrent writes silently drop state in real time, I built the lost-update concurrency sandbox below. Fire simultaneous parallel agent writes at a shared markdown task index to see race conditions, lost updates, and phantom loops emerge:
	</p>

	<p><a href="https://ulukaya.dev/posts/ai-agent-split-brain-trap#lab-split-brain">Interactive lab: split-brain. Open the essay to run it.</a></p>

	<p><a href="https://ulukaya.dev/posts/ai-agent-split-brain-trap">Video: Six concurrent subagents: 5 of 6 markdown writes lost with zero errors raised, then 6 of 6 committed under SQLite transactions. Watch it in the essay.</a></p>

	<section class="bias-section" id="memory-tearing">
		<h3>03. Trap 2: Memory tearing and container recycles</h3>
		<p>
			Running my agents on serverless infrastructure like Cloud Run or Cloud Functions provides elastic scale, but introduces container lifecycles. If my agent process terminates or scales to zero while writing a 50 KB markdown index, the file is left half-written.
		</p>
		<p>
			When the container spins up on the next turn, my agent encounters truncated JSON or broken markdown syntax, causing immediate tool execution crashes. For deeper client transport failure patterns, read my guide on <a href="https://ulukaya.dev/posts/client-runtime-agent-resilience">Client-Side Runtime Agent Resilience</a>.
		</p>
	</section>

	<section class="bias-section" id="phantom-hallucinations">
		<h3>04. Trap 3: Phantom state loops and stale cache trust</h3>
		<p>
			When an index file indicates a task is <code>OPEN</code> while the underlying database marks it <code>RESOLVED</code>, my agent experiences split-brain confusion.
		</p>
		<p>
			Rather than querying ground truth, the model trusts the summary file, reasons that the resolution must have failed, and re-executes API calls against external systems. In my early tests, this produced duplicate GitHub issues, repeated Slack pings, and wasted compute. For cost protection patterns against runaway execution loops, see <a href="https://ulukaya.dev/posts/cloud-spend-caps-firebase">The Production Reality of Firebase Spend Caps</a>.
		</p>
	</section>

	
	<h2>PART 03: The solution: The three-tier zero-drift stack</h2>

	<p>
		To permanently eliminate the split-brain trap, I changed my fundamental architecture: <strong>I offload state indexing from the LLM to a transactional database.</strong>
	</p>

	<div class="table-container">
		<table class="data-table">
			<thead>
				<tr>
					<th>Architectural dimension</th>
					<th>Fragile file-based storage</th>
					<th>Transactional cloud memory</th>
				</tr>
			</thead>
			<tbody>
				<tr>
					<td><strong>State coherence</strong></td>
					<td>Dual-writes required across entity files and markdown index tables.</td>
					<td>Single source of truth in Cloud Firestore with dynamic indexed queries.</td>
				</tr>
				<tr>
					<td><strong>Concurrency control</strong></td>
					<td>No atomic file locks; parallel tool calls overwrite and clobber state.</td>
					<td>Optimistic concurrency control (OCC) via <code>runTransaction</code>.</td>
				</tr>
				<tr>
					<td><strong>Serverless lifecycle</strong></td>
					<td>Local disk state lost on container recycle or scale-to-zero.</td>
					<td>Stateless Cloud Run workers with zero persistent local disk state.</td>
				</tr>
				<tr>
					<td><strong>Reasoning stability</strong></td>
					<td>Stale markdown summary tables poison multi-turn agent reasoning.</td>
					<td>Every query returns ground truth directly from the database engine.</td>
				</tr>
			</tbody>
		</table>
	</div>

	<section class="bias-section" id="stack-layers">
		<h3>05. Production architecture overview</h3>
		<p>
			My three-tier transactional memory architecture separates ingress attestation, stateless execution, and atomic state storage:
		</p>

		
		<div class="arch-diagram-container">
			<div class="arch-diagram-header">
				<span class="arch-diagram-title">Three-tier agent transactional architecture</span>
				<span class="arch-diagram-subtitle">Attestation • Stateless compute • Atomic persistence</span>
			</div>
			<div class="arch-layers-grid">
				<div class="arch-layer-card">
					<div class="arch-layer-top">
						<span class="arch-layer-badge badge-l1">LAYER 01: INGRESS AND IDENTITY</span>
						<span class="arch-layer-spec">App Check and Cloud IAM</span>
					</div>
					<div class="arch-layer-name">Runtime attestation and service isolation</div>
					<p class="arch-layer-desc">
						Firebase App Check attests client triggers at the Cloud Run boundary, while IAM service accounts isolate backend database mutations to verified containers.
					</p>
				</div>

				<div class="arch-flow-connector">▼ <span>Attested execution invocation</span> ▼</div>

				<div class="arch-layer-card">
					<div class="arch-layer-top">
						<span class="arch-layer-badge badge-l2">LAYER 02: STATELESS COMPUTE</span>
						<span class="arch-layer-spec">Google Cloud Run</span>
					</div>
					<div class="arch-layer-name">Stateless tool execution and OCC coordination</div>
					<p class="arch-layer-desc">
						Executes agent tool logic statelessly. No conversational state or scratch files persist on container local disks across turns.
					</p>
				</div>

				<div class="arch-flow-connector">▼ <span>ACID optimistic transaction boundary</span> ▼</div>

				<div class="arch-layer-card">
					<div class="arch-layer-top">
						<span class="arch-layer-badge badge-l3">LAYER 03: TRANSACTIONAL STORAGE</span>
						<span class="arch-layer-spec">Cloud Firestore</span>
					</div>
					<div class="arch-layer-name">Atomic document transactions and dynamic indexes</div>
					<p class="arch-layer-desc">
						Provides ACID document transactions, atomic counters, and query indexes that reflect 100% fresh state on every read.
					</p>
				</div>
			</div>
		</div>
	</section>

	
	<h2>PART 04: Production implementation: Transactional memory in TypeScript</h2>

	<p>
		The following TypeScript module implements my atomic agent memory engine using the Firebase Admin SDK on Cloud Run. I use Firestore transactions to prevent lost updates, handle version collisions with inline state recovery, and provide dynamic query helpers that eliminate static index files entirely:
	</p>

	
	<pre><code>// Transactional Agent Memory Engine for Cloud Run and Cloud Firestore
import &#123; initializeApp, getApps &#125; from 'firebase-admin/app';
import &#123; getFirestore, FieldValue, Timestamp &#125; from 'firebase-admin/firestore';

if (getApps().length === 0) &#123;
  initializeApp(); // Uses Application Default Credentials on Cloud Run
&#125;

const db = getFirestore();

export type TaskStatus = 'PENDING' | 'IN_PROGRESS' | 'COMPLETED' | 'FAILED';

export interface AgentTaskDocument &#123;
  title: string;
  status: TaskStatus;
  version: number;
  assignedAgent: string;
  lastUpdated: Timestamp;
  payload: Record&lt;string, unknown&gt;;
&#125;

export interface AgentTaskResponse &#123;
  id: string;
  title: string;
  status: TaskStatus;
  version: number;
  assignedAgent: string;
  lastUpdated: string;
  payload: Record&lt;string, unknown&gt;;
&#125;

export interface MutationResult &#123;
  success: boolean;
  newVersion?: number;
  currentState?: AgentTaskResponse;
  error?: string;
&#125;

export class TransactionalMemoryEngine &#123;
  private tasksCol = db.collection('agent_tasks');

  private formatTask(id: string, data: AgentTaskDocument): AgentTaskResponse &#123;
    return &#123;
      id,
      title: data.title,
      status: data.status,
      version: data.version,
      assignedAgent: data.assignedAgent,
      lastUpdated: data.lastUpdated
        ? data.lastUpdated.toDate().toISOString()
        : new Date().toISOString(),
      payload: data.payload || &#123;&#125;,
    &#125;;
  &#125;

  /**
   * Updates an agent task using optimistic concurrency control (OCC).
   * Prevents reasoning staleness across multi-turn LLM planning cycles.
   */
  async updateTaskAtomic(
    taskId: string,
    expectedVersion: number,
    updates: Partial&lt;Pick&lt;AgentTaskDocument, 'status' | 'assignedAgent' | 'payload'&gt;&gt;
  ): Promise&lt;MutationResult&gt; &#123;
    const taskRef = this.tasksCol.doc(taskId);

    try &#123;
      const result = await db.runTransaction(async (transaction) =&gt; &#123;
        const snapshot = await transaction.get(taskRef);

        if (!snapshot.exists) &#123;
          throw new Error(`Task $&#123;taskId&#125; does not exist.`);
        &#125;

        const currentData = snapshot.data() as AgentTaskDocument;

        if (currentData.version !== expectedVersion) &#123;
          const conflictError = new Error('VERSION_CONFLICT');
          (conflictError as any).currentState = this.formatTask(taskId, currentData);
          throw conflictError;
        &#125;

        const newVersion = currentData.version + 1;

        transaction.update(taskRef, &#123;
          ...updates,
          version: newVersion,
          lastUpdated: FieldValue.serverTimestamp(),
        &#125;);

        return newVersion;
      &#125;);

      return &#123; success: true, newVersion: result &#125;;
    &#125; catch (err: unknown) &#123;
      if (err instanceof Error &amp;&amp; err.message === 'VERSION_CONFLICT') &#123;
        const conflict = err as any;
        return &#123;
          success: false,
          error: `Concurrency collision on task $&#123;taskId&#125;. Version advanced before write.`,
          currentState: conflict.currentState,
        &#125;;
      &#125;

      const message = err instanceof Error ? err.message : 'Unknown transaction failure';
      return &#123; success: false, error: message &#125;;
    &#125;
  &#125;

  /**
   * Fetches fresh, query-indexed state directly from Firestore.
   */
  async getActiveTasksForAgent(
    agentId: string,
    limitCount = 10
  ): Promise&lt;AgentTaskResponse[]&gt; &#123;
    const querySnapshot = await this.tasksCol
      .where('assignedAgent', '==', agentId)
      .where('status', 'in', ['PENDING', 'IN_PROGRESS'])
      .orderBy('lastUpdated', 'desc')
      .limit(limitCount)
      .get();

    return querySnapshot.docs.map((doc) =&gt;
      this.formatTask(doc.id, doc.data() as AgentTaskDocument)
    );
  &#125;
&#125;</code></pre>
	</div>

	<blockquote><strong>Firestore composite index configuration:</strong> Queries combining equality filters, <code>in</code> operators, and custom ordering require a composite index. Deploy the following configuration in your <code>firestore.indexes.json</code>:</blockquote>

	<pre><code>&#123;
  "indexes": [
    &#123;
      "collectionGroup": "agent_tasks",
      "queryScope": "COLLECTION",
      "fields": [
        &#123; "fieldPath": "assignedAgent", "order": "ASCENDING" &#125;,
        &#123; "fieldPath": "status", "order": "ASCENDING" &#125;,
        &#123; "fieldPath": "lastUpdated", "order": "DESCENDING" &#125;
      ]
    &#125;
  ]
&#125;</code></pre>

	<p>
		For $0.00 offline development and testing, I run the entire transactional stack locally using the Firebase Local Emulator Suite (<code>firebase emulators:start --only firestore</code>) without provisioning cloud resources.
	</p>

	<blockquote><strong>Architectural takeaway:</strong> In stateful autonomous systems, I never use language models to maintain storage indexes. I offload state to atomic database transactions and rely on the database engine for indexing and concurrency control.</blockquote>

	<blockquote><strong>Architecture blueprint and spec:</strong> Inspect my complete <a href="https://ulukaya.dev/blueprints">Transactional Memory Blueprint &rarr;</a> or scaffold a repository-native specification tree with my <a href="https://ulukaya.dev/instruments#generators">noVibes Agent Spec Generator &rarr;</a></blockquote>

	<section class="bias-section" id="references">
		<h2>Industry validation and benchmarks</h2>
		<ul>
			<li><a href="https://arxiv.org/abs/2609.03619v1" target="_blank" rel="noopener">Remember and Reweight: Enhancing Multi-Agent Debate with Experience Memory and Confidence Estimation (Sep 2026, arXiv:2609.03619v1)</a>: Confirms that concurrent multi-agent state updates require explicit confidence-weighted state reconciliation to prevent conflicting memory overwrites.</li>
			<li><a href="https://arxiv.org/abs/2609.05261v1" target="_blank" rel="noopener">Trace2Tower: Transition-Aware EigenTrace Induction of Multi-Level Skills for LLM Agents (Sep 2026, arXiv:2609.05261v1)</a>: Confirms that multi-agent transition traces must enforce strict state-transition preconditions to prevent divergent execution graphs.</li>
			<li><a href="https://people.eecs.berkeley.edu/~brewer/cs262/concurrency-distributed-databases.pdf" target="_blank" rel="noopener">Bernstein and Goodman (1981): Concurrency Control in Distributed Database Systems (ACM Computing Surveys)</a></li>
			<li><a href="https://amturing.acm.org/p558-lamport.pdf" target="_blank" rel="noopener">Leslie Lamport (1978): Time, Clocks, and the Ordering of Events in a Distributed System (CACM)</a></li>
			<li><a href="https://jimgray.azurewebsites.net/papers/thetransactionconcept.pdf" target="_blank" rel="noopener">Jim Gray (1981): The Transaction Concept: Virtues and Limitations (VLDB)</a></li>
			<li><a href="https://firebase.google.com/docs/firestore/manage-data/transactions" target="_blank" rel="noopener">Cloud Firestore Transactions and Batched Writes</a></li>
			<li><a href="https://firebase.google.com/docs/app-check" target="_blank" rel="noopener">Firebase App Check Overview</a></li>
			<li><a href="https://cloud.google.com/run/docs/overview/what-is-cloud-run" target="_blank" rel="noopener">Cloud Run Serverless Compute Architecture</a></li>
			<li><a href="https://firebase.google.com/docs/emulator-suite" target="_blank" rel="noopener">Firebase Local Emulator Suite</a></li>
			<li><a href="https://genkit.dev" target="_blank" rel="noopener">Google Genkit Open Source Framework</a></li>
		</ul>
	</section>]]></content:encoded>
			<pubDate>Wed, 12 Aug 2026 00:00:00 GMT</pubDate>
			<dc:creator><![CDATA[Ibrahim Ulukaya]]></dc:creator>
			<category><![CDATA[AI Agents]]></category>
			<category><![CDATA[Firestore]]></category>
			<category><![CDATA[Cloud Run]]></category>
			<category><![CDATA[Firebase App Check]]></category>
			<category><![CDATA[Transactional Memory]]></category>
		</item>
		<item>
			<title><![CDATA[The Leaky Abstraction: Why GenAI Streaming Breaks Your JSON]]></title>
			<link>https://ulukaya.dev/posts/the-leaky-abstraction-vol1</link>
			<guid isPermaLink="true">https://ulukaya.dev/posts/the-leaky-abstraction-vol1</guid>
			<description><![CDATA[A TCP chunk split a 4-byte emoji in my LLM stream and the UI showed a U+FFFD diamond. I built a stateful Node.js reassembler that buffers partial UTF-8 bytes.]]></description>
			<content:encoded><![CDATA[<section id="introduction" data-part="INTRO" data-title="Introduction">
		<p class="lead-paragraph">
			When I streamed LLM token chunks over raw TCP/SSE sockets into a naive <code>new TextDecoder().decode(chunk)</code>, multi-byte UTF-8 characters (like 4-byte emojis or CJK characters sliced across network frame boundaries) <a href="https://ulukaya.dev/posts/the-leaky-abstraction-vol1#lab-stream-tear">rendered as garbled replacement diamonds</a> (<code>U+FFFD</code>) in my UI.
		</p>
		<p><em>Figure 1.</em> I split one 4-byte character across two chunk reads; decoding each chunk alone yields three U+FFFD, and one StringDecoder yields the character. <a href="https://ulukaya.dev/posts/the-leaky-abstraction-vol1">View the figure in the essay.</a></p>
		<p>
			Introductory tutorials assume instantaneous network calls and neatly formatted responses. In production distributed networks, I found that network packets do not respect character or JSON object boundaries. Building reliable streaming applications requires stateful, byte-level stream reassemblers in Node.js that buffer partial multi-byte sequences before parsing.
		</p>
		<blockquote><strong>The leaky reality:</strong> In my production AI streaming pipelines, network packets do not align with character or object boundaries. Treating incoming stream events as isolated strings corrupts Unicode characters and crashes on partial JSON payloads.</blockquote>
	</section>

	
	<h2>PART 01: The architectural gap and my two GenAI use cases</h2>

	<p>Before writing backend stream parsers, I evaluate whether my architecture even requires one. I encountered chunk boundary crashes when deploying backends for two distinct reasons:</p>

	<section class="bias-section">
		<h3>01. Hiding the API key (the anti-pattern)</h3>
		<p>
			I often see engineers proxy LLM calls through a Cloud Function solely to hide API keys from the client. <strong>When hiding keys is the only goal, deploying a backend proxy is an anti-pattern.</strong> It introduces unnecessary latency, compute costs, and stream parsing complexity.
		</p>
		<blockquote><strong>My client-side pattern:</strong> I use client SDKs like the <a href="https://firebase.google.com/docs/ai-logic" target="_blank" rel="noopener">Firebase AI Logic client SDK</a> paired with <a href="https://firebase.google.com/docs/app-check" target="_blank" rel="noopener">App Check</a>. This removes the API key from client code and allows my client app to call Gemini models directly and securely while the SDK handles chunk accumulation automatically.</blockquote>
	</section>

	<section class="bias-section">
		<h3>02. Trusted backend execution (the mandatory pattern)</h3>
		<p>
			When I build an agentic workflow that executes Retrieval-Augmented Generation against a private vector database, runs tool calls against third-party endpoints, or protects proprietary reasoning loops, that logic cannot live on the client.
		</p>
		<blockquote><strong>My trusted backend pattern:</strong> I route the request through a trusted environment like <strong>Cloud Functions for Firebase (Gen 2)</strong> or <strong>Firebase App Hosting</strong>.</blockquote>
		<p>
			In this second category, I intercept and parse the raw HTTP chunks manually in Node.js before streaming them back to the client. This requires explicit byte-level buffer management.
		</p>
	</section>

	<section class="bias-section" id="failure-anatomy">
		<h3>03. The failure anatomy: UTF-8 and JSON tearing</h3>
		<p>
			When I stream LLM completions using <a href="https://html.spec.whatwg.org/multipage/server-sent-events.html" target="_blank" rel="noopener">Server-Sent Events (SSE)</a> or HTTP chunked transfer encoding, my runtime receives binary <code>Buffer</code> or <code>Uint8Array</code> chunks. A critical mistake is assuming each incoming chunk represents a complete semantic token.
		</p>
		<p>
			<strong>A. Multi-byte UTF-8 truncation:</strong> <a href="https://datatracker.ietf.org/doc/html/rfc3629" target="_blank" rel="noopener">UTF-8 characters (IETF RFC 3629)</a> span 1 to 4 bytes (for example, <code>ğ</code> is 2 bytes and <code>🚀</code> is 4 bytes). When <a href="https://datatracker.ietf.org/doc/html/rfc9293" target="_blank" rel="noopener">TCP packet boundaries (IETF RFC 9293)</a> split across those bytes, calling <code>chunk.toString('utf-8')</code> on partial bytes produces <code>\uFFFD</code> (the Unicode replacement character), permanently corrupting the character in my output stream.
		</p>
		<p>
			<strong>B. Fragmented JSON payloads:</strong> In structured output and tool-calling modes, models emit JSON frames inside SSE events. A single JSON object frequently spans multiple network chunks. Calling <code>JSON.parse(chunk)</code> directly throws an immediate <code>SyntaxError: Unexpected end of JSON input</code>.
		</p>
	</section>

	
	<h2>PART 02: The old way vs. the new way</h2>

	<p>Here is how my naive parser compared to my production-ready stateful stream reassembler:</p>

	<div class="table-container">
		<table class="data-table">
			<thead>
				<tr>
					<th>Failure mode</th>
					<th>The old way (naive parser)</th>
					<th>The new way (stateful reassembler)</th>
				</tr>
			</thead>
			<tbody>
				<tr>
					<td><strong>UTF-8 byte split</strong></td>
					<td><code>chunk.toString('utf-8')</code> corrupts multi-byte sequences into <code>\uFFFD</code>.</td>
					<td><code>StringDecoder('utf-8')</code> holds incomplete bytes in my internal buffer until complete.</td>
				</tr>
				<tr>
					<td><strong>Split JSON payloads</strong></td>
					<td><code>JSON.parse(rawString)</code> crashes my runtime on partial frames.</td>
					<td>Delimiter-based line buffering isolates complete <code>data:</code> blocks before parsing.</td>
				</tr>
				<tr>
					<td><strong>Fused SSE packets</strong></td>
					<td>Processes first payload, dropping trailing events in the same chunk.</td>
					<td>Loops through all matched message delimiters (<code>\n\n</code>) within my combined buffer.</td>
				</tr>
				<tr>
					<td><strong>Error recovery</strong></td>
					<td>Stream crashes, terminating my client connection abruptly.</td>
					<td>Emits parsing errors gracefully while keeping the underlying transport alive.</td>
				</tr>
			</tbody>
		</table>
	</div>

	
	<div class="arch-diagram-container" id="stream-topology">
		<div class="arch-diagram-header">
			<span class="arch-diagram-title">Resilient streaming ingress stack</span>
			<span class="arch-diagram-subtitle">Byte decoding • Buffer accumulation • Event dispatch</span>
		</div>
		<div class="arch-layers-grid">
			<div class="arch-layer-card">
				<div class="arch-layer-top">
					<span class="arch-layer-badge badge-l1">LAYER 01: BYTE DECODING</span>
					<span class="arch-layer-spec">Node.js StringDecoder</span>
				</div>
				<div class="arch-layer-name">Multi-byte sequence state preserver</div>
				<p class="arch-layer-desc">
					Accepts raw Buffer/Uint8Array network chunks, transparently retaining incomplete UTF-8 bytes in memory across TCP packet splits.
				</p>
			</div>

			<div class="arch-flow-connector">▼ <span>Decoded UTF-8 stream fragments</span> ▼</div>

			<div class="arch-layer-card">
				<div class="arch-layer-top">
					<span class="arch-layer-badge badge-l2">LAYER 02: EVENT BUFFER</span>
					<span class="arch-layer-spec">Double newline accumulator</span>
				</div>
				<div class="arch-layer-name">SSE frame delimiter boundary parser</div>
				<p class="arch-layer-desc">
					Accumulates decoded text until the canonical <code>\n\n</code> delimiter is found, slicing complete frames and preserving partial tails.
				</p>
			</div>

			<div class="arch-flow-connector">▼ <span>Complete isolated frame payloads</span> ▼</div>

			<div class="arch-layer-card">
				<div class="arch-layer-top">
					<span class="arch-layer-badge badge-l3">LAYER 03: SAFE DISPATCH</span>
					<span class="arch-layer-spec">TransformStream push</span>
				</div>
				<div class="arch-layer-name">JSON safe parsing and event emission</div>
				<p class="arch-layer-desc">
					Executes isolated JSON parsing per block, gracefully falling back to raw text strings for unformatted tokens without aborting my stream.
				</p>
			</div>
		</div>
	</div>

	<p><a href="https://ulukaya.dev/posts/the-leaky-abstraction-vol1">Video: Terminal Proof: A 22-Byte Payload Split Mid-Emoji Yields 3 U+FFFD Replacement Characters. Watch it in the essay.</a></p>

	<p>
		To demonstrate this network physics failure in action, I built the byte-level streaming simulator below. Type multi-byte characters (such as emojis or accented text), shrink the simulated TCP chunk size, and observe raw UTF-8 tearing at chunk boundaries before <a href="https://ulukaya.dev/posts/the-leaky-abstraction-vol1#lab-stream-tear">healing through my streaming decoder:</a>
	</p>

	<p><a href="https://ulukaya.dev/posts/the-leaky-abstraction-vol1#lab-stream-tear">Interactive lab: stream-tear. Open the essay to run it.</a></p>

	
	<h2>PART 03: Production-ready TypeScript implementation</h2>

	<p>
		Below is my production-tested <code>SafeSseReassemblyStream</code> class. I deploy it directly as a Node.js <code>Transform</code> stream on top of Cloud Functions or Firebase App Hosting endpoints:
	</p>

	<pre><code>// Resilient SSE & UTF-8 Stream Reassembler for Node.js / Cloud Run
import &#123; StringDecoder &#125; from 'node:string_decoder';
import &#123; Transform, TransformCallback &#125; from 'node:stream';

export interface SseEvent&lt;T = unknown&gt; &#123;
  event?: string;
  data: T;
  id?: string;
  retry?: number;
&#125;

export class SafeSseReassemblyStream extends Transform &#123;
  private readonly decoder: StringDecoder;
  private buffer: string;

  constructor() &#123;
    super(&#123; readableObjectMode: true &#125;);
    this.decoder = new StringDecoder('utf8');
    this.buffer = '';
  &#125;

  public _transform(
    chunk: Buffer | Uint8Array | string, 
    _encoding: string, 
    callback: TransformCallback
  ): void &#123;
    try &#123;
      const text = typeof chunk === 'string' 
        ? chunk 
        : this.decoder.write(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
      
      this.buffer += text;

      let delimiterIndex: number;
      while ((delimiterIndex = this.buffer.indexOf('\n\n')) !== -1) &#123;
        const rawEvent = this.buffer.slice(0, delimiterIndex);
        this.buffer = this.buffer.slice(delimiterIndex + 2);

        const parsed = this.parseSseBlock(rawEvent);
        if (parsed) &#123;
          this.push(parsed);
        &#125;
      &#125;

      callback();
    &#125; catch (error) &#123;
      callback(error instanceof Error ? error : new Error(String(error)));
    &#125;
  &#125;

  public _flush(callback: TransformCallback): void &#123;
    try &#123;
      this.buffer += this.decoder.end();

      if (this.buffer.trim().length &gt; 0) &#123;
        const parsed = this.parseSseBlock(this.buffer);
        if (parsed) &#123;
          this.push(parsed);
        &#125;
      &#125;
      this.buffer = '';
      callback();
    &#125; catch (error) &#123;
      callback(error instanceof Error ? error : new Error(String(error)));
    &#125;
  &#125;

  private parseSseBlock(rawBlock: string): SseEvent | null &#123;
    const lines = rawBlock.split(/\r?\n/);
    let eventType: string | undefined;
    let id: string | undefined;
    let retry: number | undefined;
    const dataLines: string[] = [];

    for (const line of lines) &#123;
      if (line.startsWith(':') || line.trim() === '') continue;

      const colonIdx = line.indexOf(':');
      if (colonIdx === -1) continue;

      const field = line.slice(0, colonIdx).trim();
      const value = line.slice(colonIdx + 1).replace(/^\s/, '');

      switch (field) &#123;
        case 'data': dataLines.push(value); break;
        case 'event': eventType = value; break;
        case 'id': id = value; break;
        case 'retry': retry = Number.parseInt(value, 10); break;
      &#125;
    &#125;

    if (dataLines.length === 0) return null;

    const rawData = dataLines.join('\n');
    let parsedData: unknown;

    try &#123;
      const cleaned = rawData.trim();
      const parsedData = cleaned.startsWith('{') || cleaned.startsWith('[') 
        ? JSON.parse(cleaned) 
        : rawData;
    &#125; catch &#123;
      parsedData = rawData;
    &#125;

    return &#123;
      event: eventType,
      data: parsedData,
      id,
      retry,
    &#125;;
  &#125;
&#125;</code></pre>
	</div>

	<blockquote><strong>Architectural takeaway:</strong> I never assume network chunks align with character or JSON boundaries. I preserve raw bytes across TCP boundaries using <code>StringDecoder</code> and decouple transport delimiters from application payloads to prevent stream parsing crashes in production.</blockquote>

	<blockquote><strong>Architecture blueprint and spec:</strong> Inspect my complete <a href="https://ulukaya.dev/blueprints">Deterministic Agent Runtime Blueprint &rarr;</a> or test my streaming token burn with the <a href="https://ulukaya.dev/instruments#calculators">AI Tokenomics Solvency Calculator &rarr;</a></blockquote>

	<section class="bias-section" id="references">
		<h2>Industry validation and benchmarks</h2>
		<ul>
			<li>
				<a href="https://arxiv.org/abs/2608.27658v1" target="_blank" rel="noopener">When Tokenizers Fail: Byte-Level Chunking for Zero-Shot Transfer to Low-Resource Languages (Aug 2026)</a>: Confirms that byte-level boundary misalignment across subword tokenizers and transport streams corrupts multi-byte UTF-8 sequences unless explicit stateful byte-boundary buffering is enforced.
			</li>
			<li>
				<a href="https://arxiv.org/abs/2609.03079v1" target="_blank" rel="noopener">LeanStream: A Speculate-and-Refine Streaming Framework for Efficient on-Device LLM Inference (Sep 2026)</a>: Confirms that streaming token boundaries across network and storage buffers require stateful buffer alignment to prevent boundary corruption.
			</li>
			<li><a href="https://datatracker.ietf.org/doc/html/rfc9293" target="_blank" rel="noopener">IETF RFC 9293: Transmission Control Protocol (TCP) Specification</a></li>
			<li><a href="https://datatracker.ietf.org/doc/html/rfc3629" target="_blank" rel="noopener">IETF RFC 3629: UTF-8, a transformation format of ISO 10646</a></li>
			<li><a href="https://html.spec.whatwg.org/multipage/server-sent-events.html" target="_blank" rel="noopener">WHATWG HTML Standard: Server-Sent Events (SSE) Protocol</a></li>
			<li><a href="https://nodejs.org/api/string_decoder.html" target="_blank" rel="noopener">Node.js StringDecoder Core API Specification</a></li>
			<li><a href="https://firebase.google.com/docs/ai-logic" target="_blank" rel="noopener">Firebase AI Logic Documentation</a></li>
			<li><a href="https://firebase.google.com/docs/app-check" target="_blank" rel="noopener">Firebase App Check Attestation</a></li>
			<li><a href="https://cloud.google.com/run/docs/triggering/https-request" target="_blank" rel="noopener">Cloud Run Streaming and HTTP/2 Ingress</a></li>
		</ul>
	</section>]]></content:encoded>
			<pubDate>Mon, 10 Aug 2026 00:00:00 GMT</pubDate>
			<dc:creator><![CDATA[Ibrahim Ulukaya]]></dc:creator>
			<category><![CDATA[GenAI]]></category>
			<category><![CDATA[Node.js]]></category>
			<category><![CDATA[Streaming]]></category>
			<category><![CDATA[TCP]]></category>
			<category><![CDATA[App Hosting]]></category>
		</item>
		<item>
			<title><![CDATA[When the Browser Hangs Up: Client-Side Defense for Agent Streams]]></title>
			<link>https://ulukaya.dev/posts/client-runtime-agent-resilience</link>
			<guid isPermaLink="true">https://ulukaya.dev/posts/client-runtime-agent-resilience</guid>
			<description><![CDATA[Pushing 200 tokens a second into React state cut my UI to 8 FPS. Part 3 of The Leaky Abstraction defends the client edge with Firebase AI Logic and Cloud Run.]]></description>
			<content:encoded><![CDATA[<section id="introduction" data-part="INTRO" data-title="Introduction">
		<p class="lead-paragraph">
			When I streamed 200 tokens/second directly into a React state variable (<code>setText(prev =&gt; prev + token)</code>), my browser main thread locked up. Re-rendering the React virtual DOM 200 times per second triggered massive garbage collection (GC) pauses and dropped frame rates to 8 FPS. Over 80% of perceived AI agent failures in my web and mobile apps are silent transport drops, NAT idle timeouts, and client-side memory exhaustion that occur long before a prompt reaches a model.
		</p>
		<p><em>Figure 1.</em> The proxy idle timer fires at 10 s while the model thinks for 6 to 12 s; a :keep-alive comment every 4 s keeps that timer from ever reaching 10. <a href="https://ulukaya.dev/posts/client-runtime-agent-resilience">View the figure in the essay.</a></p>
		<p>
			When an interactive agent hangs mid-task, I used to default to tweaking system prompts, adjusting temperature, or swapping checkpoints. In my production client architectures, however, reliability breaks at the transport boundary: dropped WebSocket frames during multi-second reasoning pauses, buffer thrashing when streaming large contexts into frontend state, and orphaned tool side-effects. Defending my client runtimes requires WebSocket keep-alives, transactional stream reassembly, and App Check hardware attestation.
		</p>
		
		<blockquote><strong>My Production Reality:</strong> An autonomous agent fails at the connection and state boundary long before it fails in model reasoning. When I deploy multi-turn agents to web and mobile users, I build my resilience layer at the client edge using <a href="https://firebase.google.com/docs/app-check" target="_blank" rel="noopener">Firebase AI Logic</a> on the device and <a href="https://cloud.google.com/run/docs/triggering/https-request" target="_blank" rel="noopener">Google Cloud Run</a> at my backend gateway.</blockquote>
	</section>

	
	<h2>PART 01: Firebase AI Logic vs. Cloud Run: The full-stack topology</h2>

	<p>
		A common point of confusion I encounter is whether to execute agent loops entirely on the client or route through a custom backend container. In my production architectures, I pair them as the two complementary halves of a resilient runtime:
	</p>

	
	<div class="arch-diagram-container">
		<div class="arch-diagram-header">
			<span class="arch-diagram-title">Full-Stack Agent Execution Topology</span>
			<span class="arch-diagram-subtitle">Client Edge vs. Serverless Gateway</span>
		</div>
		<div class="arch-layers-grid">
			<div class="arch-layer-card">
				<div class="arch-layer-top">
					<span class="arch-layer-badge badge-l1">CLIENT TIER</span>
					<span class="arch-layer-spec">Firebase AI Logic SDK (Web / iOS / Android)</span>
				</div>
				<div class="arch-layer-name">Client-Native Streaming and Device Attestation</div>
				<p class="arch-layer-desc">
					Manages direct token streaming to my user viewport, cryptographically validates device health via <a href="https://firebase.google.com/docs/app-check" target="_blank" rel="noopener">Firebase App Check</a>, and throttles UI rendering at 60fps to eliminate DOM thrashing.
				</p>
			</div>
			<div class="arch-flow-connector">▼ <span>HTTPS / Event-Stream with JWT Attestation</span> ▼</div>
			<div class="arch-layer-card">
				<div class="arch-layer-top">
					<span class="arch-layer-badge badge-l2">BACKEND GATEWAY</span>
					<span class="arch-layer-spec">Google Cloud Run (Serverless Container)</span>
				</div>
				<div class="arch-layer-name">Multi-Step Tool Loops and Private Secret Execution</div>
				<p class="arch-layer-desc">
					Executes my multi-step tool calls, guards private API keys, persists state to <a href="https://firebase.google.com/docs/firestore/manage-data/transactions" target="_blank" rel="noopener">Cloud Firestore</a>, and emits synthetic keep-alive heartbeat frames during deep model reasoning.
				</p>
			</div>
		</div>
	</div>

	
	<h2>FAILURE MODES: The three production streaming failure modes</h2>

	<p>
		When my agent executes multi-step reasoning or calls external tools, my HTTP/2 or WebSocket connection remains open for 10 to 45 seconds while tokens stream sequentially. This exposes three distinct failure modes:
	</p>

	<section class="bias-section">
		<h3>01. The intermediate NAT and proxy idle timeout</h3>
		<p>
			During deep reasoning loops or multi-tool calling sequences, my backend model takes 6 to 12 seconds before emitting the next token chunk. Cellular radio handoffs, corporate firewalls, and ingress reverse proxies (such as Envoy or Cloud Load Balancing) aggressively terminate <a href="https://datatracker.ietf.org/doc/html/rfc9113" target="_blank" rel="noopener">HTTP/2 streams (IETF RFC 9113)</a> that show zero wire activity for more than 10 seconds. My frontend receives an <code>ECONNRESET</code> or silent EOF, causing my client UI to hang indefinitely.
		</p>
		<p>
			To simulate how intermediate network infrastructure silently severs quiescent streams, trigger my timeout simulator below. Hold a connection open without active frames to observe proxy termination, then enable application-layer heartbeat keep-alives to preserve transport state:
		</p>
	</section>

	<p><a href="https://ulukaya.dev/posts/client-runtime-agent-resilience#lab-idle-timeout">Interactive lab: idle-timeout. Open the essay to run it.</a></p>

	<p><a href="https://ulukaya.dev/posts/client-runtime-agent-resilience">Video: Main-Thread React Render Thrashing vs 60Hz Ring-Buffer Worker Streaming Proof. Watch it in the essay.</a></p>

	<section class="bias-section">
		<h3>02. In-memory component state thrashing</h3>
		<p>
			My initial frontend implementation bound the <a href="https://ulukaya.dev/posts/the-leaky-abstraction-vol1">raw streaming chunk handler</a> directly to reactive framework state:
		</p>

		<pre><code>// ❌ ANTI-PATTERN: Re-rendering 50-file diffs on every token chunk
onChunk((chunk) =&gt; &#123;
  setMessages((prev) =&gt; [...prev.slice(0, -1), prev.at(-1) + chunk]);
&#125;);</code></pre>

		<p>
			When my agent streams a 65k-token code diff or architectural review, updating React or Vue virtual DOM nodes 200 times per second triggers massive memory garbage collection pauses, dropping my frame rates to 8 FPS on mobile viewports and crashing client tab processes. As documented in <a href="https://arxiv.org/abs/2609.01082v1" target="_blank" rel="noopener">Update for Decisions, Not Freshness: Goal-Oriented Status Updating at the Network Edge (Sep 2026)</a>, batching state updates based on UI decision intervals (16.6 ms display frames) rather than raw packet arrival prevents client buffer saturation.
		</p>
	</section>

	<section class="bias-section">
		<h3>03. The orphaned tool state execution</h3>
		<p>
			If my network connection drops while the server executes Step 3 of a 4-step tool chain (for example, creating a <a href="https://firebase.google.com/docs/firestore/manage-data/transactions" target="_blank" rel="noopener">Cloud Firestore</a> document before calling an external Stripe webhook), my client assumes the entire operation failed and automatically resubmits Turn 1. Furthermore, as shown in <a href="https://arxiv.org/abs/2609.01338v1" target="_blank" rel="noopener">mzCache: On-Device LLM Memory Management under Multitasking (Sep 2026)</a>, mobile OS backgrounding and memory pressure frequently disrupt active client buffers, requiring decoupled background state restoration. Without client-enforced idempotency keys, resubmitting turns produces duplicate database writes and inconsistent backend state.
		</p>
	</section>

	
	<h2>PART 02: The three-layer client-edge defense architecture</h2>

	<p>
		My production agent runtime enforces a strict separation between network transport, local persistence, and viewport rendering:
	</p>

	
	<div class="arch-diagram-container">
		<div class="arch-diagram-header">
			<span class="arch-diagram-title">3-Layer Client-Edge Defense Architecture</span>
			<span class="arch-diagram-subtitle">RFC 8895 • Web Streams API • App Check</span>
		</div>
		<div class="arch-layers-grid">
			<div class="arch-layer-card">
				<div class="arch-layer-top">
					<span class="arch-layer-badge badge-l1">LAYER 01: TRANSPORT</span>
					<span class="arch-layer-spec"><a href="https://html.spec.whatwg.org/multipage/server-sent-events.html" target="_blank" rel="noopener">WHATWG SSE</a> and Cloud Run Streaming</span>
				</div>
				<div class="arch-layer-name">Synthetic Keep-Alives and Resume Tokens</div>
				<p class="arch-layer-desc">
					My Cloud Run gateway emits lightweight SSE heartbeat comment frames (<code>:keep-alive\n\n</code>) every 4 seconds to keep intermediate TCP sockets active during deep reasoning, while my client tracks byte-level stream offsets (<code>X-Stream-Resume-Offset</code>) for instant reconnection.
				</p>
			</div>

			<div class="arch-layer-card">
				<div class="arch-layer-top">
					<span class="arch-layer-badge badge-l2">LAYER 02: STATE MACHINE</span>
					<span class="arch-layer-spec"><a href="https://streams.spec.whatwg.org/" target="_blank" rel="noopener">Web Streams API ReadableStream</a></span>
				</div>
				<div class="arch-layer-name">Throttled Double-Buffering and Local Queues</div>
				<p class="arch-layer-desc">
					I decouple the raw socket byte reader from my UI rendering engine. Incoming chunks stream into an in-memory ring buffer, dispatching throttled updates to my virtual DOM at a smooth 60fps render tick to prevent client memory GC pauses.
				</p>
			</div>

			<div class="arch-layer-card">
				<div class="arch-layer-top">
					<span class="arch-layer-badge badge-l3">LAYER 03: RECONCILIATION</span>
					<span class="arch-layer-spec">Firebase App Check and Firestore Transactions</span>
				</div>
				<div class="arch-layer-name">Cryptographic Attestation and Atomic Idempotency</div>
				<p class="arch-layer-desc">
					I authenticate client device integrity with <a href="https://firebase.google.com/docs/app-check" target="_blank" rel="noopener">Firebase App Check</a> JWT tokens, and bind each multi-turn request to atomic Firestore transaction IDs, verifying whether previous turns executed before retrying.
				</p>
			</div>
		</div>
	</div>

	
	<h2>CLIENT SPEC: Client implementation: Resilient stream consumer</h2>

	<p>
		Below is my production TypeScript implementation for client-side streaming using <strong>Firebase App Check</strong> and exponential backoff reconnection:
	</p>

	<pre><code>import &#123; getToken &#125; from "firebase/app-check";

/**
 * Resilient Stream Consumer with Reconnection Offsets &amp; App Check Attestation
 */
export class ResilientAgentConsumer &#123;
  private resumeOffset = 0;
  private maxRetries = 3;

  constructor(
    private readonly endpoint: string,
    private readonly appCheckInstance: any,
    private readonly turnId: string
  ) &#123;&#125;

  public async executeStream(
    prompt: string,
    onRenderTick: (text: string) =&gt; void
  ): Promise&lt;void&gt; &#123;
    let attempt = 0;
    let accumulatedText = "";

    while (attempt &lt; this.maxRetries) &#123;
      try &#123;
        // Fetch fresh App Check token with offline-safe fallback
        let appCheckToken = "";
        try &#123;
          appCheckToken = (await getToken(this.appCheckInstance, false)).token;
        &#125; catch (tokenErr) &#123;
          throw new Error(`App Check attestation failed (offline or unverified): $&#123;tokenErr&#125;`);
        &#125;

        const response = await fetch(this.endpoint, &#123;
          method: "POST",
          headers: &#123;
            "Content-Type": "application/json",
            "X-Firebase-AppCheck": appCheckToken,
            "X-Stream-Turn-Id": this.turnId,
            "X-Stream-Resume-Offset": String(this.resumeOffset),
          &#125;,
          body: JSON.stringify(&#123; prompt, resumeFrom: this.resumeOffset &#125;),
        &#125;);

        if (!response.ok || !response.body) &#123;
          throw new Error(`HTTP Transport Failure: $&#123;response.status&#125;`);
        &#125;

        const reader = response.body.getReader();
        const decoder = new TextDecoder("utf-8");
        let lineBuffer = "";

        while (true) &#123;
          const &#123; done, value &#125; = await reader.read();
          if (done) break;

          lineBuffer += decoder.decode(value, &#123; stream: true &#125;);
          const lines = lineBuffer.split(/\r?\n/);
          lineBuffer = lines.pop() || "";

          for (const line of lines) &#123;
            // Filter out W3C SSE comment frames (:keep-alive) even when packet fragmented
            if (line.startsWith(":") || !line.trim()) continue;
            accumulatedText += line + "\n";
          &#125;
          this.resumeOffset += value.byteLength;

          // Throttled UI dispatch to prevent client memory GC thrashing
          onRenderTick(accumulatedText);
        &#125;

        // Stream completed successfully
        return;

      &#125; catch (err) &#123;
        attempt++;
        if (attempt &gt;= this.maxRetries) &#123;
          throw new Error(`Agent stream terminated after $&#123;this.maxRetries&#125; retries: $&#123;err&#125;`);
        &#125;

        // Exponential backoff with jitter before resuming from last byte offset
        const backoffMs = Math.pow(2, attempt) * 500 + Math.random() * 200;
        await new Promise((res) =&gt; setTimeout(res, backoffMs));
      &#125;
    &#125;
  &#125;
&#125;</code></pre>
	</div>

	
	<h2>PART 03: Backend gateway on Cloud Run (Node.js and Firebase Admin)</h2>

	<p>
		Below is my production Express middleware running on Cloud Run, enforcing device attestation via Firebase App Check and emitting RFC-compliant keep-alive comment frames to prevent reverse-proxy timeouts:
	</p>

	<pre><code>import &#123; Request, Response, NextFunction &#125; from "express";
import &#123; getAppCheck &#125; from "firebase-admin/app-check";

/**
 * Cloud Run Middleware: App Check Token Verification
 */
export async function verifyAppCheckMiddleware(
  req: Request, 
  res: Response, 
  next: NextFunction
) &#123;
  const appCheckToken = req.header("X-Firebase-AppCheck");

  if (!appCheckToken) &#123;
    return res.status(401).json(&#123; error: "Unauthorized: Missing App Check token" &#125;);
  &#125;

  try &#123;
    const claims = await getAppCheck().verifyToken(appCheckToken);
    (req as any).appCheckClaims = claims;
    next();
  &#125; catch (err) &#123;
    return res.status(401).json(&#123; error: "Unauthorized: Invalid App Check token" &#125;);
  &#125;
&#125;

/**
 * Configures Cloud Run Streaming Headers &amp; Heartbeat Keep-Alives
 */
export function setupStreamingHeaders(res: Response): NodeJS.Timeout &#123;
  res.setHeader("Content-Type", "text/event-stream; charset=utf-8");
  res.setHeader("Cache-Control", "no-cache, no-transform");
  res.setHeader("Connection", "keep-alive");
  res.setHeader("X-Accel-Buffering", "no"); // Disable proxy buffering on Cloud Run / Envoy

  // Emit SSE keep-alive heartbeat comment frame every 4 seconds
  const heartbeatTimer = setInterval(() =&gt; &#123;
    if (!res.writableEnded) &#123;
      res.write(":keep-alive\n\n");
    &#125;
  &#125;, 4000);

  res.on("close", () =&gt; clearInterval(heartbeatTimer));
  res.on("finish", () =&gt; clearInterval(heartbeatTimer));

  return heartbeatTimer;
&#125;</code></pre>
	</div>

	<blockquote><strong>My Architectural Takeaway:</strong> I isolate transport failures from model reasoning failures. I pair Firebase App Check on the client with the Firebase Admin SDK on Cloud Run, double-buffer my client rendering to protect the virtual DOM, and emit synthetic keep-alive comment frames (<code>:keep-alive\n\n</code>) to preserve long streaming sessions at $0.00 additional compute overhead.</blockquote>

	<blockquote><strong>Architecture Blueprint and Spec:</strong> Inspect my complete <a href="https://ulukaya.dev/blueprints">Deterministic Agent Runtime Blueprint &rarr;</a> or scaffold a production-ready specification tree with my <a href="https://ulukaya.dev/instruments#generators">noVibes Agent Spec Generator &rarr;</a></blockquote>

	<section class="bias-section" id="references">
		<h2>Industry validation and benchmarks</h2>
		<ul>
			<li><a href="https://arxiv.org/abs/2609.01338v1" target="_blank" rel="noopener">mzCache: On-Device LLM Memory Management under Multitasking (Sep 2026)</a>: Confirms that mobile OS backgrounding and memory pressure disrupt active client buffers, requiring decoupled background state restoration.</li>
			<li><a href="https://arxiv.org/abs/2609.01082v1" target="_blank" rel="noopener">Update for Decisions, Not Freshness: Goal-Oriented Status Updating at the Network Edge (Sep 2026)</a>: Confirms that batching state updates based on UI decision intervals (16.6 ms frames) rather than raw packet arrival prevents client buffer saturation.</li>
		</ul>
	</section>

	<h2>REFERENCES: Primary research and documentation</h2>

	<ul>
		<li><a href="https://datatracker.ietf.org/doc/html/rfc9113" target="_blank" rel="noopener">IETF RFC 9113: HTTP/2 Standard (Stream Multiplexing and Flow Control)</a></li>
		<li><a href="https://html.spec.whatwg.org/multipage/server-sent-events.html" target="_blank" rel="noopener">WHATWG HTML Standard: Server-Sent Events (SSE) Protocol</a></li>
		<li><a href="https://streams.spec.whatwg.org/" target="_blank" rel="noopener">WHATWG Streams Standard: ReadableStream and Backpressure Handling</a></li>
		<li><a href="https://firebase.google.com/docs/app-check" target="_blank" rel="noopener">Firebase App Check Overview and Attestation Architecture</a></li>
		<li><a href="https://cloud.google.com/run/docs/triggering/https-request" target="_blank" rel="noopener">Google Cloud Run Response Streaming Configuration</a></li>
		<li><a href="https://firebase.google.com/docs/firestore/manage-data/transactions" target="_blank" rel="noopener">Cloud Firestore Transactions and Batched Writes</a></li>
	</ul>]]></content:encoded>
			<pubDate>Fri, 07 Aug 2026 00:00:00 GMT</pubDate>
			<dc:creator><![CDATA[Ibrahim Ulukaya]]></dc:creator>
			<category><![CDATA[Firebase AI Logic]]></category>
			<category><![CDATA[Cloud Run]]></category>
			<category><![CDATA[Firebase App Check]]></category>
			<category><![CDATA[Firestore]]></category>
			<category><![CDATA[WebSockets]]></category>
		</item>
		<item>
			<title><![CDATA[Why Your AI Agent Agrees With Everything: 10 Production Failure Modes]]></title>
			<link>https://ulukaya.dev/posts/ten-cognitive-biases-ai-agents</link>
			<guid isPermaLink="true">https://ulukaya.dev/posts/ten-cognitive-biases-ai-agents</guid>
			<description><![CDATA[My review agent approved a regex, then reversed itself when I asked the opposite question. I map the 10 biases behind that and one platform primitive per bias.]]></description>
			<content:encoded><![CDATA[<section id="introduction" data-part="INTRO" data-title="Introduction" data-bias="">
		<p class="lead-paragraph">
			When I asked my code review agent "Is this regex safe against ReDoS?", it agreed with me and approved the PR. When I asked the exact same agent in a new session "Why is this regex vulnerable to catastrophic backtracking?", it reversed its stance completely and apologized, exposing severe RLHF sycophancy bias. Single-turn hallucinations are trivial compared to the failure modes I encounter in stateful, multi-turn AI agents. When my autonomous systems operate with persistent memory and tool access, they enter sycophantic echo chambers, path-dependent deadlocks, and infinite action loops that mimic human cognitive biases.
		</p>
		<p><em>Figure 1.</em> The same regex got approved when I asked if it was safe and rejected when I asked why it was vulnerable; a second persona that must name two failure modes blocks approval either way. <a href="https://ulukaya.dev/posts/ten-cognitive-biases-ai-agents">View the figure in the essay.</a></p>
		<p>
			When I move my architecture from single-turn prompts to <strong>stateful, memory-augmented AI agents</strong>, failure is rarely an isolated model defect. It is an emergent systemic breakdown: attention degradation across extended context, uncritical agreement with user assumptions, and greedy sampling traps. Eliminating these failures in my production pipelines requires mechanical platform primitives that enforce objective verification at runtime.
		</p>
		
		<blockquote><strong>The agentic shift:</strong> An autonomous agent fails differently than a raw language model. It develops structural blind spots arising from the interaction between attention curves, state accumulation, greedy sampling, and human feedback loops. Left unmanaged, these biases cause my production agents to silently drop critical user constraints, loop on failing tool chains, agree with flawed architectural premises, and exhaust cloud API budgets.</blockquote>

		<p>
			Here is my architectural analysis of the 10 cognitive biases in autonomous agent systems, backed by empirical 2026 research, and the concrete <a href="https://firebase.google.com" target="_blank" rel="noopener">Firebase</a> and <a href="https://cloud.google.com" target="_blank" rel="noopener">Google Cloud</a> platform primitives I use to solve them.
		</p>
	</section>

	
	<h2>PART 01: Epistemic and memory biases: Grounding agents in truth</h2>

	<section id="bias-1" class="bias-section" data-part="PART 01" data-title="Epistemic and Memory Biases" data-bias="Bias 01: Context Attention Loss">
		<h3>01. Context attention degradation (the "lost-in-the-middle" drop)</h3>
		<p>
			<strong>The failure mode:</strong> Large context windows obscure attention non-uniformity. In my multi-turn traces, transformer self-attention forms a U-curve where tokens in the middle 40% to 70% of the context window receive weaker attention than the system prompt at the beginning and the most recent turn at the end. An agent given a 50,000-token conversation history silently ignores constraints I established early in the session.
		</p>
		<p>
			<strong>The architectural fix:</strong> I stop passing unbounded conversational history arrays to the LLM. Instead, I store conversational state, user profiles, and active constraints as discrete documents in <a href="https://firebase.google.com/docs/firestore" target="_blank" rel="noopener">Cloud Firestore</a>. I use <a href="https://firebase.google.com/docs/firestore/query-data/queries" target="_blank" rel="noopener">Firestore Structured Queries</a> to retrieve only the exact entity records relevant to the immediate intent. I pin immutable system rules and tool definitions in high-speed memory using <a href="https://cloud.google.com/vertex-ai/generative-ai/docs/context-cache/context-cache-overview" target="_blank" rel="noopener">Vertex AI Context Caching</a>, reducing my token costs by up to 75% while keeping core behavioral invariants at the high-attention front of the context window. For $0.00 offline verification, I test my query filters locally against the Firebase Local Emulator Suite.
		</p>

		<pre><code>// Query specific entity constraints instead of passing raw unbounded history
import &#123; Firestore &#125; from "@google-cloud/firestore";

const db = new Firestore();

const constraintsRef = db.collection("agent_sessions").doc(sessionId).collection("active_constraints");
const snapshot = await constraintsRef.where("status", "==", "ENFORCED").limit(50).get();
const contextTokens = snapshot.docs.map(doc =&gt; doc.data().rule_text).join("\n");</code></pre>
	</section>

	<section id="bias-2" class="bias-section" data-part="PART 01" data-title="Epistemic and Memory Biases" data-bias="Bias 02: Daisy-Chain Summarization">
		<h3>02. Daisy-chain summarization decay (compression entropy)</h3>
		<p>
			<strong>The failure mode:</strong> When my background heartbeats or memory systems summarize previous daily summaries (A ➔ Summary(A) ➔ Summary(Summary(A))), mathematical entropy increases across iterations. Specific bug IDs, exact error codes, URLs, and edge constraints are stripped out, leaving behind generic platitudes.
		</p>
		<p>
			<strong>The architectural fix:</strong> I enforce <strong>immutable source pointers and raw signal ingestion</strong>. My background tasks query primary APIs directly (live calendar events, unread inbox threads, issue trackers) rather than re-summarizing previous summaries. In persistent memory, I store raw, immutable event records with unique content hashes in <a href="https://firebase.google.com/docs/firestore" target="_blank" rel="noopener">Cloud Firestore</a> or Cloud Storage, and pass lightweight pointer references across execution cycles so my agents re-read original source records on demand rather than relying on compressed text chains.
		</p>
	</section>

	<section id="bias-3" class="bias-section" data-part="PART 01" data-title="Epistemic and Memory Biases" data-bias="Bias 03: Algorithmic Sycophancy">
		<h3>03. Algorithmic sycophancy (the false-validation loop)</h3>
		<p>
			<strong>The failure mode:</strong> Reinforcement learning from human feedback (RLHF) incentivizes user agreement over objective critique. When I ask an ungrounded agent whether a flawed architecture looks complete, it validates my design rather than identifying missing service level agreements or security boundaries.
		</p>
		<p>
			<strong>The architectural fix:</strong> I configure <a href="https://cloud.google.com/vertex-ai/generative-ai/docs/grounding/overview" target="_blank" rel="noopener">Vertex AI Search Grounding</a> to force model outputs to evaluate claims against authoritative enterprise data repositories or live Google Search data with verifiable citations. Additionally, I enforce multi-persona adversarial evaluation in my system prompts, requiring the model to identify at least two explicit failure modes or missing trade-offs before issuing validation.
		</p>
		<p>
			To test this cognitive failure mode interactively, run my sycophancy simulation below. Pose questions containing subtle architecture anti-patterns and compare an agreeable model against my adversarially prompted verifier:
		</p>
	</section>

	<p><a href="https://ulukaya.dev/posts/ten-cognitive-biases-ai-agents#lab-sycophancy-loop">Interactive lab: sycophancy-loop. Open the essay to run it.</a></p>

	<p><a href="https://ulukaya.dev/posts/ten-cognitive-biases-ai-agents">Video: Single-Agent Sycophancy Collapse vs Adversarial Multi-Agent Debate Triad Proof. Watch it in the essay.</a></p>

	<section id="bias-4" class="bias-section" data-part="PART 01" data-title="Epistemic and Memory Biases" data-bias="Bias 04: Self-Referential Memory">
		<h3>04. Self-referential memory loops (echo chambers)</h3>
		<p>
			<strong>The failure mode:</strong> When my agent writes an unverified draft assumption to a local markdown file, reads that file in subsequent sessions, and cites its own past output as authoritative proof, it creates a self-referential feedback loop where unverified data is treated as ground truth.
		</p>
		<p>
			<strong>The architectural fix:</strong> I implement <strong>epistemic provenance tagging and dual-storage separation</strong>. I tag all stored agent records with explicit epistemic states (<code>HYPOTHESIS</code>, <code>EMPIRICAL_OBSERVATION</code>, <code>VERIFIED_GROUND_TRUTH</code>) along with confidence scores and expiration TTLs in <a href="https://firebase.google.com/docs/firestore" target="_blank" rel="noopener">Cloud Firestore</a> or <a href="https://firebase.google.com/docs/data-connect" target="_blank" rel="noopener">Firebase Data Connect</a>. I enforce a strict invariant: a <code>HYPOTHESIS</code> can never be cited as authority or promoted to permanent truth without passing an external verification check (such as a live tool execution or user confirmation).
		</p>
	</section>

	
	<h2>PART 02: Execution and tooling biases: Eliminating runaway loops</h2>

	<section id="bias-5" class="bias-section" data-part="PART 02" data-title="Execution and Tooling Biases" data-bias="Bias 05: Tool-Selection Bias">
		<h3>05. Tool-selection bias (law of the instrument)</h3>
		<p>
			<strong>The failure mode:</strong> My agents exhibit an affinity for complex tools they have recently used. Unconstrained agents over-complicate tasks, spawning complex multi-agent background swarms with custom scripts when a single direct API call is sufficient.
		</p>
		<p>
			<strong>The architectural fix:</strong> I define strict execution hierarchies: native direct APIs first, standardized tools exposed via the open <a href="https://modelcontextprotocol.io/introduction" target="_blank" rel="noopener">Model Context Protocol (MCP)</a> second, and dynamic code execution strictly as a last resort. I host tool backends on serverless container infrastructure such as <a href="https://cloud.google.com/run/docs" target="_blank" rel="noopener">Google Cloud Run</a> to provide isolated, auto-scaling tool execution environments with strict per-invocation timeouts.
		</p>
	</section>

	<section id="bias-6" class="bias-section" data-part="PART 02" data-title="Execution and Tooling Biases" data-bias="Bias 06: Path Dependency">
		<h3>06. Path dependency and cascading error loops</h3>
		<p>
			<strong>The failure mode:</strong> When Step 2 of a 5-step execution plan fails, my LLMs suffer from path dependency: they repeatedly retry local variations of Step 2 instead of backtracking to question if Step 1 selected the wrong data source.
		</p>
		<p>
			<strong>The architectural fix:</strong> I implement an explicit <strong>2-failure backtracking threshold (Tree-of-Thought / MCTS)</strong> in my orchestrator. If two consecutive tool invocations fail on the same branch, my runtime aborts the sub-branch, pops the execution stack, and re-evaluates Step 1 assumptions. I isolate exploratory agent code execution inside ephemeral Cloud Run session sandboxes and dispatch asynchronous jobs with dead-letter isolation.
		</p>
		<blockquote><strong>The client-side observability blind spot:</strong> In my web-based agent apps (streaming generative UI, browser-side tool executions in Next.js or React), backend distributed tracing (Google Cloud Trace, Genkit) only detects server-side model failures. If an unhandled promise rejection or malformed JSON payload crashes the browser runtime, my user experiences a frozen state while backend logs appear healthy. Addressing execution failure loops requires client-level exception tracking like <a href="https://firebase.google.com/docs/crashlytics" target="_blank" rel="noopener">Firebase Crashlytics</a> backed by <a href="https://cloud.google.com/products/observability" target="_blank" rel="noopener">Google Cloud Observability</a>.</blockquote>
	</section>

	<section id="bias-7" class="bias-section" data-part="PART 02" data-title="Execution and Tooling Biases" data-bias="Bias 07: Unbounded Action Bias">
		<h3>07. Unbounded action bias and quota exhaustion</h3>
		<p>
			<strong>The failure mode:</strong> Autonomous agents have an inherent bias toward generating visible action and calling tools to prove utility, leading to infinite autonomous tool loops that drain my API budgets and trigger rate limits.
		</p>
		<p>
			<strong>The architectural fix:</strong> I enforce <strong>deterministic step limits, idempotency keys, and budget circuit breakers</strong>. Every autonomous agent session in my stack has a hard execution step ceiling (such as 10 tool iterations per user prompt). I configure <a href="https://cloud.google.com/billing/docs/how-to/notify" target="_blank" rel="noopener">Google Cloud Billing Budget Notifications</a> connected to Cloud Functions to programmatically trip circuit breakers and pause agent execution if daily token expenditure thresholds are crossed.
		</p>
	</section>

	
	<h2>PART 03: Strategic and persona biases: Controlling tone and velocity</h2>

	<section id="bias-8" class="bias-section" data-part="PART 03" data-title="Strategic and Persona Biases" data-bias="Bias 08: Premise Anchoring">
		<h3>08. Document premise anchoring (author authority bias)</h3>
		<p>
			<strong>The failure mode:</strong> When reviewing an existing document or PRD, an uncalibrated agent anchors heavily to the author's initial structure, framing, and wording, limiting its feedback to superficial line-level edits while missing fundamental architectural gaps.
		</p>
		<p>
			<strong>The architectural fix:</strong> I implement <strong>dual-track greenfield baseline and delta analysis</strong>. Before inspecting the author's draft, my orchestrator routes the raw project constraints and requirements to a fresh model instance to generate an independent, unanchored architecture baseline from first principles. My orchestrator then passes both the independent baseline and the author's draft into cloud Gemini for structured delta comparison and architectural gap analysis, immediately surfacing omitted requirements and unstated assumptions.
		</p>
	</section>

	<section id="bias-9" class="bias-section" data-part="PART 03" data-title="Strategic and Persona Biases" data-bias="Bias 09: Linguistic Style Drift">
		<h3>09. Linguistic drift and negative style degradation</h3>
		<p>
			<strong>The failure mode:</strong> Pre-training biases cause agents to saturate technical documents with promotional marketing adjectives and decorative punctuation.
		</p>
		<p>
			<strong>The architectural fix:</strong> I decouple prompt templates and negative stylistic guardrails from client codebases using <a href="https://firebase.google.com/docs/remote-config/get-started" target="_blank" rel="noopener">Firebase Remote Config</a>. I maintain system instructions, banned word lists, and parameter thresholds (temperature, top_p) on the server side, updating them instantly across client instances and agent workers without redeploying application code.
		</p>
	</section>

	<section id="bias-10" class="bias-section" data-part="PART 03" data-title="Strategic and Persona Biases" data-bias="Bias 10: Premature Convergence">
		<h3>10. Premature convergence (the "first plausible solution" trap)</h3>
		<p>
			<strong>The failure mode:</strong> Because LLMs are greedy auto-regressive samplers, autonomous agents exhibit premature convergence (satisficing). When presented with an open-ended design challenge or optimization task, the agent locks onto the first candidate solution that satisfies surface-level constraints, failing to explore stronger, more resilient, or lower-cost architectural trade-offs.
		</p>
		<p>
			<strong>The architectural fix:</strong> I implement <strong>competitive multi-agent sampling and trade-off scoring</strong>. For high-stakes decisions, I configure my orchestration layer to generate N divergent candidate architectures in parallel using distinct persona priors (such as Cost-Optimized, Latency-Optimized, and Simplicity-Optimized). My orchestrator scores all candidates against a structured evaluation matrix before committing to an execution path.
		</p>
	</section>

	
	<div class="table-container">
		<table class="data-table">
			<thead>
				<tr>
					<th>Bias #</th>
					<th>Failure mode</th>
					<th>Systemic root cause</th>
					<th>Architectural fix (Firebase / GCP)</th>
				</tr>
			</thead>
			<tbody>
				<tr>
					<td><strong>01</strong></td>
					<td>Context attention loss</td>
					<td>U-curve attention degradation across long contexts</td>
					<td>Cloud Firestore Structured Queries + Vertex AI Context Caching</td>
				</tr>
				<tr>
					<td><strong>02</strong></td>
					<td>Sycophancy and affirmation</td>
					<td>RLHF user agreement optimization bias</td>
					<td>Remote Config adversarial verification and persona prompting</td>
				</tr>
				<tr>
					<td><strong>03</strong></td>
					<td>Anchoring on drafts</td>
					<td>First-input token priming bias</td>
					<td>Dual-track independent greenfield generation</td>
				</tr>
				<tr>
					<td><strong>04</strong></td>
					<td>Model collapse and degradation</td>
					<td>Recursive synthetic summary entropy</td>
					<td>Raw signal pointers in Cloud Storage and raw API ingestion</td>
				</tr>
				<tr>
					<td><strong>05</strong></td>
					<td>Action loops and retries</td>
					<td>Deterministic token entrapment</td>
					<td>2-failure backtracking threshold + error diagnosis</td>
				</tr>
				<tr>
					<td><strong>06</strong></td>
					<td>Self-consistency illusion</td>
					<td>Confabulation feedback loops</td>
					<td>Epistemic confidence scoring and multi-agent verification</td>
				</tr>
				<tr>
					<td><strong>07</strong></td>
					<td>Temporal staleness</td>
					<td>Static parameter knowledge decay</td>
					<td>Vertex AI Search Grounding + real-time tool bus</td>
				</tr>
				<tr>
					<td><strong>08</strong></td>
					<td>Tool sunk cost fallacy</td>
					<td>Prefix token momentum</td>
					<td>Hard step ceilings and execution watchdog timers</td>
				</tr>
				<tr>
					<td><strong>09</strong></td>
					<td>Uncalibrated confidence</td>
					<td>Poor log-prob calibration</td>
					<td>Firebase Crashlytics and Cloud Observability telemetry</td>
				</tr>
				<tr>
					<td><strong>10</strong></td>
					<td>Premature convergence</td>
					<td>Greedy sampling satisficing</td>
					<td>Parallel multi-candidate exploration</td>
				</tr>
			</tbody>
		</table>
	</div>

	
	<h2>CHECKLIST: The builder's invariant checklist</h2>

	<blockquote><ul class="checklist-clean">
			<li><strong>1. The 2-failure backtracking threshold:</strong> I never let an agent attempt a third local retry on a failing tool branch. I abort and re-evaluate upstream architecture.</li>
			<li><strong>2. Raw signal ingestion and pointer memory:</strong> My background tasks query primary APIs directly. I store raw immutable records and pass pointers rather than re-summarizing summaries.</li>
			<li><strong>3. Dual-track unanchored baseline:</strong> I generate an unanchored ideal draft from raw requirements before evaluating existing documents.</li>
			<li><strong>4. Epistemic state gates:</strong> I tag memories as hypotheses vs confirmed ground truth; I never cite an unconfirmed hypothesis as authoritative truth.</li>
			<li><strong>5. Deterministic step limits and spend caps:</strong> I enforce hard execution step ceilings and automated billing circuit breakers to prevent runaway token spend.</li>
			<li><strong>6. Parallel candidate exploration:</strong> I sample several divergent candidates in parallel on critical decisions to avoid premature convergence on the first plausible solution.</li>
		</ul></blockquote>

	<blockquote><strong>Architecture blueprint and spec:</strong> Inspect my complete <a href="https://ulukaya.dev/blueprints">Transactional Memory Blueprint &rarr;</a> or scaffold a repository-native specification tree with my <a href="https://ulukaya.dev/instruments#generators">noVibes Agent Spec Generator &rarr;</a></blockquote>

	
	<section class="bias-section" id="references" data-part="REFERENCES" data-title="Industry Validation">
		<h2>Industry validation and benchmarks</h2>
		<ul>
			<li><a href="https://arxiv.org/abs/2609.04841v1" target="_blank" rel="noopener">MABPD: Multi-Agent Bias Probing &amp; Detection via Structured Argument Debate (Sep 2026)</a>: Confirms that structured adversarial argument debate between specialized agents exposes and neutralizes single-model cognitive and sycophancy biases.</li>
			<li><a href="https://arxiv.org/abs/2609.05069v1" target="_blank" rel="noopener">A Structured Debate-Mixture-of-Agents Framework for Complex Decision Support (Sep 2026)</a>: Confirms that isolating critique roles from generation roles prevents groupthink collapse in multi-agent ensembles.</li>
			<li><a href="https://firebase.google.com/docs/firestore" target="_blank" rel="noopener">Cloud Firestore Documentation</a></li>
			<li><a href="https://cloud.google.com/run/docs" target="_blank" rel="noopener">Cloud Run Serverless Containers</a></li>
			<li><a href="https://cloud.google.com/vertex-ai/generative-ai/docs/context-cache/context-cache-overview" target="_blank" rel="noopener">Vertex AI Context Caching Overview</a></li>
			<li><a href="https://cloud.google.com/vertex-ai/generative-ai/docs/grounding/overview" target="_blank" rel="noopener">Vertex AI Search Grounding Overview</a></li>
			<li><a href="https://firebase.google.com/docs/remote-config/get-started" target="_blank" rel="noopener">Firebase Remote Config Get Started</a></li>
			<li><a href="https://firebase.google.com/docs/data-connect" target="_blank" rel="noopener">Firebase Data Connect Overview</a></li>
			<li><a href="https://cloud.google.com/billing/docs/how-to/notify" target="_blank" rel="noopener">Google Cloud Billing Budget Notifications</a></li>
			<li><a href="https://firebase.google.com/docs/crashlytics" target="_blank" rel="noopener">Firebase Crashlytics Documentation</a></li>
			<li><a href="https://cloud.google.com/products/observability" target="_blank" rel="noopener">Google Cloud Observability Overview</a></li>
		</ul>
	</section>]]></content:encoded>
			<pubDate>Wed, 05 Aug 2026 00:00:00 GMT</pubDate>
			<dc:creator><![CDATA[Ibrahim Ulukaya]]></dc:creator>
			<category><![CDATA[AI Agents]]></category>
			<category><![CDATA[Firebase]]></category>
			<category><![CDATA[Vertex AI]]></category>
			<category><![CDATA[Cloud Run]]></category>
			<category><![CDATA[Firestore]]></category>
			<category><![CDATA[Remote Config]]></category>
		</item>
		<item>
			<title><![CDATA[Why a $50 Cloud Spend Cap Won't Save You From an Agent Loop]]></title>
			<link>https://ulukaya.dev/posts/cloud-spend-caps-firebase</link>
			<guid isPermaLink="true">https://ulukaya.dev/posts/cloud-spend-caps-firebase</guid>
			<description><![CDATA[A runaway loop burned $412.00 past my $50.00 cap before billing tripped. Google Cloud spend caps guard the account, not the session; I built a 3-layer defense.]]></description>
			<content:encoded><![CDATA[<section id="introduction" data-part="INTRO" data-title="Introduction">
		<p class="lead-paragraph">
			When I originally tested a runaway prompt loop on August 1 against a legacy Google Cloud Billing alert setup (a Pub/Sub budget alert hooked to a billing-disablement Cloud Function on an internal Google Billing Account that bypassed Lightning Billing), my script burned $412.00 above my $50.00 budget cap before the billing account shut down. In September 2026, <a href="https://jhuleatt.com/posts/cloud-spend-caps-firebase/" target="_blank" rel="noopener">Jeff Huleatt</a> and Cloud Billing engineers empirically verified that native Spend Caps powered by Google's Lightning Billing pipeline enforce in roughly 72 seconds (1.2 minutes) on Cloud Run and 11 minutes on Cloud Run Functions 2nd Gen. Yet even with sub-minute and 11-minute infrastructure cutoffs, relying solely on billing spend caps will not save my application from a runaway prompt loop.
		</p>
		<p><em>Figure 1.</em> A billing-layer cap at $50.00 fires after metering lag, so spend overshoots and every user gets a 503; a per-user token bucket in Firestore refuses work before the cap. <a href="https://ulukaya.dev/posts/cloud-spend-caps-firebase">View the figure in the essay.</a></p>
		<p>
			Native spend caps protect project solvency at the account level, not individual user sessions. When I deploy them against autonomous, multi-turn agents without an application-level rate limiter, infrastructure cutoffs introduce whole-service outages and severe state consistency risks. Protecting my production workloads requires a real-time, three-layer tokenomics defense that halts abusive token consumption at the application layer before reaching cloud billing.
		</p>
		
		<blockquote><strong>September 2026 Production Update (Lightning Billing Reality):</strong> Empirical benchmarks by Jeff Huleatt and Cloud Billing engineering confirm that native Spend Caps on external Lightning Billing accounts enforce in ~72 seconds on Cloud Run and ~11 minutes on Cloud Run Functions (compared to 30 minutes to 4 hours for legacy Pub/Sub budget alerts or non-Lightning internal billing accounts). However, when a native Spend Cap trips, Cloud Run immediately halts execution and returns <code>503 Service Unavailable</code> across the entire service, taking down my production application for every customer.</blockquote>
	</section>

	
	<h2>PART 01: The asynchronous billing metering lag and state traps</h2>

	<section class="bias-section">
		<h3>01. Empirical pipeline comparison: Lightning Billing vs. legacy alerts</h3>
		<p>
			Cloud Billing operates an asynchronous metering pipeline whose enforcement speed depends on the underlying billing architecture and compute runtime. Official <a href="https://docs.cloud.google.com/billing/docs/how-to/budgets-spend-caps" target="_blank" rel="noopener">Google Cloud spend caps documentation</a> and production Eventarc telemetry reveal three distinct real-world enforcement profiles:
		</p>

		<div class="table-scroll-wrap">
			<table class="data-table">
				<thead>
					<tr>
						<th>Billing Cutoff Pipeline</th>
						<th>Enforcement Latency</th>
						<th>$50 Cap Overshoot (180 to 600 req/min)</th>
						<th>Whole-Service Impact</th>
					</tr>
				</thead>
				<tbody>
					<tr>
						<td><strong>Native Spend Caps on Cloud Run</strong><br />(Lightning Billing Pipeline)</td>
						<td><strong>~72 seconds (1.2 min)</strong></td>
						<td>+$8.00 to +$25.00 ($58 to $75 total)</td>
						<td>Immediate service-wide <code>503 Service Unavailable</code> across all users</td>
					</tr>
					<tr>
						<td><strong>Native Spend Caps on Cloud Run Functions 2nd Gen</strong><br />(Lightning Billing Pipeline)</td>
						<td><strong>~11 minutes</strong></td>
						<td>+$100.00 to +$400.00+ ($150 to $450+ total)</td>
						<td>Service-wide execution halt across all functions</td>
					</tr>
					<tr>
						<td><strong>Legacy Pub/Sub Budget Alert Cutoff</strong><br />(or Non-Lightning Internal BA)</td>
						<td><strong>30 minutes to 4 hours</strong></td>
						<td>+$412.00 to +$2,000.00+</td>
						<td>Delayed global project billing disablement (<code>402</code>/<code>403</code>)</td>
					</tr>
				</tbody>
			</table>
		</div>

		<p>
			Even with 72-second enforcement on Cloud Run and 11-minute enforcement on Cloud Run Functions, my three-layer tokenomics defense remains mandatory for three physical reasons:
		</p>
		<ul>
			<li><strong>11 minutes on Cloud Functions (or 72s burst) still overshoots:</strong> At 180 to 600 requests per minute in an autonomous retry loop, an 11-minute metering window still burns $100 to $400+ past my $50.00 cap before global cutoff.</li>
			<li><strong>Whole-service <code>503 Service Unavailable</code> outage:</strong> When a native Spend Cap trips, Cloud Run immediately halts execution and returns <code>503</code> errors across the entire service (verified in Eventarc telemetry). Without Layer 2 per-user Firestore ACID token buckets, a single runaway user session or stuck agent loop takes down my entire production app for every customer.</li>
			<li><strong>Mid-turn state orphaning:</strong> Abrupt <code>402</code>, <code>403</code>, or <code>503</code> infrastructure cutoffs abort multi-step tool executions mid-flight without ACID rollback.</li>
		</ul>
		<p>
			To observe these three pipelines dynamically, I built the spend-cap fuse simulator below. Select any of the three production pipelines against my simulated $50.00 account cap and compare how infrastructure cutoffs behave versus a per-user application circuit breaker:
		</p>
	</section>

	<p><a href="https://ulukaya.dev/posts/cloud-spend-caps-firebase#lab-billing-lag">Interactive lab: billing-lag. Open the essay to run it.</a></p>

	<p><a href="https://ulukaya.dev/posts/cloud-spend-caps-firebase">Video: Asynchronous Pub/Sub Billing Overrun vs Synchronous Edge Circuit Breaker Proof. Watch it in the essay.</a></p>

	<section class="bias-section" id="state-trap">
		<h3>02. The multi-turn agent state trap</h3>
		<p>
			When a project spend cap trips, Cloud Billing pauses the billing account, causing my subsequent API calls to fail immediately with <code>402 Payment Required</code> or <code>403 Forbidden</code> quota errors.
		</p>
		<p>
			If my agent is in Step 3 of a 4-step tool execution chain (for example, it wrote a state update to <a href="https://firebase.google.com/docs/firestore" target="_blank" rel="noopener">Cloud Firestore</a> and was about to trigger an external webhook), an unannounced infrastructure pause aborts Step 4. Because multi-tool LLM loops lack native ACID transaction boundaries, a blunt infrastructure pause without an application-level circuit breaker creates orphaned, inconsistent database records in my production environment.
		</p>
	</section>

	
	<h2>PART 02: The three-layer tokenomics defense architecture</h2>

	<p>
		To protect my production AI systems, I stack three distinct layers of defense across ingress, application runtime, and billing infrastructure:
	</p>

	
	<div class="arch-diagram-container">
		<div class="arch-diagram-header">
			<span class="arch-diagram-title">3-Layer Tokenomics Defense Stack</span>
			<span class="arch-diagram-subtitle">Edge Attestation • Token Buckets • Billing Cutoff</span>
		</div>
		<div class="arch-layers-grid">
			<div class="arch-layer-card">
				<div class="arch-layer-top">
					<span class="arch-layer-badge badge-l1">LAYER 01: INGRESS EDGE</span>
					<span class="arch-layer-spec"><a href="https://firebase.google.com/docs/app-check" target="_blank" rel="noopener">Firebase App Check</a></span>
				</div>
				<div class="arch-layer-name">Cryptographic Device and Client Attestation</div>
				<p class="arch-layer-desc">
					I validate client attestation tokens at the edge, blocking unauthorized automated bots and malicious scripts before expensive model inference runs.
				</p>
			</div>

			<div class="arch-flow-connector">▼ <span>Authenticated Client Ingress</span> ▼</div>

			<div class="arch-layer-card">
				<div class="arch-layer-top">
					<span class="arch-layer-badge badge-l2">LAYER 02: APPLICATION LOGIC</span>
					<span class="arch-layer-spec"><a href="https://datatracker.ietf.org/doc/html/rfc2697" target="_blank" rel="noopener">IETF RFC 2697 Token Bucket</a> in Cloud Firestore</span>
				</div>
				<div class="arch-layer-name">User-Level Quotas and Circuit Breakers</div>
				<p class="arch-layer-desc">
					I track input and output token consumption per user using atomic field increments, returning structured application-level rate limits instead of abrupt infrastructure crashes.
				</p>
			</div>

			<div class="arch-flow-connector">▼ <span>Account Solvency Boundary</span> ▼</div>

			<div class="arch-layer-card">
				<div class="arch-layer-top">
					<span class="arch-layer-badge badge-l3">LAYER 03: INFRASTRUCTURE</span>
					<span class="arch-layer-spec">Google Cloud Spend Caps and Billing</span>
				</div>
				<div class="arch-layer-name">Ultimate Account Spending Limit</div>
				<p class="arch-layer-desc">
					Acts as my final account billing cutoff, disabling project billing only if upstream application token buckets and edge rate limits are breached.
				</p>
			</div>
		</div>
	</div>

	
	<h2>PART 03: Application-layer rate limiting in Cloud Firestore</h2>

	<p>
		Instead of waiting for billing accounts to pause, I maintain per-user token quotas at the application layer using atomic increments in Cloud Firestore:
	</p>

	<pre><code>// Enforce per-user token budgets atomically inside an ACID transaction
import &#123; Firestore, FieldValue &#125; from "@google-cloud/firestore";

const db = new Firestore();

export async function checkAndDeductTokens(
  userId: string, 
  estimatedTokens: number, 
  maxDailyTokens: number
): Promise&lt;&#123; allowed: boolean; remaining: number &#125;&gt; &#123;
  const userBudgetRef = db.collection("user_budgets").doc(userId);

  return await db.runTransaction(async (transaction) =&gt; &#123;
    const budgetDoc = await transaction.get(userBudgetRef);
    const currentUsage = budgetDoc.data()?.dailyTokensUsed || 0;

    if (currentUsage + estimatedTokens &gt; maxDailyTokens) &#123;
      throw new Error(
        `Application token budget exceeded: &#36;&#123;currentUsage + estimatedTokens&#125;/&#36;&#123;maxDailyTokens&#125; tokens used today.`
      );
    &#125;

    transaction.set(
      userBudgetRef,
      &#123;
        dailyTokensUsed: FieldValue.increment(estimatedTokens),
        lastRequestTimestamp: FieldValue.serverTimestamp(),
      &#125;,
      &#123; merge: true &#125;
    );

    return &#123;
      allowed: true,
      remaining: maxDailyTokens - (currentUsage + estimatedTokens),
    &#125;;
  &#125;);
&#125;</code></pre>
	</div>

	<blockquote><strong>Architectural takeaway:</strong> I never rely solely on infrastructure billing pauses to manage agent state. I stack Firebase App Check at the edge, Firestore token buckets in application logic, and Google Cloud spend caps as my final billing cutoff.</blockquote>

	<blockquote><strong>Interactive tool:</strong> Test my workload's token burn against a Google Cloud spend cap using my <a href="https://ulukaya.dev/instruments#calculators">AI Tokenomics Solvency Calculator &rarr;</a></blockquote>

	<section class="bias-section" id="references">
		<h2>Industry validation and benchmarks</h2>
		<ul>
			<li><a href="https://arxiv.org/abs/2608.28044v1" target="_blank" rel="noopener">Characterization of Request and Token Energy Costs for LLM Inference Workloads on GPU Platforms (Aug 2026)</a>: Confirms that unthrottled burst token generation creates non-linear cost spikes that asynchronous cloud telemetry cannot bound without synchronous ingress rate limiting.</li>
			<li><a href="https://arxiv.org/abs/2608.21719v1" target="_blank" rel="noopener">PowerSlider: Exploiting Phase Asymmetry for LLM Serving under Demand Response (Aug 2026)</a>: Confirms that enforcing synchronous prefill/decode admission control at the serving gateway prevents resource and budget exhaustion during traffic surges.</li>
			<li><a href="https://datatracker.ietf.org/doc/html/rfc2697" target="_blank" rel="noopener">IETF RFC 2697: A Single Rate Three Color Marker (Token Bucket Algorithms)</a></li>
			<li><a href="https://docs.cloud.google.com/billing/docs/how-to/budgets-spend-caps" target="_blank" rel="noopener">Google Cloud Spend Caps and Billing Quota Architecture</a></li>
			<li><a href="https://firebase.google.com/docs/app-check" target="_blank" rel="noopener">Firebase App Check Device and Client Attestation</a></li>
			<li><a href="https://firebase.google.com/docs/firestore" target="_blank" rel="noopener">Cloud Firestore Transactions and Atomic Increments</a></li>
			<li><a href="https://jhuleatt.com/posts/cloud-spend-caps-firebase/" target="_blank" rel="noopener">Jeff Huleatt: Cloud Spend Caps for Firebase Architecture</a></li>
		</ul>
	</section>]]></content:encoded>
			<pubDate>Sat, 01 Aug 2026 00:00:00 GMT</pubDate>
			<dc:creator><![CDATA[Ibrahim Ulukaya]]></dc:creator>
			<category><![CDATA[Cloud Billing]]></category>
			<category><![CDATA[Firebase App Check]]></category>
			<category><![CDATA[Firestore]]></category>
		</item>
		<item>
			<title><![CDATA[Enforcing Firebase App Check on Cloud Run Endpoints Without SDK Wrappers]]></title>
			<link>https://ulukaya.dev/til#03-app-check-cloud-run</link>
			<guid isPermaLink="true">https://ulukaya.dev/til#03-app-check-cloud-run</guid>
			<description><![CDATA[When deploying standalone containers on Cloud Run, you can verify incoming Firebase App Check JWTs at the Envoy ingress layer or inside Express/Fastify middleware by verifying the token against Google's public JWKS (`https://firebaseappcheck.googleapis.com/v1/jwks`).]]></description>
			<content:encoded><![CDATA[<p>When deploying standalone containers on Cloud Run, you can verify incoming Firebase App Check JWTs at the Envoy ingress layer or inside Express/Fastify middleware by verifying the token against Google's public JWKS (<code>https://firebaseappcheck.googleapis.com/v1/jwks</code>).</p>
<p>This rejects synthetic bot ingress with <code>HTTP 401 Unauthorized</code> before your Node.js application ever instantiates a Gemini API request, saving 100 percent of token burn from unauthenticated traffic.</p>
<pre><code>import { createRemoteJWKSet, jwtVerify } from "jose";

const JWKS = createRemoteJWKSet(new URL("https://firebaseappcheck.googleapis.com/v1/jwks"));

export async function verifyAppCheck(req, reply) {
  const token = req.headers["x-firebase-appcheck"];
  if (!token) {
    return reply.status(401).send({ error: "Missing App Check token." });
  }
  try {
    await jwtVerify(token, JWKS, {
      issuer: "https://firebaseappcheck.googleapis.com/v1",
      audience: `projects/${process.env.GCP_PROJECT_NUMBER}`,
    });
  } catch (err) {
    return reply.status(401).send({ error: "Invalid App Check verification." });
  }
}</code></pre>]]></content:encoded>
			<pubDate>Sat, 01 Aug 2026 00:00:00 GMT</pubDate>
			<dc:creator><![CDATA[Ibrahim Ulukaya]]></dc:creator>
			<category><![CDATA[Cloud Run]]></category>
			<category><![CDATA[App Check]]></category>
		</item>
		<item>
			<title><![CDATA[11 Rules of AI Tokenomics: From Prompt Hygiene to Hard Caps]]></title>
			<link>https://ulukaya.dev/posts/eleven-principles-of-ai-tokenomics</link>
			<guid isPermaLink="true">https://ulukaya.dev/posts/eleven-principles-of-ai-tokenomics</guid>
			<description><![CDATA[78% of my inference bill was repeated prompts and unpruned history. The 11 Principles of AI Tokenomics cover development; I add the guards live runtimes need.]]></description>
			<content:encoded><![CDATA[<section id="introduction" data-part="INTRO" data-title="Introduction">
		<p class="lead-paragraph">
			When I audited my monthly cloud inference bill across five production AI services, 78% of my total spend came from re-sending identical system prompts, un-pruned conversation histories, and routing trivial classification tasks to frontier reasoning models. Prompt engineering discipline optimizes my development costs, but it cannot prevent financial ruin during live production traffic spikes. While developer guidelines teach caching and concise prompting, my live application runtimes require deterministic code-level defense.
		</p>
		<p>
			In <a href="https://cloud.google.com/blog/products/application-development/11-principles-of-ai-tokenomics" target="_blank" rel="noopener">11 Principles of AI Tokenomics</a>, Alex Astrum and Luke Schlangen established the baseline for developer token efficiency. When my applications scale to thousands of concurrent users, prompt discipline alone fails against runaway loops, bot scraping, and unmetered client bursts. My live runtimes require hardware attestation, atomic token buckets, and hard application circuit breakers.
		</p>
		
		<blockquote><strong>The tokenomics reality:</strong> While prompt discipline reduces my baseline token usage during development, my live applications require runtime defense mechanisms (idempotency keys, circuit breakers, and stateful spend boundaries).</blockquote>
	</section>

	
	<h2>PART 01: Developer discipline: Where prompt tokenomics excels</h2>

	<p>
		The original eleven principles excel at minimizing waste during my prompt authoring and model invocation pipelines:
	</p>

	<section class="bias-section">
		<h3>01. Model sizing and prompt caching</h3>
		<p>
			I target lightweight models for classification, structured JSON extraction, and high-frequency tool validation, reserving heavy reasoning models for final synthesis. I pair large prompt templates with <a href="https://cloud.google.com/vertex-ai/generative-ai/docs/context-cache/context-cache-overview" target="_blank" rel="noopener">Vertex AI Context Caching</a> to reduce my input token costs by up to 75%.
		</p>
	</section>

	<section class="bias-section" id="context-caching">
		<h3>02. Subagent delegation and session brevity</h3>
		<p>
			I delegate repetitive, token-heavy data transformations to specialized subagents. I prune conversation history aggressively instead of passing unbounded multi-turn chat arrays to every subsequent inference step in my system.
		</p>
		<p>
			The numbers behind the 77%: my gateway benchmark sends 250,000 requests a month at about 1,500 input tokens each, 375M tokens. All of it on the frontier tier at $2.00 per 1M is $750. Routing 80% to the fast serverless tier at $0.075 drops the blended rate to $0.46 per 1M, or $172.50. The second benchmark run in the proof video adds a context cache on the shared system prompt and lands at $142.50.
		</p>
	</section>

	<section class="bias-section" id="tier-routing-simulator">
		<h3>03. Interactive simulator: The 80/20 tier-routing principle</h3>
		<p>
			In production, I never route 100% of traffic to expensive frontier models. By deploying an intelligent gateway that routes 80% of routine traffic to Gemini 3.6 Flash and 20% of complex turns to Gemini 3.1 Pro, I achieve a 77% cost reduction with identical reasoning quality.
		</p>

		<p><em>Figure 1.</em> The blended rate is a straight line between two prices, so the only lever that matters is how much traffic reaches the frontier model. At my 80/20 split the blend costs $0.46 per 1M tokens against $2.00 for routing everything to the frontier, which is the 77% the simulator below reproduces. <a href="https://ulukaya.dev/posts/eleven-principles-of-ai-tokenomics">View the figure in the essay.</a></p>

		
		<div class="tier-sim-card">
			<div class="tier-sim-header">
				<span class="tier-sim-badge">LIVE SIMULATOR</span>
				<h4>80/20 tier-routing blend vs. 100% frontier model</h4>
			</div>
			
			<div class="tier-sim-control">
				<label for="post-sim-prompts">Monthly Prompt Volume: <strong id="post-sim-vol-label">250,000 prompts</strong></label>
				<input id="post-sim-prompts" type="range" min="10000" max="1000000" step="10000" value="250000" />
			</div>

			<div class="tier-sim-grid">
				<div class="tier-sim-box frontier-box">
					<span class="sim-box-tag">100% Gemini 3.1 Pro</span>
					<span id="frontier-cost" class="sim-cost">$750.00</span>
					<span class="sim-sub">At $2.00 / 1M input tokens</span>
				</div>

				<div class="tier-sim-box blend-box">
					<span class="sim-box-tag blend-tag">80/20 Hybrid Blend</span>
					<span id="blend-cost" class="sim-cost blend-cost-val">$172.50</span>
					<span class="sim-sub">80% Flash ($0.075) + 20% Pro ($2.00)</span>
				</div>
			</div>

			<div class="tier-sim-result">
				<span>Net Monthly Savings: <strong id="sim-savings">$577.50 (77.0% Saved)</strong></span>
				<a href="https://ulukaya.dev/instruments?dau=2500&prompts=5&model=hybrid-tier-routing&cache=50&cap=100#calculators" class="sim-full-link">
					Open full tokenomics solver in Calculator &rarr;
				</a>
			</div>
		</div>
	</section>

	
	<h2>PART 02: Runtime defense: Why code-level guardrails are mandatory</h2>

	<p>
		The circuit breaker below uses small numbers on purpose. Budget: $2.00. One un-cached call: $0.10. At a 50% cache hit rate the call costs $0.05. The agent's job is to reconcile 500 invoices through a vendor API that is returning 500 errors. With discipline only, the agent retries 120 times and spends $6.00 for zero reconciled invoices, three times the budget. Caching halved the unit price and did nothing about the count. With the guard on, the idempotency key for invoice 4417 repeats on call 25 and the breaker opens at $1.25; the in-flight request is the last one that bills.
	</p>

	<p><a href="https://ulukaya.dev/posts/eleven-principles-of-ai-tokenomics#lab-tokenomics-guard">Interactive lab: tokenomics-guard. Open the essay to run it.</a></p>

	<p><a href="https://ulukaya.dev/posts/eleven-principles-of-ai-tokenomics#lab-webgpu-kv-thermal">Interactive lab: webgpu-kv-thermal. Open the essay to run it.</a></p>

	<p><a href="https://ulukaya.dev/posts/eleven-principles-of-ai-tokenomics">Video: Un-Cached Linear Token Burn vs 81% Cost Reduction via Context Caching & Tier Routing Proof. Watch it in the essay.</a></p>

	
	<div class="arch-diagram-container" id="defense-matrix">
		<div class="arch-diagram-header">
			<span class="arch-diagram-title">Unified tokenomics defense architecture</span>
			<span class="arch-diagram-subtitle">Dev Discipline • Runtime Circuit Breakers • Infrastructure Fuse</span>
		</div>
		<div class="arch-layers-grid">
			<div class="arch-layer-card">
				<div class="arch-layer-top">
					<span class="arch-layer-badge badge-l1">LAYER 01: DEVELOPMENT</span>
					<span class="arch-layer-spec">11 Tokenomics Principles</span>
				</div>
				<div class="arch-layer-name">Prompt discipline and model selection</div>
				<p class="arch-layer-desc">
					Optimizes my prompt tokens, leverages context caching, delegates subagent tasks, and enforces short conversation sessions.
				</p>
			</div>

			<div class="arch-flow-connector">▼ <span>Application Boundary</span> ▼</div>

			<div class="arch-layer-card">
				<div class="arch-layer-top">
					<span class="arch-layer-badge badge-l2">LAYER 02: APPLICATION RUNTIME</span>
					<span class="arch-layer-spec">Idempotency and Atomic Quotas</span>
				</div>
				<div class="arch-layer-name">Deterministic request protection</div>
				<p class="arch-layer-desc">
					Guards every inference call with unique idempotency keys in <a href="https://firebase.google.com/docs/firestore/manage-data/transactions" target="_blank" rel="noopener">Cloud Firestore</a> and deducts per-user quotas before triggering the LLM.
				</p>
			</div>

			<div class="arch-flow-connector">▼ <span>Infrastructure Boundary</span> ▼</div>

			<div class="arch-layer-card">
				<div class="arch-layer-top">
					<span class="arch-layer-badge badge-l3">LAYER 03: INFRASTRUCTURE</span>
					<span class="arch-layer-spec">Google Cloud Spend Caps</span>
				</div>
				<div class="arch-layer-name">Automated account billing cutoff</div>
				<p class="arch-layer-desc">
					Disables my billing account access as a hard spending limit if upstream rate limiters and application quotas are exceeded.
				</p>
			</div>
		</div>
	</div>

	
	<h2>PART 03: Production idempotency guard in TypeScript</h2>

	<p>
		I prevent duplicate LLM invocations and token waste during network retries by checking deterministic idempotency tokens in Cloud Firestore (implementing the <a href="https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-idempotency-key-header" target="_blank" rel="noopener">IETF Idempotency-Key HTTP Header specification</a>):
	</p>

	<pre><code>// Deduplicate LLM inference requests using Firestore atomic transactions
import &#123; getFirestore, doc, runTransaction &#125; from "firebase/firestore";

export async function executeIdempotentInference&lt;T&gt;(
  requestId: string,
  inferenceFn: () =&gt; Promise&lt;T&gt;
): Promise&lt;T&gt; &#123;
  const db = getFirestore();
  const requestRef = doc(db, "inference_idempotency", requestId);

  return await runTransaction(db, async (transaction) =&gt; &#123;
    const snap = await transaction.get(requestRef);
    if (snap.exists()) &#123;
      return snap.data().cachedResult as T;
    &#125;

    const result = await inferenceFn();
    transaction.set(requestRef, &#123;
      cachedResult: result,
      createdAt: new Date().toISOString()
    &#125;);
    return result;
  &#125;);
&#125;</code></pre>
	</div>

	<blockquote><strong>Architectural takeaway:</strong> I pair developer prompt discipline with code-level idempotency guards to eliminate duplicate token consumption and protect my production application runtimes.</blockquote>

	<p>
		The guard costs one document read per request and one write per first-seen key. That cost is fixed per request and does not grow with prompt size, unlike the duplicate frontier call it prevents, which bills 1,500 tokens every time a client retries.
	</p>

	<blockquote><strong>Interactive tool:</strong> Simulate hybrid 80/20 tier routing and context caching discounts using my <a href="https://ulukaya.dev/instruments#calculators">AI Tokenomics Solvency Calculator &rarr;</a></blockquote>

	<section class="bias-section" id="references">
		<h2>Industry validation and benchmarks</h2>
		<ul>
			<li><a href="https://arxiv.org/abs/2609.04748v1" target="_blank" rel="noopener">Same Request, Different Answer: Quantization Amplifies Cache-Induced Divergence in LLM Serving (Sep 2026)</a>: Confirms the exact KV-cache reuse mechanics and token cost reductions achieved by prefix context caching in production serving pipelines.</li>
			<li><a href="https://arxiv.org/abs/2609.04681v1" target="_blank" rel="noopener">Beyond Code Generation: Reliability, Verification, and Cost Economics in the Agentic Software Development Lifecycle (Sep 2026)</a>: Establishes empirical unit-economic models for balancing frontier reasoning tokens against deterministic verification passes.</li>
			<li><a href="https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-idempotency-key-header" target="_blank" rel="noopener">IETF HTTP Working Group: The Idempotency-Key HTTP Header Field Specification</a></li>
			<li><a href="https://cloud.google.com/blog/products/application-development/11-principles-of-ai-tokenomics" target="_blank" rel="noopener">Alex Astrum and Luke Schlangen: 11 Principles of AI Tokenomics (Google Cloud)</a></li>
			<li><a href="https://cloud.google.com/vertex-ai/generative-ai/docs/context-cache/context-cache-overview" target="_blank" rel="noopener">Vertex AI Context Caching Architecture and TTL Management</a></li>
			<li><a href="https://firebase.google.com/docs/firestore/manage-data/transactions" target="_blank" rel="noopener">Cloud Firestore Transactions and Concurrency Control</a></li>
		</ul>
	</section>]]></content:encoded>
			<pubDate>Thu, 30 Jul 2026 00:00:00 GMT</pubDate>
			<dc:creator><![CDATA[Ibrahim Ulukaya]]></dc:creator>
			<category><![CDATA[Gemini API]]></category>
			<category><![CDATA[Vertex AI]]></category>
			<category><![CDATA[Context Caching]]></category>
		</item>
		<item>
			<title><![CDATA[Atomic Firestore Token Bucket Increments Under Concurrency]]></title>
			<link>https://ulukaya.dev/til#04-firestore-token-bucket</link>
			<guid isPermaLink="true">https://ulukaya.dev/til#04-firestore-token-bucket</guid>
			<description><![CDATA[To prevent client retry loops from overwhelming per-UID token allowances, do not use read-then-write transactions. Instead, use `FieldValue.increment(consumedTokens)` within a Firestore document update.]]></description>
			<content:encoded><![CDATA[<p>To prevent client retry loops from overwhelming per-UID token allowances, do not use read-then-write transactions. Instead, use <code>FieldValue.increment(consumedTokens)</code> within a Firestore document update.</p>
<p>Atomic field increments execute at database speed without transaction retry contention, ensuring accurate token accounting even when an AI agent fires 50 concurrent tool calls.</p>
<pre><code>import { FieldValue } from "firebase-admin/firestore";

export async function recordTokenUsage(db, userId, promptTokens, completionTokens) {
  const ref = db.collection("token_usage").doc(userId);
  await ref.set(
    {
      promptTokens: FieldValue.increment(promptTokens),
      completionTokens: FieldValue.increment(completionTokens),
      totalInvocations: FieldValue.increment(1),
      lastUpdated: FieldValue.serverTimestamp(),
    },
    { merge: true }
  );
}</code></pre>]]></content:encoded>
			<pubDate>Wed, 29 Jul 2026 00:00:00 GMT</pubDate>
			<dc:creator><![CDATA[Ibrahim Ulukaya]]></dc:creator>
			<category><![CDATA[Firestore]]></category>
			<category><![CDATA[Tokenomics]]></category>
		</item>
		<item>
			<title><![CDATA[Preventing Prompt Loop Quota Exhaustion with 402 HTTP Circuit Breakers]]></title>
			<link>https://ulukaya.dev/til#05-spend-cap-circuit-breaker</link>
			<guid isPermaLink="true">https://ulukaya.dev/til#05-spend-cap-circuit-breaker</guid>
			<description><![CDATA[When a Google Cloud Spend Cap fuses, new API calls return a quota error. If your agent does not catch this explicitly, it can crash mid-execution and leave database state partially mutated.]]></description>
			<content:encoded><![CDATA[<p>When a Google Cloud Spend Cap fuses, new API calls return a quota error. If your agent does not catch this explicitly, it can crash mid-execution and leave database state partially mutated.</p>
<p>Always wrap Gemini API calls in a circuit breaker that intercepts quota errors and returns a structured <code>HTTP 402 Payment Required</code> to the calling agent, allowing the client to safely checkpoint its progress.</p>
<pre><code>export async function callWithSpendCapGuard(apiCall) {
  try {
    return await apiCall();
  } catch (err) {
    if (err.status === 429 || err.code === "RESOURCE_EXHAUSTED") {
      const error = new Error("Spend cap reached or quota exhausted. Safe checkpoint created.");
      error.statusCode = 402;
      error.isFatalQuota = true;
      throw error;
    }
    throw err;
  }
}</code></pre>]]></content:encoded>
			<pubDate>Sat, 25 Jul 2026 00:00:00 GMT</pubDate>
			<dc:creator><![CDATA[Ibrahim Ulukaya]]></dc:creator>
			<category><![CDATA[Cloud Billing]]></category>
			<category><![CDATA[Gemini API]]></category>
			<category><![CDATA[Tokenomics]]></category>
		</item>
	</channel>
</rss>