Postmortem

My Agent Reads 1.4 MB of Its Own Source Code to Remember What It Already Did

August 8, 2026 · By Italo Campilii

My Agent Reads 1.4 MB of Its Own Source Code to Remember What It Already Did

TL;DR

One of my agents stores part of its working memory in its own source code. Before each run, a sweep script globs its sibling scripts, opens them, and regex-parses their Python for the values a previous run used — so it can avoid repeating them. That directory now holds 249 sweep scripts totalling 1,492,772 bytes, up from 57,049 bytes sixteen days ago, and the newest run re-reads all of it. The failure is not the cost. It is that the memory is scoped by a filename pattern: the dedupe glob matches sweep-pass*-workable.py, which covers 78 files and misses 171 — and 22 of those missed files define 825 query strings the check will never see. The measured consequence sits in the ledger: one job req at Apothekary has four rows, three marked submitted before the fourth was caught as duplicate-submitted. State that lives in code is not state. It is a coincidence that happens to be true until someone renames a file.

Five days ago I wrote about 149 throwaway scripts with no shared library — an agent duplicating code because it had no way to import a fix. This is the sequel, and it is worse, because the same directory turns out to be doing something I did not design and would never have approved: it is using its own source tree as a database.

I found it while reading a sweep script to see why a duplicate slipped through. Line 28.

The mechanism: a glob, an open(), and a regular expression#

Here is the actual code from sweep-pass198-workable.py, the pass that ran most recently in this family:

prior = set()
for f in glob.glob(os.path.join(os.path.dirname(os.path.abspath(__file__)),
                                "sweep-pass*-workable.py")):
    if os.path.basename(f) == os.path.basename(__file__): continue
    m = re.search(r'QUERIES\s*=\s*\[(.*?)\n\]', open(f).read(), re.S)
    if m: prior |= {q.lower() for q in re.findall(r'"([^"]+)"', m.group(1))}
dupes = sorted({q for q in QUERIES if q.lower() in prior})

Read that again as a data-storage decision rather than as code. The question the script is asking is "which search queries have I already run?" — a question with an obvious answer shape: a list, in a file, appended to after each run. Instead the script answers it by treating every previous version of itself as a record, opening each one, and extracting the literal contents of a variable named QUERIES with a regular expression.

The sibling family does the same thing with a wider net. sweep-pass199-universe.py globs sweep*.py — every sweep in the directory — and scrapes any string that looks like an applicant-tracking-system slug out of any triple-quoted block or list literal it finds:

slugs = set()
for f in glob.glob(os.path.join(HERE, 'sweep*.py')):
    if os.path.abspath(f) == os.path.abspath(__file__): continue
    t = open(f, errors='ignore').read()
    for blk in re.findall(r'=\s*"""(.*?)"""', t, re.S):
        slugs.update(w for w in blk.split() if re.fullmatch(r'[a-z0-9][a-z0-9._-]{1,40}', w))
    for blk in re.findall(r'=\s*\[(.*?)\]', t, re.S):
        slugs.update(re.findall(r'"([a-z0-9][a-z0-9._-]{1,40})"', blk))

No agent was told to do this. It emerged. Each pass needed to know what earlier passes had covered, no ledger existed for that particular fact, and the earlier passes were sitting right there on disk — so the model reached for the data it could see. That is a rational local decision and a catastrophic global one.

The cost curve#

Every new universe sweep reads the whole corpus. So the corpus is also the input, and the input grows every time the agent works. Here is the measured size of sweep*.py in that directory, by day:

SOURCE CODE RE-READ PER RUN — 57 KB TO 1.49 MB IN 16 DAYS 0400K800K1.2M1.6M Jul 23Jul 30Aug 2Aug 5Aug 8 10 files4394182249 files / 1,492,772 B Each run parses the entire corpus. The corpus grows by one file per run.

The narrower workable-family glob is smaller — 78 files, 352,060 bytes — and it does recover real data: 2,878 query strings. The mechanism works. That is exactly what makes it dangerous. A broken mechanism gets fixed. A mechanism that works and is silently incomplete gets trusted.

Where it actually breaks: memory scoped by filename#

The dedupe glob is sweep-pass*-workable.py. That pattern is the entire definition of "what I have already done."

Set Files Notes
All sweep scripts in the directory 249 Every script that has ever issued queries
Matched by the dedupe glob 78 The only ones the check can see
Invisible to the dedupe glob 171 Different naming convention, same job
Invisible files that still define QUERIES 22 Real query history, structurally unreachable
Query strings hidden in those 22 files 825 Against 2,878 the check does recover

Roughly a fifth of the query vocabulary this system has ever used is invisible to the deduplication step, and not because of a bug in the parsing. The regex is fine — it extracts a clean QUERIES list from all 22 of those files when pointed at them directly. They are excluded purely by their names.

The clearest example is sweep-2026-08-02-pass136-workable.py. It is a workable sweep. It ends in -workable.py. It defines QUERIES. It is invisible, because it is named sweep-2026-08-02-pass136-... instead of sweep-pass136-..., and sweep-pass* does not match a date. One dash-separated segment of a filename, chosen by a model at 4 AM, silently removed that run from the agent's memory.

This is the same class of defect I documented when a watcher reading a 1,503-row pipeline could only see 76 of them: a component that reports success while looking at a fraction of reality. There the scope was a status vocabulary. Here it is a filename pattern. In both cases the check passed, the exit code was zero, and the coverage was partial.

The measured consequence#

None of this would matter if the downstream effect were "an extra search runs." It isn't. Downstream, this system files job applications with my name on them.

The ledger, pipeline.csv, currently holds 1,666 rows. Two of the duplicate incidents are still visible in it:

Company Role Rows Statuses
Apothekary Creative Director 4 submitted, submitted, submitted, duplicate-submitted
Blueprint (Kate Tolo) Director, Shortform Content 2 submitted, duplicate-submitted

Four applications to one req. Three of them went out before anything noticed.

And the repairs are themselves preserved in the source code, as comments — because there is nowhere else to put them. The normalization helper in the latest sweep carries its own incident history inline:

def _norm(s):
    """Normalize for dedupe: drop parentheticals, punctuation, spacing.
    Added pass #160 after a DUPLICATE application was filed to Blueprint
    'Director, Shortform Content' - pipeline had company 'Blueprint (Kate Tolo)',
    the sweep saw 'Blueprint', and the exact-string pair match missed it."""

Then, five lines down, the next lesson: a diacritic in "Apothékary" normalized differently on each side of the comparison, so the pair never matched. Both fixes are correct. Both live in a docstring, in one file, in a directory of 249 — which means the next script the agent writes inherits them only if it happens to be copied from this one.

Why this shows up in agent fleets specifically, and what it looks like at your stage#

The shape of this failure changes depending on where your build is, so it is worth being concrete about which version you are likely looking at.

If you are ten or twenty runs in, with one script that keeps getting edited in place, you will not see it at all — the state and the code are the same file, the file is small, and reading it works. The defect is invisible precisely because the coincidence still holds.

If you are running a scheduled fleet that writes a new script per pass — which is what any agent given a shell and a recurring task tends to do — this is probably already happening in your repo. Look for glob.glob in a script that also imports re. That combination almost always means one artifact is parsing another artifact's source instead of reading a data file.

If your state is genuinely append-only and factual — companies applied to, URLs seen, IDs processed — a flat ledger fixes it outright, and you should expect the ledger to get large; I have written separately about what happens when an append-only memory file outgrows the context window. Large is a solvable problem. Unreachable is not.

If your state is derived rather than recorded — "which query themes have I explored," "which angles are exhausted" — the trap is sharper, because there is no obvious moment when someone was supposed to write it down. That is the case here. Nobody forgot to log the queries; logging them was never a step. The agent invented recall from the only durable trace it had.

And if your fleet spans several script families with different naming conventions-workable, -universe, -sr — assume any glob-based memory is scoped to one family and blind to the rest, which is exactly the 78-versus-249 split above. The moment two conventions coexist, a pattern-matched memory has a hole in it.

The common tell across all of these: the agent's ability to remember depends on something no test asserts. Not a schema, not a row count — a filename. My handoff document template exists for the same reason. State an agent needs across sessions has to be written to a named place on purpose, in a format a check can read, or the next session reconstructs it from whatever happens to be lying around.

The fix: give the derived state a real home#

I am not rewriting 249 scripts. The repair is narrow and it is the same repair every time:

  1. Name the state. In this case: query-log.csv, three columns — date, pass, query. One row per query actually issued. It is the smallest possible file and it did not exist.
  2. Write it at the moment of use, not after. The sweep appends the query when it issues it. State recorded by a later reader is state that can be missed; state recorded by the actor is not.
  3. Read the ledger, delete the glob. The dedupe check opens one file with csv.DictReader instead of pattern-matching 249. Cost drops from 1.49 MB per run to a few kilobytes, and — the actual point — the coverage stops depending on filenames.
  4. Backfill once, deliberately. Run the existing regex over all 249 files a single time, including the 22 hidden ones, and write the 3,703 recovered query strings into the ledger. The parser was never the problem, only its scope. Used once as a migration, it is exactly the right tool.
  5. Assert the coverage. A check that fails when the ledger's distinct-pass count diverges from the number of sweeps on disk. That single assertion would have caught sweep-2026-08-02-pass136-workable.py the day it was written.

Step 5 is the one people skip and the one that matters. Everything above it is hygiene; step 5 is what makes the memory's completeness a measurable property rather than an assumption. It is the same principle behind designing an agent to refuse rather than guess — a system that cannot verify what it knows should not act confidently on it.

The general rule#

Source code is a terrible database, and agents will use it as one unless you give them a better option.

A model writing a script has a strong bias toward the data in front of it. If the only durable record of what happened is the code that made it happen, the model will read the code. It will do this competently — the regex here is well written, the dedupe genuinely works, 2,878 real queries come back — and that competence is what keeps the flaw alive for sixteen days and 249 files.

The audit is one command. Go to whatever directory your agents write to and grep for glob in files that also import re. Every hit is a place where your system's memory is a coincidence about filenames.

RUNNING A FLEET THAT WRITES ITS OWN CODE?

The interesting failures in agent systems are almost never the model. They are the seams — where state lives, what a check can actually see, which coincidences your pipeline is quietly resting on. Finding and closing those seams is the work I do for one-person operations running real fleets. Get in touch, or see the press and hire page.

— Italo Campilii. Measured 2026-08-08 against ~/career-engine/scripts/ on my own machine. File and byte counts from glob.glob('sweep*.py') and os.path.getsize (249 files, 1,492,772 bytes; 78 files, 352,060 bytes for the sweep-pass*-workable.py subset); the growth series from mtime-ordered cumulative sizes. The 22 hidden files and 825 hidden query strings come from running the sweeps' own QUERIES regex against the 171 non-matching files. Row counts and statuses read from pipeline.csv (1,666 rows) with Python's csv module. Code blocks and the _norm docstring are quoted verbatim from the scripts. Every figure is an exact measured count; none are estimates.

IC

Italo Campilii

AI systems builder and one-person operator. I run the infrastructure for five brands solo with Claude Code agent fleets — SEO, content, e-commerce ops, and the verification layer that keeps it honest. The Build Log is where I write down what actually works.