The AI Didn't Replace Engineering Discipline. It Made It Explicit.
How an AI-built codebase survives a major pivot. The git history of Escribano's recorder rewrite.
Escribano started as a batch processor. You finish a recording, drop a file, it runs. Simple. Then I needed it to be always on.
Now, that kind of pivot in an AI-assisted codebase is not just adding a feature. Is a full architectural replacement; different execution model, new system boundaries, new native dependency. The question was never “can the AI write this code”. It was whether the codebase had enough structure to survive being ripped apart and rebuilt while the AI was doing the heavy lifting.
It did. Let me walk you through it.
First things first: nuke the dead code
Before touching the new architecture, I merged PR #25 ; basically a cleanup that removed all the dead V1/V2 code and dropped the legacy tables.
Not for tidiness. There’s a very practical reason: a stateless AI agent will read everything in your repo. It doesn’t know V1 is deprecated. It sees a class, assumes is load-bearing, and starts wiring things to it. Leave enough dead code around and you’ll spend full sessions untangling hallucinated dependencies pointing at stuff you deleted six sprints ago.
I learned this the hard way. Without the cleanup the agent kept pulling in old patterns from V1 and I’d only notice halfway through a session when things stopped making sense.
Prove it works before the AI touches it
The always-on recorder needed macOS ScreenCaptureKit. I hadn’t used SCStream before. Rather than letting the AI start integrating experimental Swift code directly into the TypeScript app (which it would’ve done, confidently, if I’d asked), we ran a contained spike in PR #30 .
The spike lived entirely outside the core. We proved Phase B of the SCStream implementation independently; only once that held up did I let the AI start connecting it to anything real.
AI gives you speed; premature integration is how you burn through it without getting anywhere.
The ADR as operating memory
With the spike validated, I wrote ADR-009 before writing any production code, merged in PR #29 .
Without the ADR, the agent had no memory of our architectural boundaries and immediately started guessing at trade-offs. With it loaded at the start of every session, it knew why we were building a separate Swift capture agent instead of embedding everything in Node, why the recorder needed to be independent from the analyzer, what the boundaries were. It had something concrete to push back on when my prompts were vague.
One thing I’ve stopped doing: writing ADRs after decisions are made. If the agent is going to make good decisions, the ADR has to come first.
ADRs tell you why. TDDs tell you what to actually build.
This is where the workflow evolved beyond what I described in my previous post.
An ADR tells you why a decision was made. But “build an always-on recorder with a separate Swift agent and a Node analyzer” is abstract enough that the AI can interpret it in like four different ways. And it did.
With only ADR-009 as guidance the agent would’ve guessed at every boundary. Where should the VLM live; Swift, Node, or a separate Python bridge? Pick one. Frame-claiming strategy? Zero concurrency control, no process locks, nothing preventing two analyzers from loading the model simultaneously and thrashing memory. Timestamp formats between tables? Inconsistent, breaks joins. Config thresholds? Just made up.
I’d done a version of this before and it failed. So this time I already had a previous design to reference; the TDDs were easier to write because I knew what had gone wrong. And reviewing them in a draft PR before any code existed meant a lot of the obvious gaps got caught in markdown, not in a debugging session.
Issues still came up during implementation tho. But the difference is where you go when something breaks. With the TDDs in place you don’t just stare at the code; you go back to the design, figure out if the assumption was wrong, where you forgot to add more detail, or where the thinking fell apart. Way easier to fix a doc than untangle code that was built on a bad premise.
(sidenote: this is just design-driven development; the earlier you catch an error the cheaper it is to fix. nothing new. AI just makes ignoring it way more expensive.)
So before writing any implementation code I put PR #31 up as a draft; three Technical Design Documents, one per component:
Each TDD specified interfaces, concurrency model, error paths, and config defaults with justifications pulled from the spike data. The point wasn’t the docs themselves; it was reviewing them before they became code. A wrong assumption corrected in a markdown file takes a few seconds. That same assumption corrected after is been woven into a Swift process talking over a Node IPC channel? That’s a full session gone.
Once the TDDs were merged the difference was immediate. The agent stopped guessing at boundaries and started implementing against contracts. Concurrency bugs that would’ve surfaced as crashes in production (overlapping launchd spawns, those same unsafe POSIX signal handlers) got caught because the TDD explicitly enumerated those scenarios.
Write TDDs in a draft PR. Review them there. Merge them before the implementation branch gets created.
So what does this look like in the git log?
If you look at the actual history, the first four steps didn’t involve a single line of production code:
- PR #25 : delete the old architecture
- PR #30 : prove the new one works in isolation
- PR #29 : document why
- PR #31 : document what, in enough detail to review
Four PRs before implementation started. That’s the cost of keeping an AI-assisted project coherent.
Is not exotic at all tho; basically is the same structured process you’d use with any async distributed team. I’ve been replicating how I used to work with geographically distributed teams, just applied to a new kind of contributor. The AI is faster than any engineer I’ve worked with. But it has no memory, no judgment about what’s in scope, and zero idea what the codebase looked like six weeks ago. So you give it the structure and let it give you the speed.