Twenty AI agents built an iOS app end to end. The human's job turned out to be something else entirely.

I shipped an iOS app using AI agents as the team. Not “AI-assisted”—agents wrote the Swift, ran the audits, reviewed the App Store copy, and built the release scripts. I was the PM and the QA. Mostly the QA.

Here's what that's actually like.


Twenty Agents, Zero Conflicts

The team had specific roles. An apple-developer agent owned all Swift/SwiftUI implementation. An apple-designer owned design artifacts and interaction specs. A design-qa agent ran audits and returned PASS/FAIL verdicts—it never fixed anything, only flagged. A code-architect built the automation tooling: build scripts, pre-archive gates, screenshot pipelines, and submission runbooks. A voice-qa agent reviewed the App Store copy before submission. And a rotating cast of exploration agents found things in the codebase when I wasn't sure where to look.

In peak sessions, roughly twenty agents ran concurrently on different parts of the codebase. The technique that made this work: each agent got an explicit, non-overlapping write-set before launching. Type contracts were dictated before agents started. Single build at session end.

Zero merge conflicts across the entire project. That part worked better than I expected.

What the Audit Couldn't See

The design audit was the first time I realized the agents couldn't do everything.

The parallel audit—five agents, five dimensions (fidelity, layout, theming, accessibility, and code), running simultaneously—caught real problems. A non-deterministic hash function silently corrupting import dedup across launches: the app was generating different filenames for the same imported track on every launch, creating duplicates without error. WCAG contrast failures on 35 color values across nine themes. The root cause of a screen treatment that hadn't looked like glass yet: wrong specular, missing backlight bloom, incorrect recess depth.

These were good finds. The kind of systematic sweep I couldn't have done manually in the same session.

The audit also passed the mode-picker.

Now Playing had a center-tap interaction that cycled between states, each one replacing the progress bar and shrinking the album art to show a different settings sub-panel. It was internally consistent—the code did exactly what it said it did. Every correctness lens passed it. The pattern was coherent.

It was also completely wrong for the feel of the app.

The rule—“the wheel controls classic iPod things; touch handles everything else; center never takes over the screen”—was implicit at that point. The agents didn't have it as an explicit input. The audit had no lens for “does this belong here at all?” So it passed something that was technically correct and shouldn't exist.

I caught it on device. The fix was deletion: removed the entire mode-picker, all related code, all related state. The agents did the removal fast. I had to see the problem first.

The 8-Build Lock-Screen Bug

The 8-build lock-screen bug was the longest single debugging arc.

Lock-screen media controls were wrong—inverted play/pause, stale scrubber, required two taps to respond. The apple-developer agent added explicit metadata fields, per-tick resync, and audio session activation on play. Every intervention was correct. Nothing changed.

An on-screen readback showed the now-playing info was right the whole time: paused, correct playback rate, correct elapsed time. The metadata was fine.

Eight device builds across the session. The actual culprit was the click sound effect.

HapticService ran an audio engine for haptic clicks. That engine started on the first click and never stopped. A continuously running audio engine keeps the session active. iOS inferred “audio is playing” from the live session and overrode the explicit now-playing state—ignoring the correctly set values entirely.

The fix was a debounced engine stop: half a second after the last click, the engine pauses. Two lines of change.

The metadata was never the problem. The click sound effect was.

Eight builds to find that. The agents couldn't have found it any faster than I could—both of us were reasoning about the wrong layer. It took running out of metadata interventions to look somewhere else.

A Missing Line

The bug that lived longest wasn't the hardest to fix. It was a missing line.

The runtime Apple Music show/hide toggle added a new navigation node to the Settings menu. The feature was wired into the menu enum, the navigator, the preference store, and the view hierarchy. Technically complete.

Not included: isNavigable().

The dispatch function guarded on whether a node had children or was listed as navigable. The new case wasn't listed. So center-tap highlighted the Settings row—that code path used a different gate—then silently dropped the action. No error, no log, and no feedback. The row looked interactive. It wasn't.

One line fixed it. The bug lived through the feature's initial build and into a subsequent review session before being caught.

Adding a new navigation node requires five coordinated edits in five different files. The agents made four of the five. I spent time on device wondering why a feature I'd watched get built wasn't working.

The Person With the Phone

The human's job in this collaboration wasn't what I expected.

I thought I'd be the architect—designing the system, delegating the implementation. That part was true. But the more important role turned out to be the person with the phone.

The agents built exactly what they were told. The audits caught exactly what they were designed to catch. Neither of those is the same as knowing whether something feels right.

“This looks cool”—my verdict when the screen treatment finally read like glass—wasn't available from any audit. “Bulky/clunky”—my reaction to button chrome that was technically fine but felt wrong on the LCD—required a human looking at it on a 6.1-inch screen. The cramped Now Playing layout, the glow that read “raised” instead of “recessed,” and the mode-picker that passed every check and still shouldn't exist: all of these were on-device reactions, not spec derivations.

The design iteration loop was: rough spec → build → I react on device → next pass. The agents handled the spec and the build. I handled the react.

That's a specific kind of contribution. Not architectural, exactly. More like: taste, judgment, and knowing when the output is correct but the brief was wrong. I went in expecting to be the architect. I ended up being the oracle.


klikWheel shipped to the App Store—v1.2.0 at submission—and it's kept iterating since (v1.6.0, build 33 as of late June).

The team did most of the work. The work kept requiring me to be there.