Case study by Gabriel Espinheira, senior full-stack engineer
Relay, my own AI harness
A macOS app where models from four providers write, draw, speak and render in one project folder, on plans I already pay for. I built it directing coding agents: first commit 28/09/2026, v0.9.0 on 08/10/2026.
A private tool that runs only on my Mac. Open to full-time roles and contracts.

What the repository says
236
commits in 12 days, from 28/09/2026
7
tagged versions in four days, up to v0.9.0
163,636
lines of product TypeScript
3,983
tests declared in 543 files
73
tools agents call over MCP
4
chat agents, plus 9 media generators
Counted from the repository on 09/10/2026 at commit d0aa1e5. Test figures are declarations in the code, not a test run.
Good models, no studio
My studio, SharpHaw, makes images, voices, films and copy for clients every week. The model I want for each job sits in a different app. A raw model forgets the prompt behind last week's image, can't keep a character the same across shots, and never looks at the frame it just rendered.
Relay is the harness around those models. It adds five things raw models lack, and every feature on this page answers to one of them.
- Structure
- traits and briefs become the prompt
- Provenance
- every file keeps the exact prompt that made it
- Consistency
- a character or a voice stays the same across shots
- Choice
- switch the model mid-session, fork, compare versions
- Feedback
- agents render and inspect their own frames
One project, every medium
A project is any folder on my Mac. Relay keeps its sessions, prompts and media in a relay/ folder beside the files. Each film below is a real recording of Relay 0.9.

Every agent in one composer
Claude Code, Codex, Grok and Antigravity run behind one provider contract, each on its own signed-in CLI. Claude Code runs through the Agent SDK and Codex through one shared app server. Grok and Antigravity share a single Agent Client Protocol adapter. Switch agents mid-conversation and the new one gets the transcript, trimmed to fit 24,000 characters.

Auto picks the model for each message
Before each turn in Auto, a router model picks the agent, model and effort. It chooses from the models I switched on, using how fast each subscription is burning and a routing table built from public benchmark feeds. Its answer only counts if it names one of those options, and any failure runs the turn on the default model.

A studio where the prompt stays with the file
An artifact is a markdown file whose body is the prompt. Each generation writes a run record beside its output with the prompt, parameters, model and timing, so any image or voice can be traced and remade. Characters are built from traits, and writing a character's name in a prompt attaches its sheet.

Films written as code
A film is one HTML page whose seek function paints any frame as a pure function of time, with springs solved in closed form. Relay previews it live, renders it with eight subframes of motion blur, and mixes its sound to −14 LUFS. Agents lint their own films, prove each frame renders the same twice, and read contact sheets of the result.

Boards that agents draw on
Canvases are Excalidraw boards with cards for artifacts, sessions and links. Agents read and draw on them with six tools, and edits from me and from agents merge element by element. The board in this film is the plan of this page, drawn by an agent from one prompt.

Work that runs without me
Routines run instructions on a schedule in a project, and the scheduler reads the wall clock every 15 seconds because timers stop while a Mac sleeps. Sub-agents split a session's work across agents and report back. Bits are always-on assistants with their own memory. They change a project only through a task, and only with the access I grant them.
One daemon, thin clients
One Node process owns every project, session, agent and file. The browser and the Electron app only render it. Commands go over HTTP, and every change comes back as an event, replayed from a buffer of 10,000 when a window reconnects. Closing every window never stops a turn.
App state lives in SQLite, built into Node, with full-text search kept in sync by triggers. Project content lives in files: prompts, run records, films and recordings sit in the project folder where any agent can read them.
A run record is a file, not a row.
- Browser tabs
- The Electron app
HTTP commands, SSE events
Relay daemon · Hono API, event log, MCP tools, terminals
Agents · Claude Code, Codex, Grok, Antigravity
Generators · GPT Image, Qwen3-TTS, ElevenLabs, Stable Audio, motion
SQLite for app state
Project folders for content
Built as if the web is hostile
The daemon runs agents that execute commands, on a port any web page can reach. The README tells every agent to treat the web and model output as hostile, and these are the defenses that follow from it.
Local only
The daemon binds 127.0.0.1, accepts only its own Host header against DNS rebinding, and rejects cross-site requests. Every call needs an HttpOnly cookie or the launch token, compared in constant time.
Agents get their own keys
Relay's MCP endpoint takes only per-session tokens kept in memory, never the launch token. Sub-agents can't delegate further, and a Bit's tool set has no shell and no project tools.
Files stay in the project
Every path resolves inside the project root before a read or write, nothing writes into .git, and deletes go to the Trash.
The browser waits for a person
In Relay's own browser, an agent's submits, sends, payments and deletes wait for my Allow, and Allow refuses if the page changed in the meantime.
Public addresses only
Link previews and agent fetches reach public addresses only, checked again at DNS lookup so rebinding can't swap them.
Agent-written films run sandboxed
A film is JavaScript an agent wrote, so it loads in a sandboxed frame through capability URLs signed per boot and expiring after six hours.

The checks every change has to pass
The repository's instructions tell every coding agent to write the failing test first. A change is done when lint, types and the full test suite pass, and the pre-push hook runs all three on every push.
A fake agent for real tests
A deterministic fake provider, driven by keywords in the prompt, runs whole turns end to end without spending a credit: tools, approvals, questions and failures.
Design rules as a test
One test enforces the design system with seven exact-count allowlists. A new raw control fails it, and so does fixing an old one without lowering the count.
Plans that admit drift
Each of the 30 design plans ends with what was built, how it was checked and where the build departed from the plan. The code wins over the plan.
A refused release became a gate
On 08/10/2026 a version merge reached main without its release commit, and the release workflow refused to tag it and opened an issue. A new check on version pull requests followed, and v0.9.0 shipped about ten minutes later.
Twelve days, one author, many agents
Every commit carries my name, and the work happened in agent sessions. My local logs hold about 250 Claude Code and 164 Codex sessions on the repository. Relay has run sessions on itself since 30/09/2026.
I own code I didn't type through what surrounds it. Each major feature starts as a written plan, every agent reads the same rules, and the checks above must pass before anything is pushed.
The first Relay code landed four hours after the scaffold, as one commit of 17,941 lines with 216 tests. The films on my SharpOS and Taste Vault case studies were composed and rendered in Relay's motion engine, then encoded for the web.
| Day | Commits |
|---|---|
| 28/09 | 3 |
| 29/09 | 19 |
| 30/09 | 18 |
| 01/10 | 50 |
| 02/10 | 17 |
| 03/10 | 17 |
| 04/10 | 18 |
| 05/10 | 27 |
| 06/10 | 30 |
| 07/10 | 24 |
| 08/10 | 7 |
| 09/10 | 6 |
A hard call: films as a function of time
Problem
Agents had to make motion graphics with exact text, interface and brand, and preview them live. Every frame had to come out the same each time, so renders, checks and fixes could be trusted.
Options
A video model can't hold exact text or a brand, and every fix means paying for a new generation. Remotion needs a React bundle per film and a paid company license for my studio.
Choice
One HTML page with a seek function and a small vendored engine. Every animated value is a damped spring evaluated at time t, with no state carried between frames, so any frame can be painted in any order. That makes motion blur, parallel rendering and a determinism check possible.
Timers, animation frames, CSS transitions and random numbers are linted out.
Cost
Agents have to write in a constrained style and pass the lint. Rendering is offline and heavy: this site's films took 2 to 8 minutes each at 1080p, and 83 to 131 minutes when supersampled twice.
A smaller call: a Node daemon in a Bun repository
The repository runs on Bun, but the daemon runs on Node with its built-in SQLite, because the Claude Agent SDK and the test suite target Node. The cost is two runtimes in one repo, no Bun APIs in the server, and an experimental SQLite API kept behind one function.
Need someone to own the build?
I'm open to a full-time role or a contract build. I read every message myself and reply within 2 business days.
- Full-time role
A senior engineer on your team
Remote on EU hours, or hybrid and on-site in Lisbon.Talk about a role - Contract build
Your product, built end to end
Web, desktop and mobile on TypeScript, Next.js, Convex and Expo, including AI features.Talk about a build
