fnMoa: Engineering on Autopilot

Inspiration

When AI coding agents started getting really good, I noticed something interesting.

Writing code wasn't actually the hard part anymore.

The real work starts after the code is written.

Someone still has to read the issue, understand what needs to change, create a branch, open a pull request, respond to review comments, fix failing CI, merge the PR, and make sure nothing breaks along the way.

That's the part that quietly eats hours every week.

I also kept seeing the same problems whenever people demoed coding agents. They could generate code, but they weren't safe to trust with a real repository. An agent would force-push over someone's work, open multiple pull requests for the same issue, or "fix" a failing test suite by deleting the failing test.

It became obvious that the biggest challenge wasn't making the model smarter.

The challenge was building a system around the model that I could actually trust with my repositories.

So instead of building another coding agent, I built fnMoa, a system that automates the entire GitHub engineering workflow while keeping every dangerous action outside of the model's control.


What it does

fnMoa takes a GitHub issue from open to merged completely automatically.

The workflows look like this:

Triage → Solve → Review → Address Review Comments → Fix CI → Merge

Today you kick off a workflow from the moa CLI or the dashboard. For example moa wf solve on an issue, then review, fix-CI, and merge.

Under the hood, everything revolves around a single HTTP server that acts as the source of truth. The dashboard, the CLI, and GitHub webhooks are all just different ways of talking to the same backend.

It also supports multiple organizations running at the same time. Each organization gets complete isolation:

  • its own repositories
  • its own GitHub identity
  • its own Claude session
  • its own memory
  • its own billing

Nothing leaks across tenants.

But what really makes fnMoa different are two ideas.

Grounded Oracles

Instead of asking the coding agent to "remember" how a codebase works, every project can have domain-specific knowledge bases called oracles.

Before writing any code, the agent consults the appropriate oracle.

Each oracle is simply another read-only Claude instance that only has access to documentation for that domain. It answers questions with citations instead of guesses.

That means the coding agent follows the project's actual architecture and conventions instead of hallucinating patterns.

Today fnMoa ships with 26 built-in domains, and repositories can add their own.

Closed-Loop Memory

Every run leaves behind structured memories.

Future runs automatically retrieve relevant past decisions before doing any work.

Over time the agent stops repeating the same mistakes because it remembers previous reviews, fixes, architectural decisions, and patterns from the repository.

Instead of every run starting from zero, the system continuously gets better.


How I built it

The entire system is written in TypeScript.

The biggest architectural decision was surprisingly simple:

Keep deterministic engineering separate from AI reasoning.

The model is responsible for exactly one thing: thinking.

  • It can read code.
  • It can write code.
  • It can review code.

That's it.

It never performs Git operations.

Every irreversible action, such as creating commits, pushing branches, opening pull requests, updating branches, or merging code, is handled by deterministic application code outside the model.

This makes dangerous mistakes impossible.

The model literally cannot:

  • force push
  • create duplicate PRs
  • merge the wrong branch
  • delete tests just to make CI pass

Those actions simply aren't available to it.

The backend exposes a single HTTP API that manages workflows, authentication, async tasks, GitHub webhooks, and orchestration.

Whenever reasoning is needed, fnMoa launches the logged-in Claude CLI in headless mode with the appropriate oracle attached.

The only public-facing component is the moa CLI, published on npm as @fnmoa/cli.


Design decisions that mattered

Repository Isolation

Every repository keeps one clean base clone.

Each workflow creates its own temporary Git worktree before doing anything.

Once the workflow finishes, that worktree disappears.

This means dozens of workflows can run simultaneously without ever touching each other's files or branches.

Guardrails

A coding agent should never be able to run forever.

fnMoa limits concurrent workflows with a semaphore.

Every workflow has:

  • inactivity timeouts
  • least-privilege tool restrictions
  • configurable spending limits

If a run exceeds its budget, it's immediately terminated.

Verify Before Merge

Passing CI isn't enough.

Before merging, fnMoa also runs a local verification step.

That might include:

  • TypeScript compilation
  • linting
  • formatting
  • custom validation scripts

A PR only becomes mergeable when both verification and GitHub CI succeed.

Deployment

State is stored in PostgreSQL using Drizzle and handwritten idempotent SQL migrations.

The backend runs on AWS behind a Cloudflare Tunnel.

Aurora Serverless v2 automatically scales down when idle, making it inexpensive to leave running all the time.

The landing page is a prerendered Next.js application deployed on Vercel.


Challenges I ran into

Like most engineering projects, the interesting lessons came from unexpected failures.

Large prompt limits

Review prompts include entire pull request diffs.

Eventually one became large enough to exceed the operating system's command-line argument limit (ARG_MAX).

Instead of passing prompts as command-line arguments, fnMoa now streams them through standard input.

The limit disappeared completely.

Git worktree failures

One forgotten branch left checked out in the base repository caused every future workflow to fail.

The fix was making every run start from a detached HEAD and serializing clone and fetch operations per repository.

Sequential merge problems

If one pull request merged before another, the second workflow became outdated and waited forever.

Now fnMoa automatically updates the branch using GitHub's Update Branch API, reruns CI, and only stops when there's an actual merge conflict.

Multi-tenant authentication

Originally every organization shared one broker secret.

Each new enrollment overwrote the previous one, breaking token refresh for everyone else.

The fix was storing authentication separately for every organization.

Waiting efficiently

The CLI originally polled task status continuously.

That wasted requests and felt slow.

Switching to long polling allowed clients to wait on a single request until a workflow finished, making the experience much smoother.


What I learned

The biggest lesson surprised me.

The model wasn't the product. The infrastructure around the model was.

Reliable AI systems come from deterministic engineering, not clever prompting.

Another lesson was that grounding consistently beats reminding. Giving the model access to accurate, cited documentation produced dramatically better results than trying to squeeze more instructions into a system prompt.

Memory was equally important. Once every workflow could remember previous decisions, the agent stopped behaving like isolated one-shot sessions and started feeling much more like a teammate that actually knew the repository.

Finally, I realized infrastructure decisions directly shape the user experience. Choosing Aurora Serverless and Cloudflare Tunnel wasn't just about deployment; it made an always-on engineering assistant inexpensive enough to keep running continuously.


What's next

The roadmap is focused on making fnMoa useful wherever developers already work.

Soon you'll be able to:

  • Run fnMoa entirely on your own infrastructure so your repositories, secrets, and data never leave your environment.
  • Bring your own Claude or OpenAI API keys and choose different models for different workflows. For example, using a fast model for triage and a stronger one for implementation.
  • Turn on GitHub webhook triggers so that labeling an issue with moa or opening a pull request kicks off a workflow automatically. The webhook path is built into the server; enabling it is a configuration step, not new code.
  • Control fnMoa directly from Slack and watch every workflow stream live progress back into the conversation.
  • Embed fnMoa inside internal developer portals using a lightweight SDK.
  • Add custom project-specific oracles, improve cross-PR conflict prediction, and expand the memory graph to answer questions like "Which files have caused the most problems over the last six months?"

The long-term goal isn't just to automate writing code.

It's to automate the entire engineering workflow in a way that's reliable enough for teams to trust.

Built With

Share this project:

Updates