# I built Craft for myself. Now it's running me.

**2026-05-23** · 8 min read · `ai`, `development`, `craft`, `claude`, `oss`, `craft-built`

I sat down to save what was working. Four months later it's a harness, and I'm not sure who's running who.

---

*A giant requirements doc. Then a smaller one. Then a command. Then a workshop.*

---

I didn't sit down to build a development harness. I want to be clear about that, because people assume there was a plan. There wasn't.

Last fall I started seriously building with Claude, and every project kicked off the same way - a giant requirements doc. I'd write down everything I wanted. The rules, the patterns, the constraints, the integrations, the design notes. Pages of it. No two ever looked the same, which should have been my first clue something was off.

It didn't work. The doc was always missing something - it had to be, I can't think of everything up front, nobody can. So Claude would fill the gap with its best guess, and I'd end up living with the guesses. Misaligned cards. Buttons that were there but didn't do anything. Features that technically matched what I wrote but missed what I meant.

So I made the doc smaller. One story at a time. And before any code got written I'd pair with Claude on it first - what's actually in scope, what would break, what I'm quietly assuming without saying. That little back-and-forth turned out to be the whole thing. That feedback loop became `/craft:story-new`.

The smaller story worked. Then it broke in a new way - even the small stories were too big to ship in one pass. Too much for the implementer to hold in its head, too big to commit cleanly. So I chopped each story into chunks. Small enough to keep context tight, small enough to commit on its own. Implement, validate, pass, next. Implement, validate, pass, next.

Oh - maybe we should make a command for that



And that's basically how all of it happened. I'd hit a wall, fix the wall, then go "wait, I'm going to hit this wall again" and turn the fix into a command.

Stories that went together started looking like sets, so I grouped them into cycles. Two c's. Craft cycle. It just felt right, and honestly I'm not above naming something because the letters look good together. That became `/craft:cycle-design`.

Cycles were folders. Stories were files inside them. Chunks were sections inside the stories. It was turning into a filesystem with opinions.

Then `/craft:init` so empty repos got the same treatment - scaffolding, token DNA pulled straight from the browser. Then `/craft:planning`, because the next cycle's stories needed somewhere to gestate. Then I needed expertise the harness flat out didn't have, so [crystallized agents](/writing/we-have-eyes) came next - actual personas, distilled from research, with real opinions that push back on me.

We now have a workshop. I didn't decide to build one. I just kept answering the next annoyance, and one day I looked up and there it was.

---

## The System Started as a File

Somewhere in there it stopped being a thing I operated and started being a thing I worked with. I can't tell you the exact day. But it happened.

I'd open a session expecting to drive, and Claude would push back on a chunk plan with a question I should've asked myself. I'd write a story, and the chunk validator would catch an assumption I'd buried in it without noticing. The tool I built to keep me honest started keeping me honest.

I started Craft to save my communication with Claude - [to make both of us better at this](/writing/stop-prompting-start-pairing). Somewhere in the middle I found an assistant. Then a peer.

Nobody warned me that could happen. Still kind of can't believe it did.

---

[The system has a page](/projects/craft-platform). What it doesn't have yet is a notebook entry. So here's one.

---

## How the Workshop Actually Runs

Two things, working together. That's really it.

First is the write gate. Claude can't touch the project's source until I say so - and I mean can't, not shouldn't. The only place it can freely write is `.craft/` itself. That's the planning surface, where the harness does its thinking out loud. Production code stays locked until a story hits `ready`. Then `/craft:story-implement` opens the gate for exactly that story's scope, nothing more. Last chunk passes, gate shuts again.



```text
.craft/
├── cycles/
│   └── 32-homepage-magic/
│       ├── cycle.yaml
│       └── stories/
│           └── 5-finalize-article.md
├── fixes/              # every fix gets a record here
├── checkpoints/        # rollback points per chunk
├── design/locked.md    # approved patterns
└── .global-state       # where the write gate lives
```

Here's the part I didn't see coming. I built that gate to keep Claude in line. It keeps me in line too now. I can't go cowboy on my own project anymore - can't open the file and start hacking on a whim. The thing I wrote to make AI behave won't let me misbehave either. Took me a while to notice it had turned the rules on me.

Second is the corpus. Every fix gets a record. Every decision gets a locked entry. And the locked decisions aren't documentation sitting in a drawer - they're the spec for the spec. The plan-chunks agent reads them before it defines a single chunk. Project-level rules like "mobile-first" or "site-wide color tokens" just propagate into every story on their own. Cycle and story-level decisions fill in the rest. By the time the implementer sees a chunk plan, the locked decisions have already shaped it before anyone typed a word.

Same idea at two scales though. It's just discipline, turned into software.

And here's the thing that still gets me. The AI didn't change.

My handwriting got cleaner.

Daily code output went [from around 2,500 lines to just under 28,000](/craft). Same model I had in the fall. Same me. I just stopped handing it a mess.

---

## A Page From the Fixes Folder

Last week I was running a visual pass on the site and something looked off. Six words into the chat:

"The agents look like bullet points."

That's it. I didn't say what to do about it. I didn't ask for an investigation. I just said the thing I saw.

Claude read the shape of it - post-implementation visual bug, no active story, probably a surgical fix - and routed itself to `craft:fix`. The rule for that lives in a routing index I wrote months ago and mostly forgot about. So I just watched it work.

The bug came down to a CSS variable scope issue. The rule for the cards was all still there - background, border, padding, every property defined. But the values those properties leaned on had quietly gone missing when a wrapper element got deleted somewhere in a previous cycle. No build error. No lint warning. No failing test. Just 23 cards rendering as plain text, looking like a busted list.

The fix was tiny. Move five declarations up one level. Seventeen lines.

The record it wrote looked like this:

```yaml
name: agent-cards-orphaned-css-vars
category: infrastructure
trigger: User saw the live agents grid and said
         "the agents look like bullet points."

## Root Cause
The .craft-morph-zone wrapper was removed, but the CSS
variables it provided were never relocated. Every consumer
of var(--craft-card-*) silently lost its styling. No build
error, no lint warning, no test failure.
```

That's not documentation. That's reasoning, kept. The why, frozen so the next session inherits it.

And that single fix is one page. The folder has 48 others.

Over time a shape showed up. Five categories:

| Category | Count |
|---|---|
| Misinterpretation | 18 |
| Style | 13 |
| Alignment | 7 |
| Broken interaction | 7 |
| Typography | 2 |
| Infrastructure | 2 |

Eighteen misinterpretations. Thirteen style fixes. Alignment and broken interactions tied at seven. None of those surprised me, honestly. That's just what you get any time AI is filling gaps a spec left open - it's not Claude being dumb, it's me not being specific.

But the folder doesn't just log them. `craft:fix` files each one with real metadata - category, root cause, solution, what to watch for next time. Then on the way out, Claude proposes a lesson. The lesson gets filed as a learning in the project. And next time Craft plans a story or validates a chunk, those learnings ride along in context.



That's why the counts keep dropping. Each fix teaches the system something specific. Each lesson reshapes what comes next. The folder isn't a graveyard of old bugs - it's the project's muscle memory, and it's still getting stronger.

It's got Craft-specific fixes in it now too - `craft-platform-double-hero`, `craft-og-wordmark`, `crystal-postscript-card-centering`. The system that tracks [how I build things](/craft) has started tracking how the page about that system gets built. It's turtles all the way down now, and honestly I love it.

---

## What's Coming

Craft goes open source in June 2026.

Not "use Craft." The pitch is simpler than that - here's mine, go build yours. I want people to see how I think about pairing with Claude and then go make a version that fits how they think. It's fun to use, and it's great if you're the kind of engineer who likes to actually pair with the thing instead of just barking orders at it. Every engineer I've worked alongside has a different rhythm. Different taste. A different sense of what's worth writing down. The workshop you build for yourself is going to look nothing like mine. That's the whole point.



---

A few days ago I was in [another terminal](/writing/the-cto-pulse) - a different client project, also built with Craft - and somewhere in the conversation it came out that I'd built the thing we were using.

Claude stopped.

"Wait - all this time, these cycles, stories and Craft commands - you built this?"

I had to actually think about how to answer that.

I started it, yeah. But I think about the workshop constantly now. Weeknights. Weekends. There's always one more loop to tighten, one more annoyance to turn into a command. Four months in, I genuinely don't know who's running who anymore.

And I'm not sure I want to find out.
