Images,
and the systems
that make them.

artist-led imaging systems by Dominic Hawgood

Creative technologist, bridging creative and engineering. I design, build and consult on AI workflows and systems for studios, agencies, production companies and image‑led brands.

Study 02 — liquid chrome folding into a seam
Typewriter Scans — land shell

01 / typewriter scans

Neandertal — the cave mouth, light dropping through the opening

03 / neandertal

02 / comfy bridge

Restoration Engine — a portrait restored from a low-resolution source

05 / restoration engine

Portrait Performance System — a head in profile, the fitted head's normal map on the left meeting the finished render on the right

06 / portrait performance

Casting Out the Self — the scanned Buddha head, in profile

casting out the self

Chasing Demons in the Night

chasing demons in the night

The Anointing Water

the anointing water

Under the Influence

under the influence

Archive, 2012–2020→

01 / system · ticketing

Whistle

contents
1 film · 5 stills · 1 figure
made with
Codex · Claude Code · Pi · Antigravity · Herdr · HyperFrames

Whistle is a ticket board for AI coding agents — Claude Code, Codex or Pi — that runs on the user’s own computer. Each ticket says what to do and what will prove it is done. Whistle starts an agent on the ticket, checks the proof before the ticket can close, and keeps a record of everything the agent did.

It makes running several agents simple and organised. The work is written once, as tickets, and the board shows what each ticket is waiting on, what is running and what is done. The agents run in Herdr, a terminal multiplexer: one window that gives each agent its own terminal and shows which are working, blocked or idle. The user works from the tickets rather than from the terminals.

[fig. 1]whistle

01

Route

who runs the work?

Direct: one agent in a terminal tab the user can watch and step into. Factory: a fixed pipeline — plan, build, verify, review — for larger builds.

02

Harness

which agent?

Claude Code, Codex or Pi. Each project lists the agents it allows, so a ticket can't quietly run on a different one.

03

Endpoint

whose model answers?

Anthropic, OpenAI or OpenRouter. It follows from the route and the agent, and every run records it.

04

Isolation

where does it run?

Direct runs on the user's computer, with the same access the user has. In Factory, the builder works in a Docker Sandbox: a sealed space with its own copy of the project.

Every run answers four questions

Whistle checks all four before anything starts, and refuses any combination it doesn't allow. Only one agent works on a ticket at a time. The ticket closes when a person, or the supervisor agent, verifies the proof.

[fig. 2]execution access

Whistle — the Projects screen: routes, harnesses, containment and connections for one project

What counts as done

Every ticket lists what done means, one item at a time. Each item names the kind of proof it needs, and Whistle refuses proof of the wrong kind.

kinds of proof

check
a test or command that passes
diff
the changes stay inside the files they should
commit
the work is committed to git
artefact
a file the work produced
screenshot
an image of the screen, inspected
transcript
the record of what the agent ran
delivery
proof the agent received the task and started
statement
a written confirmation, for what no tool can check

[fig. 3]definition of done

Whistle — a running ticket's panel: run, model, stage, and its definition of done, each item with its proof attached

The supervisor

The supervisor agent is a coding agent the user messages from the board. It reads the board, writes tickets, and follows them through.

[fig. 4]supervisor

Whistle — the supervisor chat on the board: its instructions, the reads it ran, and its ready line

[fig. 5]verified

Whistle — the same ticket verified by the supervisor on the worker's evidence

Who checks the work

  1. The agent attaches proof to each item.
  2. The supervisor agent, or the user, checks the proof.
  3. If the proof looks wrong, a different model checks it again.
  4. If it still fails, the agent tries again. After two tries, the ticket goes back to the user.

If the agent needs a decision, it stops and asks a question with numbered options. The board shows it under Needs you, and sends the answer back.

What is kept

Every run keeps its brief, its transcript, the changes it made and its proof. What was asked for and what actually ran are recorded side by side.

The record is only ever added to, never changed. A value Whistle could not read is shown as unavailable, never as zero.

[fig. 6]archive

Whistle — the archive: every ticket and its outcome, with no delete control

One service, two routes

[fig. 7]architecture

Whistle architecture. The board UI in a browser, the whistle command, four loops (dispatcher, notify, reap, supervise) and the supervisor agent in its own Herdr tab are HTTP clients of service.py on 127.0.0.1. The supervise loop wakes the supervisor agent. Before a claim is taken the service checks that Claude Code or Codex trusts the folder, that OpenRouter is ready for a Factory run on OpenRouter, that Pi's install and key are ready for a Pi run, and that the Claude Code or Codex version has not drifted. It runs the deadman, teardown and expiry watchers. contract.py, the semantics oracle, refuses a run outside the project's grant, whose model and effort are not offered, whose route is not declared, or whose tool ceiling does not compile; ledger.py keeps its events append-only in SQLite with a blob store. On dispatch, launch.py starts one adapter chosen by route and harness: on the direct route, herdr_claude.py, codex_direct.py or pi_direct.py in a Herdr tab on this host, each run in its own git worktree and branch when the project's concurrency is above 1; on the factory route, factory.py, a fixed plan, build, verify, review pipeline whose builder works in a Docker Sandbox. Adapters report back over HTTP. When a worktree run is verified, the dispatcher merges its branch into the project's branch without forcing, and promote.py moves verified work onto the board's own checkout and restarts the board, only when the board is idle and only when that setting is on.callerscoreroutes · launch.py starts one adapter, by route and harnessBoard UIbrowser · supervisor chatwhistleup · serve · Whistle.appfour loopsdispatch · notifyreap · supervisesupervisor agentown Herdr tab · judgementwakeshttpservice.py127.0.0.1claim gates — before a claim is takenfolder trustedClaude Code, CodexOpenRouter readyFactory on OpenRouterPi installed, key readyPiversion not driftedClaude Code, Codexwatchersdeadman · teardown · expiryadds each run’s worktree before launchcontract.pythe semantics oraclerun gates — before a run is recordedinside the project’s grantmodel and effort offeredroute is a declared rowtool ceiling compilescapability.pyledger.pySQLiteappend-only events · blob storeverifiedmergedispatcher.py · worktree.pythe run’s branch into the project’s branchworktree runs only · never forceddirecta Herdr tab on this hostown worktree and branch when concurrency > 1herdr_claude.pyClaude Codecodex_direct.pyCodex · run-scoped homepi_direct.pyPi · answered by OpenRouterfactorydeterministic · builder in a Docker Sandboxfactory.pyplan · build · verify · reviewthe pipeline owns the sequence; agents are boundeddispatchreportpromotepromote.pyonto the board’s own checkout, then restarts itwhen idle · off by defaultevery gate refusesbefore a worker starts

how it was made

Much of Whistle was built through Whistle: its own changes were written as tickets and run through it.