When pointing the newly rebuilt Foundry at a real-world, 5,574-file monorepo, the context graph did not gracefully scale - it broke. Here are the 25 bugs, quadratic bottlenecks, and silent hallucinations uncovered by dogfooding an AI context substrate.
In a previous article, Escaping the Context Drift Trap: Introducing The Foundry, I confessed to building a developer tool for an audience of exactly one and then losing the plot entirely - spending weeks building a custom TUI and four competing agent loops before regaining my sanity. I eventually stripped The Foundry back to the two core components that actually solved my problem: the substrate and the context graph. I ended that post with a tidy little piece of advice: be your own toughest customer. Once a tool reliably solves a real problem for you, it will probably solve it for someone else too.
It feels a little smug in hindsight. Because I then did the uncomfortable thing and actually took my own advice.
I pointed the rebuilt Foundry at a real codebase. This was not the tidy, isolated, 200-file test repository I had been using during development, but a live, 5,574-file monorepo featuring a Python backend and a Vue frontend. It was the kind of sprawling, half-tidy codebase that developers actually get paid to work in day-to-day. I configured The Foundry to build its context graph, and I began grounding my AI agents on it in anger.
It was a deeply humbling experience. Over the following fortnight, I filed 25 distinct issues against my own project. Here is what being my own first customer actually taught me about software engineering, scale, and AI context systems.
Lesson One: "Works on My Machine" Means "Works on My Toy Repo"
The very first thing the rebuilt version of The Foundry did when pointed at a real repository was fail to complete its execution entirely.
The semantic index - the component that allows vector search to actually understand your code rather than relying on plain keyword matching - was committing updates to its vector store once per AST node. Every single write re-enumerated everything written up to that point, turning the indexing cost quadratic. On my toy repositories, this architectural oversight was completely invisible because the node count was negligible. On the production monorepo, however, it wrote 6.2 GB of index manifests for 68 MB of actual vectors before I killed the process. It was projected to consume roughly 37 GB of disk space and four hours to finish. Meanwhile, the structural parsing phase of the build took a mere nineteen seconds.
The tool was not merely slow; it was architecturally incapable of finishing on a repository that anyone would use in a production environment.
// Flawed approach: Quadratic write loop committing per node
for (const node of parsedGraphNodes) {
// Each commit re-enumerated and rewrote the entire index manifest
await vectorStore.commitNode(node);
}
// Fixed approach: Transactional batching with chunked commits
await vectorStore.transaction(async (tx) => {
await tx.commitBatch(parsedGraphNodes, { chunkSize: 500 });
});You simply cannot discover that category of bug on a clean 200-file project. You can only surface it by acting as a genuine customer operating against a messy, real-world codebase.
Lesson Two: The Worst Bug in a Grounding Tool Is a Confident Wrong Answer
This was the discovery that genuinely rattled me, because eliminating hallucinations is the primary reason The Foundry exists in the first place.
The value proposition of an AI context substrate is that your developer agents stop hallucinating about architectural relationships because they are grounded in a real, deterministic graph of the codebase. However, a context graph is only as honest as the parser extractors that feed it. Mine was quietly lying in three distinct ways:
- The entire frontend was invisible: The extractor lacked support for
.vuesingle-file components. Consequently, 983 components - the entire UI layer - were excluded from the graph. Asking the agent "What uses this Pinia store?" yielded a confident "Nothing," because no frontend component could ever exist as a graph edge. - Search was drowning in test utilities: Test symbols accounted for 58% of all functions in the graph. Because test names are keyword-dense by design, a search for the chat-turn driver returned five test suites and never the actual implementation driver. The agent formed its mental model out of test scaffolding rather than core code.
- Blast-radius analysis yielded false negatives: The dominant Vue idiom - invoking a composable function at the top of
<script setup>- has no enclosing function declaration. This produced no call edge whatsoever in the parser. Running a blast-radius query on critical composables returned empty results for dependencies that half the application relied upon.
None of these failures threw an exception. That is precisely what makes them so dangerous. A crash is immediate and obvious. A confident, plausible, yet flatly wrong answer leads directly to what I previously described as AI slop: code that appears correct on the surface but fundamentally violates system assumptions.
// Handling script setup calls by creating synthetic module nodes
export function extractVueEdges(ast: VueAST, fileId: string): GraphEdge[] {
const edges: GraphEdge[] = [];
const syntheticScope = `module:${fileId}`;
for (const statement of ast.scriptSetup.statements) {
if (isComposableInvocation(statement)) {
edges.push({
source: syntheticScope,
target: statement.calleeSymbol,
type: EdgeType.DEPENDS_ON
});
}
}
return edges;
}Lesson Three: The Tool Fought Me at Exactly the Wrong Moment
Because The Foundry runs as a Model Context Protocol (MCP) server that my agent interacts with, and because the graph database utilized a single-writer concurrency model, the background server process held the write lock continuously. This meant I could not run a simple CLI command such as graph query or export a backup without terminating the MCP server first.
Terminating the server dropped my active agent session's tools mid-task, precisely when I was relying on them to inspect system state. Even worse, when a second concurrent session lost the database lock, it silently degraded into a read-only fallback mode. Ten write-capable tools simply vanished from the available toolset without any warning or error message explaining why. I spent an entire afternoon conducting process archaeology using ps commands before realizing a zombie background process from an earlier session was retaining the lock.
Lesson Four: Never Let the Machine Delete Irreplaceable Authored State
The Foundry context graph consists of two distinct layers:
- The Derivable Layer: Code ASTs, git commit histories, and structural edges that can be regenerated from source files in minutes.
- The Authored Layer: Wiki entries, architectural decisions, and memory nodes manually written to explain why a complex subsystem behaves a certain way.
Unfortunately, both layers resided within the same underlying data store. When I cleared the cache to apply a parser fix, the rebuild process silently erased hours of hand-written architectural notes. In a separate incident, re-running the initialization command reset the file-scoping configuration, causing the indexer to re-ingest 300 MB of excluded benchmark datasets.
The derivable data was fully protected by automatic caching mechanisms, while the irreplaceable human-authored context had no backup strategy whatsoever.
The Fix Cadence - And Architectural Recursion
I resolved all 25 issues across three structured releases over the course of two weeks: v3.2.0, v3.2.1, and v3.2.2.
Batched database transactions eliminated the quadratic indexing bottleneck. Component extractors for .vue and .svelte were implemented as first-class citizens. Script setup composables now map to synthetic module scope nodes so blast-radius analysis reports accurate dependencies. Test utilities are deprioritised during vector search ranking. Read operations now utilize a lock-free snapshot reader, allowing inspection commands to execute alongside active MCP server sessions. Finally, authored wiki notes are now automatically serialized to version-controlled snapshot files that survive full graph rebuilds.
The most rewarding aspect of this exercise was the self-correcting workflow. The fixes themselves were executed using the exact same governed agent loops I rely on daily - writing specs, performing adversarial code reviews, and validating output against pre-submission checks. The tool actively assisted in fixing its own flaws.
What Being the First Customer Actually Teaches You
Every single one of those 25 bugs was completely invisible when viewed from the inside. When acting simultaneously as developer, maintainer, and sole designer, a system always appears feature-complete and robust. It required adopting the mindset of a demanding customer - someone with a complex codebase and immediate, non-negotiable tasks - to expose the cracks.
I am carrying two fundamental engineering principles forward from this process:
- Scale and realistic complexity are mandatory testing criteria: If your tooling has not been evaluated against an untidy, multi-thousand-file repository, you have not actually tested its core functions. Toy examples conceal architectural flaws.
- In context systems, silent omissions are far more dangerous than runtime crashes: An unindexed component or a missing edge that silently returns an empty result set causes infinitely more harm than an explicit error stack trace. Reliability requires replacing silent assumptions with clear, legible status indicators.
Being your own toughest customer remains invaluable advice. Just prepare yourself for the volume of bugs the person in the mirror is going to file.
