Skip to content
> andrew_dryga
Projects
Blitz
Single-handedly owned ~20 high-traffic backends end-to-end (77 Elixir umbrella apps, 70+ Redis/50+ PostgreSQL instances) serving ~25k RPS with peaks to 150k for a 7-figure DAU product.
Firezone
WireGuard-based replacement for legacy VPNs. Re-architected and developed key components of the enterprise product. Led infrastructure as code with Terraform on GCP. Open source, YC W22.
eHealth: National Health Service of Ukraine
Co-designed and built the national platform behind reimbursements, EMR, e-prescriptions, and nationwide APIs for clinics and pharmacies. Led architecture, security, hiring, and hands-on Elixir + DevOps. All development open-sourced under Apache license.
Hammer Corp
Advertising platform for thousands of US automotive dealerships: ingests inventory, syndicates ads to major channels (at peak accountable for 30%+ cars on Facebook Marketplace), measures conversion, and collects leads to a unified interface with 24/7 human first-responder reps answering within 60 seconds.
Ryker
A self-hosted AI teammate for engineering and on-call work. Ask in Slack or GitHub, share an alert, or give it a coding task. Ryker investigates, shows what it found, and keeps working as you follow up.
Emisar
Gives AI agents a reviewed catalog of infrastructure actions over MCP. Policy decides which requests run, need approval or are denied. Typed inputs, checks on the host and searchable run records let an agent keep working within the access you intended.
co:op
A runtime and orchestrator for coding agents. Run Claude, Codex or Gemini in a container, limit the project files and network access it receives, and work through a task queue in a separate fork you can review.
Offloader
Serves repeated product queries from published Parquet snapshots through declared REST endpoints. DuckDB runs on your infrastructure; your data pipeline owns the exports. It fits customer-facing reads that can tolerate the snapshot's age.
Bullpen - Virtual Sales Floor + CRM
When COVID hit, our sales team lost the buzz of the office. We built a platform that brought it back - a CRM with virtual space where reps could collaborate, learn from each other in real time, and keep the same drive. Then we turned it into a standalone product with AI sprinkled around it.
TalkInto - Omnichannel Messaging Platform and CRM
Messaging/voice backbone powering products like Hammer, Bullpen, and Text2Buy: SMS, voice, various chat integrations, and web chat with clean agent UI and APIs. Features included local numbers, call recording, and routing.
Contractbook
Built the self-service billing system, B2B API and marketing pipeline that kicked off the business growth.
Financial P2P Marketplace
Architecture and implementation for an institutional P2P lending marketplace for one of Europe's largest lenders ($9B portfolio).
Mbill - P2P Transfers
P2P transfer service for individuals and small-to-medium online merchants. Create a page for your card and share a link to receive payments. Includes customer cabinet, payment button constructor, and transaction reports.
Mastercard MoneySend
Front-end application to receive P2P transfers sent via recipient phone number. Country-wide rollout of phone-number-based transfers.
Forza - PayDay Loan Websites
Front-end, SMS gateway, decision engine, and marketing tools for an online lending originator operating in Moldova, Bosnia, and North Macedonia.
Best Wallet (ex. MBank)
eWallet cloud for worldwide money transfers. For B2C: pay for 2,700+ services across CIS, send money to phone numbers, cash out via partnered banks or cards. For B2B: free SaaS white-label eWallets for banks with simple integration.
IPSP.com - Payment Pages
Responsive landing and payment pages for an Internet Payment Service Provider. Improved conversion on payment flows via lighter UI.
ECommPay - Mobile App
iOS and Android business application for partners to manage payment platform on the go.
Autopayment
Automatically pays for bills based on two types of rules: by threshold of supplier balance (e.g., mobile top-up) or on a periodic basis.
Mobile Cashier
Turns Android devices into payment terminals for deposits and top-ups across numerous service providers, from cellular carriers to credit card loan repayments.
OnlyTTY
Share a shell or a single command through a link and control it from a browser or phone. Terminal traffic is end-to-end encrypted, and the relay never sees the session keys.
Sage - Sagas Pattern in Elixir
Dependency-free implementation of the Sagas pattern for distributed transactions with explicit compensation. Guarantees that either all transactions complete successfully, or compensating transactions amend partial execution.
LoggerJSON
Structured JSON logging for Elixir with first-class formatters for Google Cloud Logging, Datadog, and Elastic (ECS). Drop-in :logger formatter/handler with runtime config helpers.
Confex
Runtime configuration from environment variables with type casting and adapters (:system, :system_file). 12-factor friendly configuration for Elixir applications.
Elixir Bench
Continuous benchmarking platform for the Elixir ecosystem. Automatically runs performance benchmarks on each commit to detect regressions and track language performance improvements over time. Won Spawnfest 2017 and later accepted into Google Summer of Code.
Annon API Gateway
Configurable API gateway acting as a reverse proxy with a plugin system (ACL, Auth, Validation, CORS, Idempotency), request/response storage, metrics, management UI, and auth provider. Reduces boilerplate across services.
Ecto Mnesia Adapter
Ecto adapter for OTP's built-in Mnesia database that works in the same memory space as the application, providing extremely low latency without deploying a separate database.
Gandalf - Decision Engine
Open-source decision engine SaaS for rule tables, champion/challenger split testing, revision history, decision analytics, and debugging tools.
Man - Template Rendering Engine
Stores iex, mustache, or markdown templates and renders them with localization to HTML or PDF via REST JSON API. Includes an easy-to-use management UI. Free one-click deployment to Heroku.
Vagrant Box OS X
macOS Vagrant boxes for VirtualBox. Run UX tests or build iOS/Mac applications on any machine with a few CLI commands. Used by many teams worldwide, including Boxen. Got a $50k S3 bill in month one, was sponsored by Cloudflare for a while, and eventually taken down over Apple licensing.
Parasport - Foundation Portal
Medium-sized web portal for a foundation supporting Paralympic sport, physical rehabilitation, and social adaptation. Built on October CMS.
OneDayOfMine
Storytelling social network that helps see other people's lives through their eyes. Capture moments through the day and share them with descriptions - from special forces in Belarus to a family visit to a film museum in South Korea.
L15 - Night Club x Coworking
Experimental mix of coworking space and a night club ('clubworking') in Kyiv. Turned the office into a best-in-class night club and ran terrace events with world-class DJs every weekend for an entire summer.
Happy Customer
Outsource project to motivate small and medium-sized businesses to provide better customer service via public feedback and simple tracking.
truBrain 1.0
An early-stage product that needed help. I took some swings at UX and performance for free because I wanted to see them make it.
Blog
Your data warehouse is the wrong place to serve a dashboard
Customer-facing dashboards ask the same few questions all day, on data that's fine a few minutes stale - but every page load runs a live query against a warehouse that bills per query and was never built to sit behind a live screen. Here's the serving layer I built to move those reads onto cheap snapshots on my own infra.
Treating physical servers like cloud VMs
Modern BMCs turned bare metal into something you can terraform apply: declare a node in git, reimage it over the network, verify it against a receipt. How our cloud-to-on-prem migration kept the cloud workflow - and why that's exactly what LLM agents needed.
Your AI is brilliant. And blind
AI agents got good at writing code - and they're still flying blind on your infrastructure, guessing at problems they can't see. The obvious fix is to hand them SSH and let them run wild. Here's why I didn't, and what I built instead.
One brain, two agents: an OS for autonomous coding agents
AI agents lose context, stop early, and skip work. The fix is not a smarter prompt. It is a small, file-based operating system: a queue they cannot abandon, hooks they cannot ignore, and one shared source of truth.
Running an AI coding agent you can't trust
You can't trust an AI coding agent, and you shouldn't have to. Put it in a sandbox where the worst case is boring: one repo damaged, no secrets exposed, and a VM you can rebuild in a minute.
Design APIs with CDN in mind
Most APIs are designed for developer convenience, not cacheability. A few structural changes can make CDN do the heavy lifting for you.
The Real 10x Engineer
The real multiplier in software isn’t writing more code. It’s judgment: choosing the right problems, avoiding unnecessary systems, and reducing the maintenance burden that slows teams down.
Introducing Sage - a Sagas pattern implementation in Elixir
Distributed transactions are hard and expensive, if you wonder how to pragmatically handle them in a mid-size project - this article is for you.
Run stale tests on file change in Elixir
Mix is an awesome tool but most Elixir beginners are not aware of all its features. mix test --stale is one of them and can make your workflow much better.
Runtime configuration, migrations and deployment for Elixir applications
Shortly after moving from PHP to Elixir I've faced a common issue, the way we deploy applications is totally different from the one I'm used to.
National Health Service, on Elixir and Kubernetes
A look at building Ukraine’s national-scale eHealth platform with Elixir, Kubernetes, and pragmatic architecture for reliability and scale.
Bringing blockchain properties to centralized government databases
Making it cryptographically impossible to alter records in a database even with full system access.
Alternative approach for sensitive file uploads
Using signed URLs for secure file uploads directly to cloud storage, bypassing your application servers entirely.
Designing a P2P Lending platform with Elixir in mind
With this post, I want to share with you the design process on one of our latest projects - a P2P marketplace that was intended to be used by hundreds of thousands of users.
$ cat ./blog/os-for-coding-agents

One brain, two agents: an OS for autonomous coding agents.

I ask something simple of my AI coding agents: take a task, work on it end to end, ship the result to my taste, and do it autonomously. I want to hand them a list of 100 tasks and have them finish every single one without losing important context, stopping early, or skipping work. But that’s exactly what they don’t do.

I initially tried to fix this with a better prompt. A stricter CLAUDE.md. A sterner “do not stop until you are finished.” A queue of /goals. I tried all of it, and none of it worked reliably enough - because CLAUDE.md, AGENTS.md, and memory are just part of the context and do not enforce the behaviour.

There are three ways it goes wrong:

  • Losing context is really a compaction problem. When the window fills up, the conversation gets summarized away - everything except the project-root instruction file (which gets re-injected from disk) and memory. So your durable rules have to live in that root file, and your working state has to live on the filesystem, not in the chat.
  • Stopping early and skipping work isn’t something you can fix by writing “don’t stop” in a prompt. That’s advice. Every model will interrupt itself to wait on a human decision instead of picking up the next task, every one of them has a limit on how long it can run before it stops and the Claude API going down 20 times a day is also worth mentioning here. It needs a hook that mechanically rejects premature stop attempts. Ideally, it should also retry the turn.
  • Learning your taste can’t lean on the model “remembering.” A rule no machine enforces just gets violated again. It has to graduate into a check.

The severity of these problems depends on the model, its version, and its effort level. For example, Sol can take on long-horizon tasks or a list of them, but compactions and context rot can steer it into heavy over-engineering. Opus will stop early when I give it a long list of things to do, no matter how I prompt it. Fable is simply too limited on most subscriptions to be practical for routing coding tasks. Things also get better or worse over time as harnesses and models change. The system around them has to remain stable regardless of all of that.

So I built an “operating system”. It’s boring, pragmatic, file-based, and almost entirely made of markdown and three shell hooks. It refuses to stop while there’s work left, it learns my style and turns my corrections into checks the model can’t slip past, and, since I use multiple subscriptions, it runs on both Claude Code and Codex from a single source of truth.

This assumes you can already run an agent without it wrecking your laptop. If you can’t yet, start with the sandbox: running an AI coding agent you can’t trust. This is the other half - what makes that safe box actually finish the job.

Everything below is built on one line: the stuff that should happen goes in markdown, and the stuff that must happen goes in hooks.

Beating amnesia: a boot protocol and a memory on disk

Context loss comes first because it’s the failure everybody misreads as the model being dumb. It isn’t. When the context window fills up, the harness summarizes the conversation and carries on. State is lost during this process, and harnesses try to work around that by injecting the root instruction file - only the root one - and memory back into the context.

So treat the root file as a survival kernel - the core that has to outlive any compaction. The first thing my AGENTS.md defines is a BOOT protocol: the exact order to re-read whenever the context is fresh or freshly compacted.

# BOOT - read these, in order, before touching code:
1. portal/AGENTS.md, ..            (project specific instructions for monorepos)
2. .agent/kb/rules/                (the taste knowledge base)
3. .agent/tasks/README.md          (the queue's own instructions)
3. ls .agent/tasks/todo            (the work queue, in order of creation)
4. .agent/tasks/in_progress/*.md   (the task in flight, if any)

In my setup, every project gets an .agent/ folder for durable working memory that agents share on disk and humans can read:

Path What it is
tasks/ the work queue - one folder per task, and its state is which directory it sits in
tasks/<status>/<id>/state.md the resume snapshot: the plan, current position, next concrete action, and traps
tasks/<status>/<id>/log.md the agent’s working journal: what it did and why, so reasoning survives a compaction
tasks/<status>/<id>/decision.md the human call this task is parked on, with options and a recommendation
kb/rules/ the taste knowledge base

The <status> directories are 00_todo/, 10_in_progress/, 50_blocked/, 99_done/ and xx_backlog/. Model moves the task folders between those directories, making the queue and current work state durable.

For all those task files log.md is the one people skip, and it’s the one that pays off the most. The task queue tells a recovering agent what’s left but the log tells it why the last three tasks were done the way they were - the decision it would otherwise re-litigate or quietly contradict. Reasoning is the first thing compaction throws away and the most expensive to rebuild, so the agent is instructed to write important bits of it down as it goes. The queue’s own README speaks directly to the agent, and BOOT reads it into every fresh or freshly compacted context:

# .agent/tasks/README.md

## log.md - the working journal
Append-only history of what you did and **why** - the decisions, dead ends, and
surprises a reviewer (or the next agent) needs. Add to the bottom; never rewrite
it. This is the durable "why"; state.md is just "where am I now".

## state.md - the resume snapshot
The single most useful file for an unattended loop: each iteration is a fresh
agent with no memory of the last, so this is how a task survives an interruption.
**Overwrite the whole file** at every checkpoint (before a commit, before you
pause) - it's a snapshot, not a log. Keep it to a few lines, and on resume read
it first.

There’s a second lever for the same problem: keep exploration out of the main context entirely by instructing your agent to use subagents for exploratory and mechanical work. Subagents read files in their own window and hand back only the conclusion - one of them burns six thousand tokens reading code, and your main thread gets a four-hundred-token answer. The main context doesn’t grow so quickly and compactions happen less often.

That’s the whole antidote to amnesia. The rules and intent survive because they’re in the re-injected root file. The filesystem becomes the memory. Once that clicks, the rest of the OS kind of designs itself.

The contract: a queue with nowhere to hide

The task queue solves another problem: the model’s natural tendency to skip work. If you want hours of unattended work, the task list can’t live in the model’s head - or in the chat scrollback, which is the same thing with extra steps. It lives in .agent/tasks/, where a task is a folder and its state is which directory it sits in.

Every entry point gives the agent the same instruction:

Take the next folder in 00_todo/, claim it by moving into 10_in_progress/ → do it → run the gate → commit → write the state snapshot → close it by moving to 99_done/. Blocked? Move to 50_blocked/ plus add a decision.md describing the decisions you need to get from a human, provide options for the decision, argue about their tradeoffs and give your recommendation. Spot unrelated work? Small and ready goes straight on the queue, big or half-formed goes to the backlog - either way, stay on task. Never end a turn with a task still in 10_in_progress/, and never stop while 00_todo/ still has tasks.

That backlog line matters: it prevents an agent from wandering off and never coming back when it spots something three files over, because a compaction wiped out what it set out to do initially. “Capture it, don’t do it” gives the distraction a home, so the agent keeps the idea and stays on the task. It also saves you the worst kind of review: an unrelated change buried deep in the diff.

Hooks: the rules that can’t be argued with

Everything so far is still advice - context the model reads, weighs, and sometimes loses. I use only three hooks to enforce the parts that cannot be optional. Hooks are shell scripts the harness runs at fixed points in the agent’s life; the harness controls them, not the model, so they can’t be silently ignored.

The most practical one is the Stop hook. It fires the moment the agent decides it’s finished and lets the harness force it to continue:

#!/bin/bash
# .agent/skills/sweep/queue-guard.sh - refuse to stop while work remains
if [ -z "${CLAUDE_PROJECT_DIR:-}" ] || [ -z "${CLAUDE_CODE_SESSION_ID:-}" ]; then
  echo "Claude Code project or session ID is unavailable; cannot inspect the queue." >&2
  exit 2
fi

queue="$CLAUDE_PROJECT_DIR/.agent/tasks"
armed="$queue/.sweep-armed/$CLAUDE_CODE_SESSION_ID"

# The hook is always installed, but only an armed /sweep session is gated.
[ -d "$armed" ] || exit 0

left=0
for state in 00_todo 10_in_progress; do
  [ -d "$queue/$state" ] || continue
  left=$((left + $(find "$queue/$state" -mindepth 1 -maxdepth 1 -type d | wc -l)))
done

if [ "$left" -gt 0 ]; then
  echo "$left actionable task(s) remain in 00_todo or 10_in_progress." \
       "Finish them, or move genuinely blocked tasks to 50_blocked." >&2
  exit 2
fi

# An empty actionable queue completes the sweep and disarms this session.
if ! rmdir "$armed"; then
  echo "Queue is empty, but the sweep guard could not disarm: $armed" >&2
  exit 2
fi

Exit code 2 blocks the stop, and that stderr message goes straight back to the agent. When it says, “I’ve completed the most important items, wrapping up,” the hook answers: No. Forty-one folders are still sitting in 00_todo; back to work.

I do not leave that guard armed for every interaction. Otherwise a quick one-off question could force the agent through the entire queue. Instead, the Stop hook is installed once in .claude/settings.json and returns immediately unless the current session has been armed:

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash",
            "args": ["${CLAUDE_PROJECT_DIR}/.agent/skills/sweep/queue-guard.sh"]
          }
        ]
      }
    ]
  }
}

The /sweep skill arms the guard as it loads:

# .agent/skills/sweep/SKILL.md
---
name: sweep
description: Drain this repo's .agent/tasks/ queue autonomously, one commit per task.
disable-model-invocation: true
allowed-tools: Read, Grep, Glob, Bash, Write, Edit
---
!`mkdir -p "${CLAUDE_PROJECT_DIR}/.agent/tasks/.sweep-armed/${CLAUDE_SESSION_ID}" && printf 'Sweep guard armed for this session.\n'`

Drain `.agent/tasks/` according to the root queue contract. Move genuinely
blocked work to `50_blocked/`; do not leave it in an actionable state.

If the human explicitly cancels the sweep, remove this session's marker before
stopping. Otherwise the Stop guard removes it when the actionable queue is empty.

The marker includes Claude Code’s session ID. A normal interactive session has no marker and the permanent hook is a no-op. Resuming the same sweep session keeps the guard armed; opening a different session does not inherit it. The script removes its own marker as soon as 00_todo/ and 10_in_progress/ are empty. 50_blocked/ is parked on a human, and 99_done/ has shipped. A human interrupt still wins.

Claude Code marks a retry forced by a Stop hook with stop_hook_active. This guard uses the queue itself as the terminating condition and keeps blocking while actionable work remains. Claude Code still ends the turn after eight consecutive Stop blocks, so this hook applies pressure rather than creating an infinite process; the foreman later in this article handles restarts across sessions.

The second hook is a commit gate on PreToolUse: intercept git commit, run the fast formatters over the staged files, and refuse the commit if they come back dirty.

#!/bin/bash
# .claude/hooks/commit-gate.sh - format staged files, block the commit if they're dirty
input=$(cat)                                                    # the tool call arrives on stdin
echo "$input" | grep -q '"command"[^}]*git commit' || exit 0    # ignore every other command
staged=$(git diff --cached --name-only --diff-filter=ACM) || exit 0

go_files=$(echo "$staged" | grep '\.go$' || true)
if [ -n "$go_files" ] && command -v gofmt >/dev/null 2>&1; then # no gofmt? fail open
  bad=$(gofmt -l $go_files 2>/dev/null || true)
  if [ -n "$bad" ]; then
    echo "commit blocked - these need gofmt:" >&2; echo "$bad" >&2
    exit 2
  fi
fi
exit 0

Scope it to staged files so an unrelated mess somewhere else can’t block you, and make it fail open - that command -v gofmt check is the whole trick, so a missing toolchain never wedges the whole run.

One honest caveat: harness hooks are the single genuinely tool-specific layer here. Claude Code has them; Codex doesn’t have an equivalent yet. That’s what the third hook is for - a plain git pre-commit gate, tracked in the repo with core.hooksPath pointed at it, so the format check runs for every committer (Codex, Gemini, a bare git commit) and rides along on a fresh clone. Only the Stop guard stays Claude-side; everything else - rules, queue, log, skills - is shared. More on how that sharing works in a second.

Teaching it your taste

Here’s how to make the agent write code like you instead of like the average of all of GitHub. Treat every correction you give it as more than a one-off fix - record it as a rule in the same change:

  1. Fix the flagged instance.
  2. Record it - a one-line entry in the project’s rule index, plus a .agent/kb/rules/<slug>.md with a worked ✅-good / ❌-bad example when the rule needs one.
  3. Sweep the codebase for other instances of the old shape - one correction usually has siblings.
  4. Graduate it - if it’s mechanically checkable, add a real check: a custom lint, an AST rule, a hook. Now it can’t regress.

The Sweep step is the one most people skip, and skipping it bites you later: the model wanders back into the old, off-taste code still in the repo, decides that style must be fine, and copies it again. I learned this the hard way: while the agent is deep in a big file, it does not keep every rule in context.

The Graduate step is the endpoint: the goal of teaching your taste was never to make the model remember - it was to make the violation impossible to commit. In my setup, a dozen rules have graduated into real static-analysis checks (Credo AST rules on the Elixir side); the rest live as worked examples the agent reads on demand from .agent/kb/rules/. Memory drifts. A static-analysis rule doesn’t.

One brain, two agents

This is the part I’ll happily bore you about at parties.

The whole design follows one rule: all durable knowledge and state live in tool-neutral files at fixed paths. Each tool’s own config is thin wiring that points at them, never a copy.

The spine is one file - AGENTS.md - the canonical, tool-neutral manual, at the repo root and in each project. CLAUDE.md is just a symlink to it. Claude reads through the symlink; Codex reads AGENTS.md natively. One source, zero duplication.

myapp/
  AGENTS.md                            # the creed, the BOOT protocol, the contract
  CLAUDE.md -> AGENTS.md               # symlink
  GEMINI.md -> AGENTS.md               # symlink
  .agent/                              # shared working memory (every tool reads/writes it)
  .agent/skills/                       # the skills, in one tool-neutral place
  .claude/settings.json                # Claude-only hook wiring
  .claude/skills -> ../.agent/skills   # symlink
  .codex/skills  -> ../.agent/skills   # symlink
  .gemini/skills -> ../.agent/skills   # symlink
  control_plane/AGENTS.md              # Elixir specifics
  control_plane/CLAUDE.md -> AGENTS.md # symlink
  runner/AGENTS.md                     # Go specifics
  runner/CLAUDE.md -> AGENTS.md        # symlink

Because everything lives in those shared files, nothing is duplicated per tool - one rulebook, one queue, one set of skills. While I was building this, a Codex agent in the same repo even picked the whole thing up on its own and started working the queue right alongside Claude.

The only thing you have to get right is what gets committed. Keep the knowledge - kb/, the skills, the presets - in git, and git-ignore the working state. The queue and its logs are local, and two agents committing the same queue is a merge-conflict machine. The one exception is the queue’s own README: it is a BOOT entry point, so it has to survive a fresh clone.

**/.agent/*
!**/.agent/kb/               # the taste KB is shared
!**/.agent/skills/
!.agent/project.yaml
!**/.agent/tasks/            # the queue is local state...
**/.agent/tasks/*
!**/.agent/tasks/README.md   # ...but its layout doc is a BOOT entry point

The foreman is a while loop

The Stop hook keeps a single session honest. It does nothing for the session that dies - the laptop sleeps, the API hiccups, the context degrades into mush after the fourth compaction. The industry’s answer to this is an orchestration framework. The actual answer is twenty lines of bash:

#!/bin/bash
# bin/foreman - run disposable agents until the queue is provably done
set -euo pipefail

work_remains() {
  find .agent/tasks/00_todo .agent/tasks/10_in_progress \
       -mindepth 1 -maxdepth 1 -type d 2>/dev/null | grep -q .
}

while true; do
  while work_remains; do
    bin/agent -p "Boot, then claim and work the next task per the protocol." </dev/null || sleep 10
  done

  bin/agent -p "Signoff: for every folder in .agent/tasks/99_done/, verify its acceptance
    criteria are met, its gate passes, and a commit implementing it exists in the git log.
    Reopen any that fail - move it back to 00_todo/ and say what's missing in its log.md.
    Do not fix anything yourself." </dev/null || continue

  work_remains || break
done
echo "queue verified done"

bin/agent is the sandboxed launcher from the first article, called headless. The trick is what the loop doesn’t do: it never resumes a session. Every pass is a brand-new agent that boots, reads the queue, works a few items, commits, and dies. Don’t fight context rot - make sessions disposable. A crash costs you the one item in flight; the next pass finds it still sitting in 10_in_progress/ and picks it up from its state.md. (For that to stay fast, the environment has to be durable even though the container isn’t - toolchain in the image, deps cached in a volume, exactly as the first article set up.)

The signoff pass at the end is where the quality comes from: a fresh agent that never saw the work get done re-checks every finished task against the real git log and reopens anything that doesn’t hold up. There’s a round cap on that, and it scales with the batch - when the worker and the reviewer keep ping-ponging on the same item past it, the task gets blocked for a human instead of being reported as done. That’s your cue to re-read it: it’s underspecified, and that one’s on you.

What a day looks like

In the morning I write the queue - I point Claude at the open issues, throw ideas at it, and have it draft everything down into .agent/tasks/. Deciding what to build and what “done” means is the real work; once that’s settled I start the foreman and close the tab. Around lunch I run coop tasks decisions and unblock a couple of items with a sentence each. In the evening I review the branch like a contractor’s PR. The agents worked eight hours and interrupted me twice, both times for calls that were genuinely mine.

Final thoughts

None of this makes the model smarter - it just stops depending on the model being smart, which is the only thing that’s held up for me. There’s no framework here either: an instruction file, a folder of markdown, three shell hooks, and a while loop. I packaged it all as a single binary, coop. You can run coop loop instead of copying scripts around, but it’s the same boring markdown underneath.

It’s simple - that’s exactly why it works.

Related Projects

co:op

A runtime and orchestrator for coding agents. Run Claude, Codex or Gemini in a container, limit the project files and network access it receives, and work through a task queue in a separate fork you can review.

View project →
Projects
Blitz
Single-handedly owned ~20 high-traffic backends end-to-end (77 Elixir umbrella apps, 70+ Redis/50+ PostgreSQL instances) serving ~25k RPS with peaks to 150k for a 7-figure DAU product.
Firezone
WireGuard-based replacement for legacy VPNs. Re-architected and developed key components of the enterprise product. Led infrastructure as code with Terraform on GCP. Open source, YC W22.
eHealth: National Health Service of Ukraine
Co-designed and built the national platform behind reimbursements, EMR, e-prescriptions, and nationwide APIs for clinics and pharmacies. Led architecture, security, hiring, and hands-on Elixir + DevOps. All development open-sourced under Apache license.
Hammer Corp
Advertising platform for thousands of US automotive dealerships: ingests inventory, syndicates ads to major channels (at peak accountable for 30%+ cars on Facebook Marketplace), measures conversion, and collects leads to a unified interface with 24/7 human first-responder reps answering within 60 seconds.
Ryker
A self-hosted AI teammate for engineering and on-call work. Ask in Slack or GitHub, share an alert, or give it a coding task. Ryker investigates, shows what it found, and keeps working as you follow up.
Emisar
Gives AI agents a reviewed catalog of infrastructure actions over MCP. Policy decides which requests run, need approval or are denied. Typed inputs, checks on the host and searchable run records let an agent keep working within the access you intended.
co:op
A runtime and orchestrator for coding agents. Run Claude, Codex or Gemini in a container, limit the project files and network access it receives, and work through a task queue in a separate fork you can review.
Offloader
Serves repeated product queries from published Parquet snapshots through declared REST endpoints. DuckDB runs on your infrastructure; your data pipeline owns the exports. It fits customer-facing reads that can tolerate the snapshot's age.
Bullpen - Virtual Sales Floor + CRM
When COVID hit, our sales team lost the buzz of the office. We built a platform that brought it back - a CRM with virtual space where reps could collaborate, learn from each other in real time, and keep the same drive. Then we turned it into a standalone product with AI sprinkled around it.
TalkInto - Omnichannel Messaging Platform and CRM
Messaging/voice backbone powering products like Hammer, Bullpen, and Text2Buy: SMS, voice, various chat integrations, and web chat with clean agent UI and APIs. Features included local numbers, call recording, and routing.
Contractbook
Built the self-service billing system, B2B API and marketing pipeline that kicked off the business growth.
Financial P2P Marketplace
Architecture and implementation for an institutional P2P lending marketplace for one of Europe's largest lenders ($9B portfolio).
Mbill - P2P Transfers
P2P transfer service for individuals and small-to-medium online merchants. Create a page for your card and share a link to receive payments. Includes customer cabinet, payment button constructor, and transaction reports.
Mastercard MoneySend
Front-end application to receive P2P transfers sent via recipient phone number. Country-wide rollout of phone-number-based transfers.
Forza - PayDay Loan Websites
Front-end, SMS gateway, decision engine, and marketing tools for an online lending originator operating in Moldova, Bosnia, and North Macedonia.
Best Wallet (ex. MBank)
eWallet cloud for worldwide money transfers. For B2C: pay for 2,700+ services across CIS, send money to phone numbers, cash out via partnered banks or cards. For B2B: free SaaS white-label eWallets for banks with simple integration.
IPSP.com - Payment Pages
Responsive landing and payment pages for an Internet Payment Service Provider. Improved conversion on payment flows via lighter UI.
ECommPay - Mobile App
iOS and Android business application for partners to manage payment platform on the go.
Autopayment
Automatically pays for bills based on two types of rules: by threshold of supplier balance (e.g., mobile top-up) or on a periodic basis.
Mobile Cashier
Turns Android devices into payment terminals for deposits and top-ups across numerous service providers, from cellular carriers to credit card loan repayments.
OnlyTTY
Share a shell or a single command through a link and control it from a browser or phone. Terminal traffic is end-to-end encrypted, and the relay never sees the session keys.
Sage - Sagas Pattern in Elixir
Dependency-free implementation of the Sagas pattern for distributed transactions with explicit compensation. Guarantees that either all transactions complete successfully, or compensating transactions amend partial execution.
LoggerJSON
Structured JSON logging for Elixir with first-class formatters for Google Cloud Logging, Datadog, and Elastic (ECS). Drop-in :logger formatter/handler with runtime config helpers.
Confex
Runtime configuration from environment variables with type casting and adapters (:system, :system_file). 12-factor friendly configuration for Elixir applications.
Elixir Bench
Continuous benchmarking platform for the Elixir ecosystem. Automatically runs performance benchmarks on each commit to detect regressions and track language performance improvements over time. Won Spawnfest 2017 and later accepted into Google Summer of Code.
Annon API Gateway
Configurable API gateway acting as a reverse proxy with a plugin system (ACL, Auth, Validation, CORS, Idempotency), request/response storage, metrics, management UI, and auth provider. Reduces boilerplate across services.
Ecto Mnesia Adapter
Ecto adapter for OTP's built-in Mnesia database that works in the same memory space as the application, providing extremely low latency without deploying a separate database.
Gandalf - Decision Engine
Open-source decision engine SaaS for rule tables, champion/challenger split testing, revision history, decision analytics, and debugging tools.
Man - Template Rendering Engine
Stores iex, mustache, or markdown templates and renders them with localization to HTML or PDF via REST JSON API. Includes an easy-to-use management UI. Free one-click deployment to Heroku.
Vagrant Box OS X
macOS Vagrant boxes for VirtualBox. Run UX tests or build iOS/Mac applications on any machine with a few CLI commands. Used by many teams worldwide, including Boxen. Got a $50k S3 bill in month one, was sponsored by Cloudflare for a while, and eventually taken down over Apple licensing.
Parasport - Foundation Portal
Medium-sized web portal for a foundation supporting Paralympic sport, physical rehabilitation, and social adaptation. Built on October CMS.
OneDayOfMine
Storytelling social network that helps see other people's lives through their eyes. Capture moments through the day and share them with descriptions - from special forces in Belarus to a family visit to a film museum in South Korea.
L15 - Night Club x Coworking
Experimental mix of coworking space and a night club ('clubworking') in Kyiv. Turned the office into a best-in-class night club and ran terrace events with world-class DJs every weekend for an entire summer.
Happy Customer
Outsource project to motivate small and medium-sized businesses to provide better customer service via public feedback and simple tracking.
truBrain 1.0
An early-stage product that needed help. I took some swings at UX and performance for free because I wanted to see them make it.
Blog
Your data warehouse is the wrong place to serve a dashboard
Customer-facing dashboards ask the same few questions all day, on data that's fine a few minutes stale - but every page load runs a live query against a warehouse that bills per query and was never built to sit behind a live screen. Here's the serving layer I built to move those reads onto cheap snapshots on my own infra.
Treating physical servers like cloud VMs
Modern BMCs turned bare metal into something you can terraform apply: declare a node in git, reimage it over the network, verify it against a receipt. How our cloud-to-on-prem migration kept the cloud workflow - and why that's exactly what LLM agents needed.
Your AI is brilliant. And blind
AI agents got good at writing code - and they're still flying blind on your infrastructure, guessing at problems they can't see. The obvious fix is to hand them SSH and let them run wild. Here's why I didn't, and what I built instead.
One brain, two agents: an OS for autonomous coding agents
AI agents lose context, stop early, and skip work. The fix is not a smarter prompt. It is a small, file-based operating system: a queue they cannot abandon, hooks they cannot ignore, and one shared source of truth.
Running an AI coding agent you can't trust
You can't trust an AI coding agent, and you shouldn't have to. Put it in a sandbox where the worst case is boring: one repo damaged, no secrets exposed, and a VM you can rebuild in a minute.
Design APIs with CDN in mind
Most APIs are designed for developer convenience, not cacheability. A few structural changes can make CDN do the heavy lifting for you.
The Real 10x Engineer
The real multiplier in software isn’t writing more code. It’s judgment: choosing the right problems, avoiding unnecessary systems, and reducing the maintenance burden that slows teams down.
Introducing Sage - a Sagas pattern implementation in Elixir
Distributed transactions are hard and expensive, if you wonder how to pragmatically handle them in a mid-size project - this article is for you.
Run stale tests on file change in Elixir
Mix is an awesome tool but most Elixir beginners are not aware of all its features. mix test --stale is one of them and can make your workflow much better.
Runtime configuration, migrations and deployment for Elixir applications
Shortly after moving from PHP to Elixir I've faced a common issue, the way we deploy applications is totally different from the one I'm used to.
National Health Service, on Elixir and Kubernetes
A look at building Ukraine’s national-scale eHealth platform with Elixir, Kubernetes, and pragmatic architecture for reliability and scale.
Bringing blockchain properties to centralized government databases
Making it cryptographically impossible to alter records in a database even with full system access.
Alternative approach for sensitive file uploads
Using signed URLs for secure file uploads directly to cloud storage, bypassing your application servers entirely.
Designing a P2P Lending platform with Elixir in mind
With this post, I want to share with you the design process on one of our latest projects - a P2P marketplace that was intended to be used by hundreds of thousands of users.