---
title: "AI Lab Project Building Part 5: Iterative Engineering with AI Assistants"
date: 2026-04-09
summary: By the time four AI Lab apps were live, the real deliverable was the loop that built them. What the iteration pattern looked like, where it broke, and what the retrospective taught me about docs, tests, and gradient creep.
status: published
tags: ai lab, engineering process, ai assistants, retrospective, documentation, testing, css architecture
mediaSourcePath: /ai-lab/
mediaSourceSelector: .projects-grid, .hero, main
mediaSourceTheme: dark
imageAlt: The AI Lab hub with all four apps live.
imageCaption: The loop that produced the apps outlasted any single feature in them.
---

# AI Lab Project Building Part 5: Iterative Engineering with AI Assistants

By the time the chat, Promptpad, Problem Solver, and Agent Agenda were all running, the most useful thing I'd built wasn't any one of them. It was the loop that produced them. This post is about that loop, including the parts of it I got wrong and had to fix in a retrospective.

## The pattern

It stayed the same across every app:

1. Write the scope and the non-goals before anything else.
2. Implement in slices small enough to test on their own.
3. Run the targeted checks, then the wider ones.
4. Update the docs and the handoff note in the same pass as the code.
5. Re-verify with the broader regression run before merge.

Each app got its own feature branch and its own handoff document in `docs/`. The handoff listed the files touched, the prompt keys added, the local storage keys, the test layers, and the exact verification commands that had been run. When a branch merged into staging, that document was the reference for what "done" meant.

## Where the assistants fit

I routed by strength rather than treating assistants as interchangeable. Codex handled scoped patch loops with deterministic tests. Claude Code handled architecture interpretation, cross-file risk, and planning. I did scope and final review. That split reduced context thrash, because each tool started from a narrower question, and it made handoffs cleaner, because each produced a different kind of artifact.

The merges into staging went in clean. Four feature branches, zero conflicts. That wasn't luck. It was the non-goals list keeping each branch out of the others' files.

## Frontend: the regressions were small and expensive

Almost nothing broke dramatically. What broke was contrast drifting a few points in light mode, a control that looked wired and wasn't, a layout shifting a pixel after a theme change, focus landing somewhere unexpected after a state transition.

The practices that cut that down: every visible button is a contract with a test, dark and light parity is verified with a measured contrast ratio rather than a glance, focus management is stateful and keyboard-safe, and typography is tuned for reading rather than for screenshots. Small inconsistencies cost more in perception than their size suggests.

The About page taught the hardest version of this lesson. Getting a reliable two-page print layout across six resume perspectives took more than fifteen rounds, because `page-break` behavior differs between browsers and every round risked regressing the previous one. The thing that made fifteen rounds survivable was locking each component with a regression test before touching the next.

## Backend: constraints did the work

Quality on the server side came from saying no: server-side prompt keys with contracts, model allowlists with a cost-safety check, a controlled fallback chain with structured provider errors, and sequential inference wherever capacity or latency demanded it. No hidden auto-chaining. No speculative background generation. No silent prompt drift. Constrained behavior is easier to reason about and far easier to test.

## What the retrospective found

The February retrospective was honest about the debt. The prismatic gradient that gives the site its look had been added file by file as pages were created, and by the end it was defined six different ways. Navigation parity across four page types with different container widths had eaten significant time. The predeploy pipeline, three parallel tracks, had caught real regressions in the blog reader and the navigation, which validated the investment, but the test runner itself had habits I didn't discover until later.

The fix for the gradient was a dedicated sprint: one `_gradients.css` as the single source, one `_animations.css` for shared keyframes, an import order written into `docs/CSS_ARCHITECTURE.md`, and an observer that pauses animations off-screen. Sixty-odd fixes, each with a test. The site looks the same. It's maintained very differently.

## Documentation as a runtime dependency

The most repeatable lesson: when the docs matched the code, a new session started from decisions. When they were stale, every session began with twenty minutes of rediscovery, and assistants executed against wrong assumptions just as readily as I did.

So docs became part of the definition of done. Behavior changes update the doc that describes the behavior. Handoffs include test evidence and known risks. The review checklists live in the repo and evolve with it. The decision log records why, not just what, so the next person doesn't relitigate a settled question.

## What "done" meant for this phase

Each app functionally coherent and covered by layered tests. Regressions constrained by automation rather than memory. Documentation that lets a qualified engineer, or a fresh assistant session, continue safely. A blog that reflects the real state of the work with media generated from the real pages.

That foundation is what let the next round of changes go faster without going looser.

Series:

- [Part 1: AI Chat](/blog/ai-lab-project-building-part-1-ai-chat/)
- [Part 2: Promptpad](/blog/ai-lab-project-building-part-2-promptpad/)
- [Part 3: Problem Solver](/blog/ai-lab-project-building-part-3-problem-solver/)
- [Part 4: Agent Agenda](/blog/ai-lab-project-building-part-4-agent-agenda/)
