Case Study
DavBlu Tasks
An invitation-only issue tracker with typed Issues, Parents and Blockers, where AI agents take the next Ready Issue with one CLI command.
- Period
- – present
- Role
- Founder & sole engineer
Same day
First commit to production
2 days
Full feature set shipped in
24, against a real PostgreSQL
Automated tests
7 ADRs
Architecture decisions recorded

DavBlu Tasks: an issue tracker for AI agents, live in production on the day of its first commit
Problem
I run my work with several AI coding agents, and each one needs to know what to do next. GitLab Issues gave them no Issue types, no Parent and Child links and no Blockers, so I sequenced the work by hand.
Reading issues through GitLab's API and web pages also spent a large share of each agent's context. An agent needed one cheap command that answers: which open Issue is blocked by nothing?
What was built
I built DavBlu Tasks alone, from a glossary and 7 ADRs to production, with Next.js, Drizzle on PostgreSQL 18 and Better Auth. Nobody can sign up: the deployment creates the Owner from its configuration, and everyone else joins through a single-use Invitation link.
Every Issue has a type (task, bug, spec or epic), Labels, Markdown Comments and a full Activity record. Parent and Blocker links never form a cycle: each link write takes a Postgres advisory lock, then checks the graph in the same transaction. One SQL expression derives Blocked and Ready, so the list and the Issue page always agree.
Agents use dtask, a single-file CLI with no dependencies that the app serves itself, so its version always matches the server. It calls a Hono REST API under /api/v1, with bearer tokens, one error shape and keyset cursors that skip no Issue while other agents write. Its output is one line per Issue, sized for an agent's context.
Browsers and agents reach the app through Traefik on my VPS. The app talks to Postgres on a private network with no published port. GitLab CI checks every push and deploys main with Docker Compose.
Claude Code agents wrote the code from my specs, glossary and ADRs. I made the design decisions, reviewed the changes, and gated every deploy on lint, typecheck and 24 tests against a real Postgres.
Outcome
DavBlu Tasks runs at tasks.davblu.dev. I shipped the first commit and the production deploy on 23 September 2026, and the full feature set the next day.
My agents now take work with dtask ready and close it with a comment. The tracker also holds its own Issues: this product's work moved off GitLab Issues into it.
Every push to main deploys only after lint, typecheck and tests pass, and the deploy succeeds only when the health check answers. Production secrets stay encrypted in the repository with dotenvx. The source is private; the product is live.
Shipping Timeline
From zero to production.
01 · Architecture
Glossary and founding ADRs
Invitation-only access, committed encrypted configuration, Markdown storage, a CLI for agents.
02 · Deploy
Live at tasks.davblu.dev
GitLab CI deploys main with Docker Compose behind Traefik, gated by a health check.
03 · Build
Owner sign-in, Invitations, Projects and Issues
No sign-up on any surface; everyone else arrives by a single-use link.
04 · Build
Comments, Activity, Parents and Blockers, Markdown editor
Cycle-free links under an advisory lock; Ready and Blocked derived on every read.
05 · Build
API tokens, REST API v1 and the dtask CLI
A single-file CLI served by the app itself, with compact output for agents.
06 · Support
The tracker takes over its own Issues
Work on DavBlu Tasks moved from GitLab Issues into DavBlu Tasks.
Engineering
How it is built.
Architecture
Stack
- Better Auth
- Hono
- Drizzle ORM
- TypeScript
- React
- Next.js
- Tailwind CSS
- Node.js
- PostgreSQL
- Docker
- GitLab CI/CD
- Traefik
- Encrypted secrets (SOPS, dotenvx)
- Vitest
- Agent skills (Claude Code)
Decisions
- A CLI over an MCP server
- Chose a CLI over an MCP server because it works in every agent harness and shell, and loads no tool schemas into context. Cost: no native tool integration, though an MCP server can wrap the same REST API later.
- PostgreSQL over SQLite
- Chose PostgreSQL over SQLite because several agents write at once through the API. A named volume is no harder to keep than a database file. Cost: a second container to run and back up.
- One advisory lock over serializable retries
- Chose one advisory lock for every Parent and Blocker write over serializable transactions with retries, because two concurrent writes can each pass a cycle check. Cost: link writes take turns, a few milliseconds each.




