Haiku

How record works

How Haiku watches a live session and turns it into a .haiku spec — for UI flows on iOS and for backend test runs.

haiku record is the other half of the authoring loop. Instead of writing a spec by hand, you run your app or your tests the way you normally would, and Haiku watches what happens and writes the matching .haiku spec. Editable, replayable with haiku recite, and grounded in something that actually ran.

Record routes by what you give it.

UI recording (iOS)

When the argument looks like a reverse-DNS bundle ID, Haiku takes the UI recording path.

haiku record com.apple.Maps
haiku record com.apple.Maps -o checkout.ios.haiku
haiku record "iPhone 16 Pro com.apple.Maps"

Behind the scenes Haiku:

  1. Resolves the simulator — uses the device name you passed (before or after the bundle ID), or the currently-booted simulator. Boots it if needed.
  2. Launches the app and brings up the Haiku recording overlay alongside it.
  3. Watches the session — the overlay observes accessibility events (tap targets, text input, scroll/swipe gestures, view appearances) and captures screenshots at every meaningful moment so the vision model can later describe what was on screen.
  4. Translates events to English — the captured event stream and the screenshots are folded into a plain-English step sequence: "Tap the search bar. Type Statue of Liberty. Verify the result appears."
  5. Writes a .ios.haiku file — frontmatter populated with the app's bundle ID and the simulator that captured it; body filled with the inferred steps. Default output filename is derived from the bundle ID if you don't pass -o.

The result is ready to hand to haiku recite for replay, or to edit if you want to tighten the prose.

Backend recording

When the argument is a command (anything that isn't a reverse-DNS bundle ID), Haiku takes the backend recording path. The output filename's extension chooses the target language:

haiku record "pytest tests/test_api.py" -o api.py.haiku
haiku record "cargo test --test parser" -o parser.rs.haiku
haiku record "go test ./internal/..." -o internal.go.haiku
haiku record "node src/server.js" -o server.ts.haiku

Behind the scenes Haiku:

  1. Detects the runtime from the command — pytest/python → Python, cargo → Rust, go → Go, npx/tsx/node → TypeScript, and so on.
  2. Runs the command and observes its output: assertion successes, function names that were called, exceptions raised, return values captured at the boundary.
  3. Walks the codebase for the function and module names that showed up at runtime, so it knows which sources actually got exercised (recorded as the sources: frontmatter list, most-called first).
  4. Translates observations to English assertions"Calling create_user with an empty email raises ValueError. Looking up an unknown id returns None."
  5. Writes the .<lang>.haiku file at the path you passed to -o (or streams to stdout if you didn't). Frontmatter carries the sources: list; body is the assertion sequence.

The output runs through haiku recite, which re-generates the native test file and executes it through your language's real test runner — same pipeline a hand-written spec would take.

Trace logs

Every record invocation produces a forensic trace log, same as recite. Default location is /tmp/haiku-record-<timestamp>.log and the path is printed to stderr at the start. Pass --trace for a sticky path in the current directory, or --trace=PATH for a custom location.

When to record vs. write

Recording is great when:

  • The flow is easier to demonstrate than to describe (long UI sequences, multi-step checkouts).
  • You want a spec grounded in the actual function names and assertions your test run produced.
  • You're seeding a new spec from real behaviour and will edit it down.

Writing by hand is great when:

  • You know exactly what you want to assert and the spec is short.
  • You're describing intent that the current code doesn't yet satisfy — i.e. you're using the spec to drive new development.

On this page