Git Could Not Tell Me Who Wrote It

Over one weekend in May I shipped a macOS window manager: forty-one commits, a notarized DMG, a Sparkle update feed, sixty-nine tests. It runs on my ultrawide every day. I'm not going to oversell it. It snaps windows into zones, and that's the whole app.

Every one of those forty-one commits is authored by me, with no co-author trailer, and I didn't type one line of the Swift. Git history alone hides that split entirely.

So I rebuilt it from the only place it still existed: nineteen Claude Code session transcripts, twenty-eight megabytes of JSONL, sitting in a folder under my home directory. This post is what they said.

The Labor Split

Across the nineteen sessions, the agent made 134 file writes and 325 edits. It ran 742 shell commands, thirty of which were git commits. It spawned twenty-five subagents, asked me twenty-three clarifying questions, and churned through 86 task creations and 168 task updates keeping its own to-do list.

Every Swift source file in the shipped repo, all forty-seven app files and all nine test files, was written to by the agent at least once, and most of them many times. The largest single write was the layout editor view, 1,306 lines in one call, later edited forty-one times. The only tracked files it never touched were the license, the notice, and sixteen icon assets I made outside its reach.

The agent made thirty of the forty-one commits. I ran the other eleven from a separate shell, which is why they left no trace in the transcripts.

So the honest description is that the code is agent-authored end to end, with me in the directing seat. The reflex is to hear that as "the agent built it." The transcripts say something more specific about what the directing seat actually involved.

What I Was Doing

Git shows seven real fix commits. The transcripts show a different kind of correction that never became a commit at all, and those are the ones that tell you what the human's job was.

"Let's get rid of the button that collapses the side pane. There is no need for it. It also makes the window title get cut off."

"the add rule button goes away when I'm on the rule ... why don't you just use the same + as for the layouts and just make the user select what they're adding?"

"it worked pretty well, everything on my main screen worked ... windows got mixed up but they all docked. Didn't work on my xenon small display"

"Put a note on that task that it disrupts window placement ... Don't investigate that right now"

"revert everything you just did. trash it"

Read those in a row and the pattern is hard to miss. None of them are about implementation. They're about keeping the UX consistent and the scope small. The single plus button that became a rule across every editor in the app came from the second one. The decision to scope a whole phase to multiple displays came from the third. The fourth is me protecting a release timeline from a rabbit hole. The fifth killed a settings screen and its tests, 164 lines the agent had written minutes earlier, before either file was ever committed. They exist only in one session transcript now.

The implementation calls went the other way: the accessibility attribute toggle that makes some apps cooperate, the resize dance of size, then position, then size again, lifted from how yabai does it, and the live probe through an event tap because the system's own "is this process trusted" call lies. Those came from the agent after it read other window managers' source, and I accepted them without recorded pushback.

I didn't know that was the split until I read the transcripts. My memory of the weekend was that I built a window manager. What I actually did was refuse a collapse button, insist on one plus sign, defer a bug, and throw away a settings page. The agent did the rest, and the parts it did well were the parts where it had prior art to read.

The Corrections Came Fast

Of the seven fix commits, five fixed code the agent had written in the cluster right before, often under two hours later. The drag-to-snap overlay shipped at 10:22 and only worked on the primary display. The multi-display fix landed at 12:15. Workspace auto-restore shipped at 12:36 and fired on every grid snap instead of only on arrangement changes. The fix landed at 13:18, twenty-six minutes before the release commit.

That's the rhythm: ship, ship, fix, ship. If you're waiting for the version of this workflow where the agent gets it right the first time, you're waiting for something the transcripts say doesn't exist, at least not at this pace. What exists is a loop that expects the fix and tolerates it. The fix is cheap because the thing that broke was written an hour ago and the context is still warm.

There are zero revert commits in the history and one revert in the transcripts. Those are different facts, and the second one matters more.

Four Lines

The repo's CLAUDE.md is four lines long: target Swift 6 idioms, no force unwraps, Sendable across actor boundaries, and notes on SwiftUI modernization. Each clause traces to something that went wrong on the first pass.

The initial engine commit shipped with force unwraps and unguarded accessibility calls. The very next morning, commit two of the day was a whole pass to remove them. The "no force unwraps" rule was written after that pass, not before it. The Swift 6 hardening got bundled into a later feature commit because the earlier code wasn't ready for strict concurrency, so the rule about actor boundaries was also written after the fact.

The first version of the CLAUDE.md was wrong about the project itself. It described something other than a macOS app on Swift 5.10, and it was rewritten five minutes later to match reality. For five minutes, the file that instructs future sessions was instructing them about a project that didn't exist.

Most people write that file first, and it reads like a wish list. Mine reads as a list of scars, because that's what it is. There's a longer essay in that. This is the shortest version: a four-line file, each line a fix commit you can go find.

What Git Recorded

The commit log by itself tells a clean story. Phase 0 engine baseline Sunday night. Phase A through D on Monday: editor, release pipeline, per-app rules, arrangement detection. Phase E and F Tuesday morning: mouse-driven snapping, grid overlay, workspaces. Release commit at 13:44. Thirty-eight and a half hours from baseline to v1.0.0, of which about fifteen were active. The rest of the time I wasn't at the keyboard.

It's clean, and also incomplete in a way I wouldn't have noticed if I hadn't gone looking. The commit log records the destination. It doesn't record that I killed a feature, or that the agent got the project description wrong, or that every UX decision in the app came from a one-line message. That record lived in a folder I'd never opened until I decided to write this.

I'm not sure what to do with that except keep the folder and stop trusting my memory of weekends. Git is the story we tell about the work. The transcript is the work.

-- Justin Higgins. Software Engineer, Midwest. Shipped a window manager in a weekend and then read what actually happened.


Companion pieces: The Work Looks Like Nothing - why the session log is the first record of judgment that ever existed. The Rules Are Downstream of the Work - the four-line CLAUDE.md, at essay length.

Reactions, disagreements, war stories: jchigg2000.dev@gmail.com