donavyn haley

Software engineer in Portland. Co-founder and CTO of FITREPS, where I've spent three years building a creator platform on AWS. Currently at Capital One through TEKsystems.

I tend to build the whole thing: the services, the infrastructure they run on, the tests that gate them, and whatever tooling was missing.

fr creator dashboard
img/fr-creator-dashboard
Creator dashboard.
One package, no drift

Route definitions, Zod schemas, entities and repositories live in one package that the backend, the web app and the CDK infrastructure all read from. An endpoint gets defined once and the types follow it everywhere. That's what kept 34 services from drifting apart.

Local dev is the production shape

An Express shim wraps the exact Lambda handlers and route definitions that ship to AWS, with Docker Postgres standing in for Aurora and MinIO for S3. There is no separate local implementation to drift out of sync.

The revenue surface, end to end

Stripe subscriptions and creator payouts, the webhooks that reconcile them, and a FitCredits ledger for everything a subscription doesn't cover.

Around the edges

The admin app behind the closed alpha, the subscription-funnel analytics, comment moderation with ban write-enforcement, and a nightly sweep that marks missed days across every in-progress training program.

The test platform

499 specs across 48 subjects, 1,700 individually tagged assertions. A suite that size stays useful only if it's governed like a product:

A DSL that can't rot

Specs are written in a Given/When/Then wrapper over Playwright, and every scenario declares the one suite it belongs to. "Which tests block a merge" is a property of the code, not tribal knowledge.

A written rule for what gates a merge

Three tiers, one decision rule: does the rest of the suite depend on it — smoke. Would breakage cost revenue, reputation, or regulation — acceptance. Everything else — regression. Recategorizing all 499 files against that rule was its own PR.

Sharded CI on self-hosted runners

Per merge: a smoke pass, then regression sharded eight ways, each shard signing in with its own Cognito users so concurrent shards never collide. The full matrix runs nightly. Moving the gate to self-hosted runners cut spend without slowing the loop.

One suite, two backends

The same specs run against the deployed dev backend or the dockerised local stack. A health-check project gates every run, and global setup provisions — and sweeps — its own test users in Cognito and Aurora.

fr playwright
img/fr-playwright
A smoke pass of the suite, run locally against the dockerised stack.
fr training builder
img/fr-training-builder
Training program builder — multi-step product engineering over a complex domain.
mc runs
img/mc-runs
Starting a run: coding, PR review, or fix findings.

The pipeline

Linked-intent planning

A plan breaks into tasks that stay linked to the requirement that produced them, so a coder working one slice can see why it exists.

Parallel coders, isolated worktrees

Every coder gets its own git worktree, so several can work the same repo at once without stepping on each other.

Specs on disk, not in context

Requirements are EARS statements with semantic IDs. The spec tree carries the design across a run, not any one agent's context window.

The reviewer looks at the app, not the diff

It boots the application and drives Chrome over the DevTools Protocol, capturing the same routes at three breakpoints every round.

A critic before, a reviewer after

A planning critic reviews the plan before anyone writes code. An integration reviewer checks the merged result, because per-task reviewers structurally cannot see the seams between tasks.

TDD mode

Writes the failing test first and treats going green as the gate.

mc pipeline
img/mc-pipeline
A cross-lab PR review: six gates, every reviewer timed and costed — $25.94 and 35.9M tokens, accounted for.

Running it

One engine, two frontends

A CLI and a desktop cockpit share a backend, so a run started in the terminal is still watchable in the UI.

Provider-neutral routing

Two vendor CLIs behind one interface, failing over on typed session, quota and rate-limit errors instead of string-matching an exit code.

Deny-by-default sandboxing

Delegated to the providers' own hardened sandboxes. I didn't want harness code pretending to be a security boundary.

Replay

Every stage hand-off is written to disk as JSON, so a run can be replayed and a resumed run starts from the longest unchanged prefix instead of the beginning.

Eight role cards

Planner, developer, reviewer, tester, curator, finalizer, pr-reviewer, improver — each a prompt contract rather than hardcoded behavior.

The loop closes on itself

Friction from every run — bounced reviews, failed gates, retried tool calls — feeds an improver that proposes changes to the harness and to its own role prompts.

mc roles
img/mc-roles
Role cards.
mc usage
img/mc-usage
Two providers, one meter.

One loop, not three tools

Its knowledge lives in a vault that renders as a navigable 3D graph, and work comes in from Grimoire, further down this page. A daily brief reads what's happening across the ecosystem and marks each item adopt, watch or ignore against the principles the project already committed to.

I wanted something that told me how it failed, not just what it produced.

img/mc-vault-graph
The vault as a 3D graph — orbit it, click a node, and the note opens.
What was actually hard

Not prompting. It was context-window economics, meaning what goes in, when to summarize, and what survives across sessions. Tool-call reliability and all the ways a call fails partway through. And getting a nondeterministic worker to behave the same way twice inside a pipeline that has to be repeatable. None of those are new problems. They just show up somewhere people don't expect to find them.

img/gr-board
The board — dragging work into in progress.

Markdown first

Work is a page, pages group into chapters, and all of it is portable Markdown on disk. The project stays readable, greppable and version-controlled with the app switched off, which is most of why I built it this way.

Ideas sit in a separate garden until someone commits to one, so a maybe doesn't quietly turn into committed work.

Chapters end honestly

Closing one asks what should happen to whatever it didn't finish: roll it into the next chapter, send it somewhere named, set it loose, or leave it where it is.

Estimates are history, not forecast

They get added up from what happened — never predicted.

The board earns attention

Only what deserves focus now is visible. Everything accepted but not active lives in a searchable backlog instead of taking up a permanent column.

Nothing vanishes quietly

Archiving is undoable, and once the undo window closes, search is the only way back in. An away digest covers what moved while you were gone.

The permission model

Grimoire ships an MCP server so an agent can read and write a project over its HTTP API, and the permissions are deliberately asymmetric. An agent adds and refines. Only a person destroys or restructures. Archiving, promoting an idea, and managing chapters, categories, fields or membership stay closed to an agent no matter what its token scope says, and every agent write lands in an audit log. Working out what an agent should be able to write was easy. Working out what it should never be able to undo took a lot longer.

gr agent access
img/gr-agent-access
Agent tokens, and what a token can never do.
gr chapter close
img/gr-chapter-close
Closing a chapter.
gr digest
img/gr-digest
Back after a day away: the digest, every write attributed — including the agent's.

Shipping it

It deploys to a Proxmox guest behind a Cloudflare Tunnel, with a self-hosted GitHub Actions runner shipping verified pushes to main. The MCP package is public: grimoire-mcp.

gr markdown proof
img/gr-markdown-proof
The same project as the board draws it, and as it sits on disk.
img/rc-map
The Ascent, traveled: from today's ritual at the base, up past the conquered camps, into the mist.

Clear it region by region

The whole drawing journey laid out as a fantasy world map. Each region grants a power and takes three tiers to finish: Scout to learn the concept, Grind to do the reps, Boss fight for one finished piece that proves it.

Five class paths — from Draw Anything through Character Artist to Manga & Anime — run different routes up the same mountain, so you're not looking at twenty regions with no idea where to start. Regions gate on their prerequisites, and XP, levels and a daily warm-up streak carry the momentum between them.

rc lesson
img/rc-lesson
Opening a region from the trail.
Not only tracking

A practice room for daily drills, a critique flow for feedback on finished pieces, and a field guide for reference.

The session follows the stylus

A QR handoff moves a session from desktop to tablet. Anyone actually doing this is holding a stylus, so the session has to be able to follow them there.

img/rc-canvas
Where the time actually goes: a guided blind-contour session — timed, rules pinned to the canvas.
rc practice
img/rc-practice
The practice room.
rc camp
img/rc-camp
Base camp.