The execution loop: GitHub, Coolify, Playwright, and the two touch points that run the studio
Part 3 of How I Ship Side Projects. Part 1: planning. Part 2: the agent crew.
By the end of part 2, the agent crew was running. The CEO summary lands on my Discord at 06:00 Vienna. The product PMs work morning and evening shifts. Twelve agents share Linear as their workplace, and one label, agent, is the trust boundary that keeps me in control of the parts I should still own.
What part 2 left out was the rest of the loop. Where does the engineer's code go? How do I review it without burning my whole day on it? Where do the marketer's landing-page drafts end up? And how do I keep all of it reachable from my phone in a café without giving the agents internet access to anything dangerous?
This is the article for the devs in the audience. It's the most concrete and most copyable of the three. The previous two were "here's how to think about it." This one is "here's the wiring."
The whole loop comes down to two daily touch points, Linear and GitHub. Everything else is automated. I'll walk through each piece in roughly the order a feature flows through the system.
The deliverables repo trick
Before I get to code, the most useful piece of plumbing in the whole setup is one I almost didn't bother to write up because it sounds too simple. It's not.
There's a single shared GitHub repo, mraaroncruz/paperclip-deliverables, where every non-code artifact from every agent lands.
The repo is organized by sub-company:
paperclip-deliverables/
├── encroach/ # Encroach marketing & assets
├── billmycommits/ # BMC marketing & assets
├── happyclients/ # HappyClients marketing & assets
├── content/ # Content Strategist drafts (cross-company)
└── talks/ # Talk Researcher proposals & outlines
Every agent that produces a non-code deliverable (landing-page copy, social posts, weekly analytics digests, talk proposals, blog drafts, screenshots, hero images, style guides) saves it into the right subdirectory and commits. The agents have write access to this repo and only this repo.
When an agent finishes a draft, it follows the same pattern across all twelve roles. The exact instruction in the Encroach Marketing agent's AGENTS.md:
All content deliverables (marketing drafts, images, social posts, etc.) MUST be saved to the shared deliverables repo at
/home/aaron/Projects/deliverables/encroach/. This repo is published athttps://github.com/mraaroncruz/paperclip-deliverables.
Save files using descriptive filenames
Commit and push your deliverables to the repo
In your Paperclip task comments, Linear issues, and GitHub PRs, link to the file on GitHub
So the agents don't email me drafts. They don't generate files into a local folder I'd never see. They commit, push, and link. The link is what I get in Discord and Linear.
Why this matters more than it sounds:
I don't need access to the Mac mini to see what the agents made. I'm in a café in Vienna with a laptop. The marketer just produced three blog post drafts. They're in GitHub, and I can read them on my phone. No Tailscale into my home network, no asking the agent to send me the file. They're already in the place I'd look anyway.
The agents on the Mac mini don't need outbound access to my hosting. The marketer has no credentials for Cloudflare Pages, Plausible, or Kit.com. It pushes to a GitHub repo. From there, downstream automation (which I control, with its own credentials) deploys. If the agent goes haywire, the worst it can do is commit nonsense to a repo I review before any of it goes live.
The whole studio gets versioned. Every landing page change, blog post, and analytics digest is a commit. I can roll back a marketing change as easily as a code change. I can find out why the homepage hero changed last Tuesday and what the agent's reasoning was, because the commit message and the linked Linear issue are right there.
It works when I'm offline. The agents keep running on the Mac mini whether I'm online or not. When I come back, GitHub has the diffs waiting for me.
The deliverables repo is the most novel piece of plumbing in this setup, and it's a five-minute change. If you take one thing from this article, take this.
Code: the engineer agents' PR loop
The engineer agents work against the product repos themselves. For Encroach: encroach. For BillMyCommits: billmycommits_ex. For HappyClients: happyclients_ex.
The flow is what you'd expect:
The PM picks up an issue labeled
agentfrom Linear and either does the work itself or delegates a Paperclip subtask to the engineer.The engineer checks out a feature branch, writes the code, runs
mix testandmix format(enforced in every engineer'sAGENTS.md), commits.Before opening the PR, the engineer captures Playwright screenshots of any changed UI (more on this below).
It opens the PR via the
ghCLI with a description that closes the Linear issue (Closes ELB-1234), explains what changed, and embeds the screenshots.It posts a Discord notification with the PR link.
I review. Screenshots first, then the Coolify preview if I need it, then the diff if I still need it.
I merge or comment back. The engineer reads the merge in the next batch and moves the Linear issue to
Done.
The Linear-to-PR link is the one rule I won't bend on. Every PR description ends with Closes ELB-XXXX. Every commit message references the issue. Six months from now, when I'm asking "why does this code exist?", I can go from git blame to commit, commit to issue, issue to milestone, milestone to the original product hypothesis. Full traceability for the cost of one line per PR.
Playwright screenshots in the PR
This is the piece that surprised me most once the workflow was running.
I have a playwright-cli skill that any engineer agent can call. Before opening a PR for any UI change, the engineer:
Starts the Phoenix dev server.
Navigates to each affected route.
Captures screenshots at two viewports: desktop (1280x720) and mobile (375x812).
Embeds the screenshots in the PR body using markdown.
The skill, in summary:
# Desktop screenshot
playwright screenshot --viewport-size "1280,720" \
http://localhost:4000/dashboard \
dashboard-desktop.png
# Mobile screenshot
playwright screenshot --viewport-size "375,812" \
http://localhost:4000/dashboard \
dashboard-mobile.png
A typical PR ends up looking like:
## What changed
Added CSV export to the billing history page (Closes ELB-1234).
## Screenshots
### Desktop

### Mobile

Most of my UI review now happens in the PR itself, on my phone, before I click through to anything else. I scan the screenshots. If the change looks right at both viewports, I merge. If something looks off, a button in the wrong place or a layout glitch on mobile, I catch it without spinning anything up.
The Playwright pattern handles maybe 70% of my visual review. Coolify previews handle the other 30%, the cases where I need to click on the thing, fill in a form, or watch state change. I need both.
I didn't expect this part. Capturing mobile screenshots by default has caught more bugs than any other single change to the workflow. Left alone, the agents optimize for what they can see in the dev server, which is desktop. Forcing a mobile screenshot into every PR forces mobile awareness onto every change. Most of the layout regressions I've caught in agent PRs were mobile-only and would have been invisible without it.
Coolify previews: review the running app, not the diff
For the 30% where screenshots aren't enough, anything stateful or interactive, I rely on Coolify PR previews.
Each product has a Coolify application connected to its GitHub repo with PR preview deployments enabled. When the engineer opens a PR, Coolify builds the branch and deploys it to a unique URL like pr-142.encroach.example.com. That takes a couple of minutes, and a post-deploy webhook posts the preview URL as a comment on the PR. You could also set up something similar using AWS Cloudformation or GCS Cloud Run if you don't want to host it yourself.
I never pull the branch, install dependencies, or run anything locally to review a feature. I click the link, use the running thing, and decide.
Reviewing a 600-line PR from an agent by reading the diff is hard. The diff doesn't tell you the new validation message is too quiet, or that the export button sits below the fold on small screens. Screenshots catch the visual stuff. The running app catches everything else. The diff lies about what code does in ways that running code can't.
I now read the diff only when something in the running app or a screenshot feels off and I want to know why. The diff is a diagnosis tool, not an approval gate.
Coolify preview setup, the abridged version
This assumes you've got Coolify running on a server. Mine is on a Hetzner box. A $5 VPS works for small projects.
1. Wildcard DNS. Add a wildcard CNAME for the preview pattern:
*.encroach.example.com → CNAME → <your-coolify-server>
This single record covers all preview subdomains forever. No per-PR DNS work.
2. Enable preview deployments on the application. Settings → Preview Deployments → Enable. Coolify registers the GitHub webhook for you if you've connected your GitHub account.
3. Set the preview URL pattern. Default is pr-{number}.{domain}. Fine for most cases.
4. Enable automatic SSL. Let's Encrypt for the preview domains. This matters if your app uses cookies, service workers, or anything secure-context-only. Without it, previews behave differently from production in confusing ways.
5. Set per-application resource limits. If three products each have two previews running at once, that's six containers eating your server. Cap CPU and memory per preview container so a busy week doesn't starve your main applications.
6. Wire the post-deploy notification. Coolify can call a webhook when a deploy completes. I have a small Cloudflare Worker that listens for those events and posts the preview URL as a comment on the PR. Without this piece, the loop technically works but doesn't feel automated, and you stop using it. With it, the URL is one tap away from wherever I'm reading the morning summary.
The whole setup is an evening of work, including waiting on wildcard DNS propagation. The post-deploy worker is the part most people skip and shouldn't.
The two touch points
I said at the top that the loop comes down to two daily touch points. Here's what that looks like.
Linear is where I:
Refine issues the PM flagged as needing my input
Apply the
agentlabel to issues I'm willing to hand to an agentCreate new issues from product ideas that didn't go through the planning pipeline
Read clarifying questions from agents and answer them as comments
Sync with the PMs at noon Vienna
GitHub is where I:
Review PRs (screenshots first, then the Coolify preview if needed, then the diff if anything still feels off)
Review marketing deliverables in
paperclip-deliverablesMerge or close PRs
Occasionally edit an
AGENTS.mdfile when an agent drifts in some new way
Everything else is push notifications. Discord at 06:00 with the CEO summary, more Discord pings as PRs and drafts land through the day, and the noon sync with the PMs. I don't open Coolify daily. I don't open Cloudflare daily. I don't open the Mac mini's terminal. I don't tail logs.
When the system is working, which is most days, I spend something like 45 to 75 minutes on it, in a couple of focused chunks. Not zero, but a lot less than these three products would eat if I were the only one moving them forward.
When it's not working (usually an agent has drifted, an issue was scoped badly, or a milestone was wrong from step 1), I find out through blocker pings or by noticing the morning summary has too many "waiting on you" items. The fix is almost always upstream. Tighten an issue, add a line to an AGENTS.md, sharpen the milestone exit criterion. The loop itself rarely needs fixing. The inputs do.
What I'd do differently if I were starting over
A few things I learned the slow way.
Set up the deliverables repo on day one, not month two. I started without it and had agents emailing me drafts. It was bad. The repo solves a problem I didn't know I had until I'd lived with it.
Set up Playwright screenshots in PRs before Coolify previews. I had Coolify first and added screenshots later. I'd flip that order. Screenshots are cheaper to set up, faster to consume, and cover most cases.
Set up Coolify previews before you trust the engineer agent. Reviewing the engineer's first PRs from the diff was painful and made me distrust the agent more than I should have. Once previews were live, the trust came back, because I was reviewing the feature instead of the code.
Wire the post-deploy notification first, not last. The difference between automated-but-clunky and automated-and-frictionless is the URL landing where I'm already looking.
Keep it to two touch points. Every extra tool that demands daily attention is a tax. Things I tried and rejected: a separate Slack workspace for agent-to-me communication (Discord won), a dashboard pulling Linear + GitHub + Plausible into one view (felt useful, went unused), a daily standup video the agents would record (please don't).
What's not in this series
A few things I skipped, in case you're wondering.
The prompts for each agent role. They drift, they're long, and the framework matters more than the prompt. Give a model the role definition (the AGENTS.md) and the boundary agent label semantics) and it'll do most of the work. The full company is a publishable Paperclip export and I may share an anonymized version as a follow-up.
Model selection per role. The engineer uses something stronger than the marketer, the PM is in between. Specifics change with every model release, so anything I write today is wrong in three months.
Closing
Three products, three Linear projects in the ELB team, twelve agents, one Mac mini, one Hetzner box, one shared deliverables repo, two daily touch points. Three side projects that are moving forward instead of rotting on Github with the last changes 7 years ago.
The trick wasn't the agents. It was the planning that happens before them (part 1), the boundaries they work within (part 2), and the loop that keeps me out of the day-to-day except where it matters (this one). The agents are the engine. The system is what makes the engine useful.
If you build a version of this and it works for you, I'd love to hear what you changed and why. If you build it and it doesn't work, I'd love to hear that more.
Square Bracket eU is a one-person product studio in Vienna. Find me at squarebracket.io.
