TL;DR Muse replaces bespoke agent orchestration consoles with four tools every software team already has open: Slack, Linear, GitHub, and Claude Code. No dashboard, no SPA, no human-written code. Every PR clears seven CI gates and a three-persona AI review panel under a zero-findings merge policy. 40,000 votes collected, foundation model evaluation 80% faster.
Muse, short for Meshy Universal System for Evaluation, is our internal model evaluation platform. Teams at Meshy use it daily to figure out whether new model checkpoints perform better than old ones.
One day, a hot new model checkpoint had just finished brewing. It needed an arena match to determine the next training strategy. The team found that they needed a new Muse feature to support it.
"@Linear create a Muse issue about this"
said the Product Manager on Slack.
A minute later, Linear issue MES-12345 was created.
The Muse platform owner — me — saw the issue. It needed to ship ASAP to unblock the eval task. Then, the message was sent to Claude:
"Investigate and fix MES-12345"
Claude looked at the issue, then surfaced two design decisions. Decisions were made, and I got off the terminal to work on something else.
An hour and a half later, a new version of Muse was deployed. Done.
Autonomous software engineering does not have to be fancy agent orchestration consoles or dashboards. And in this article I will explain how we made it possible.
A gallery, a viewer, and a vote button
The question Muse answers is deceptively hard: is the new model checkpoint actually better? Of course there are quantitative metrics to track in each training run, but they are far from accurately representing what "good" looks like in 3D generation. So Muse does the simple yet effective thing: double-blind testing. It puts two models' outputs side by side, same input image, randomized left and right, and lets teammates vote blind.
An evaluation is a batch of these pairs. Someone points Muse at two sets of generated meshes and a folder of reference images, and the app takes it from there: pairs the models, blinds the assignment, serves one comparison at a time. Votes accumulate whenever people have a spare minute. The whole interface is built to be snackable, and is almost entirely navigable with hotkeys. A voter who has to reach for the mouse votes less.
![]()
The Muse voting interface: two generated meshes, same input image, randomized left and right.
That's the product. A gallery, a viewer, and a vote button. Nothing about Muse's function is complicated, which is exactly why it makes a clean case study. What's unusual is only how it gets built.
An architecture agents can navigate
The Muse project started in late 2025, when "vibe coding" had just become a new buzzword. Apart from serving Meshy's growing evaluation efforts in training and release cycles, Muse also had an ambitious goal: to build software for the whole company, with zero lines of human-written code.
Today, the outcomes are stellar:
- Over 40,000 votes collected since Muse's initial delivery, with over 30 evaluation projects per month
- More than 4 teams actively using Muse for their daily work
- First internal codebase actively contributed to by at least 1 non-technical team member
- First internal codebase achieving fully autonomous development
In total, we estimate that Muse has sped up our foundation model evaluation by at least 80% and cut at least 10 person-days of work during each model release. Without Muse, maintaining the current development and release throughput of Meshy's generative model collection such as geometry, texture, and smart topology, would have been nearly impossible.
From the first day, Muse was designed to be as agent-legible as possible. In 2025, we already found that to implement the same use case, agents struggled in some ways while succeeding in one shot in others. Many of these iterations finally resulted in a deliberately boring tech stack today. The backend is Python: FastAPI, Postgres for records, S3 for meshes and images. The frontend is server-rendered: Jinja2 templates, HTMX for partial page updates, Tailwind for styling, and one real client-side component, the WebGL 3D viewer that renders the meshes. There is no SPA and no separate frontend module. The coupling between the two stacks is the oldest one on the web: the backend renders HTML, the browser displays it. When a page needs to change, HTMX requests a fragment and the server renders that too.
Here is the architecture for the core evaluation app in Muse:
![]()
The layered architecture of Muse's core evaluation app. Dependencies point downward only.
Two properties of this shape matter for agents. First, the frontend state lives on the server. The HTML a user sees is HTML the backend produced, so a test (or an agent) can assert on it directly without driving a client-side framework to reconstruct what the user saw. Second, every dependency is machine-enforced and points downward only. A feature folder may not import its sibling; if two features need each other, the composition root injects one capability into the other, so the dependency is visible in exactly one file. Each layer has one job, and the jobs are written down where an agent will trip over them:
| Layer | Role | Why an agent can work here safely |
|---|---|---|
main/ | Wiring only. Registers the URL routes of each feature module. | A new feature is one folder plus one include line. Little to hallucinate. |
apps/<feature>/ | One folder per feature: routes, pages, service, store, tests. | The blast radius of an edit is limited to the folder. Sibling imports are a build failure, not a review comment. |
adapters/ | The outside world: web rendering, database, object storage. | All I/O goes through one seam per system. Fakes exist for every seam. |
libs/ | Shared installable libraries. | Versioned like third-party code. Changing one is deliberately loud. |
Dependencies only ever point down this list, and the direction is enforced by a build check, not a convention.
Three additional properties make the codebase agent-legible on purpose:
- Every directory carries its own documentation (what the service does, its layering rules, the exact commands that gate a merge), so an agent landing cold can orient without asking or making things up on its own.
- Non-trivial modules carry docstrings with backlinks to design docs, so the why survives next to the what.
- No file may exceed 500 lines, with a ratchet on a few exception cases. For the exceptions, files may shrink, never regrow. Context windows are finite; the architecture respects that.
Progress shouldn't live in a small room
Today, as LLM agents are much more powerful than in 2025, we want the development of Muse to be fully autonomous. That is, the human only points the agent at the task. The agent orients itself around the codebase and task context, works its way through implementation, CI, and code review, and finally self-drives the pull request to merge. The human only determines the priority of issues, and makes key design decisions.
First thoughts about fully autonomous development are usually bespoke platforms: orchestration consoles, agent-native workspaces, app after app that promises to manage your fleet. We looked, and declined. Not because they aren't great products, but because of legibility, again.
We want agents' work to be legible by humans, just as the codebase should be legible to agents. We're a decently sized company. Put autonomous development inside a custom console and its progress becomes visible only to the few people who live in that console.
As a result, again, the collaboration and development tool stack is deliberately boring. Everything in our story ran on four tools every software company already has open: Slack, Linear, GitHub, and Claude Code. This makes the progress the agent makes exactly as observable and searchable as any team member's. A product manager opens Linear issues and checks feature status the way they always have. The GitHub bug bot or security review reads PR threads the way it always has. Anyone who reports a bug pastes a screenshot into the same Slack channel as always, and the fix shows up as an ordinary pull request. Nobody has to learn anything to supervise the "machine".
Here is the shape of a single autonomous issue, end to end:
![]()
One autonomous issue, end to end. The human touches the loop in exactly two places.
The human touches this loop in exactly two places: pointing the agent at the Linear issue, and the handful of decisions the agent surfaces before it writes code. Everything in between, the implementation, the checks, the review conversation, the merge, and the deploy, runs with no one in the seat. When a check fails or a reviewer objects, the agent reads the failure, fixes it, and pushes again, and the loop simply runs another lap. It exits only when the branch is green, every finding is resolved, and the queue has merged it.
What makes this possible is not a bespoke integration. It is that the three tools already expose command surfaces an agent can drive directly, the same ones a person uses:
- GitHub, through its CLI. The agent opens the pull request, reads back each CI job's status and logs, replies in review threads and marks them resolved, and enqueues the branch for merge, all through ordinary
ghcommands. - Linear, through its MCP server. The agent reads the issue it was pointed at, follows the links to related issues for context, moves status, and files follow-up issues for work it discovers along the way, all as first-class tool calls.
- Claude Code's own monitor loop. CI takes minutes and the merge queue takes longer, and no human is watching. The agent watches instead, and the way it watches is what turns an unattended run into something practical rather than a token bonfire.
When the agent opens a pull request it does not sit refreshing a page; it registers what it is waiting on, the CI run, the review threads, the merge-queue entry, and then completes the turn. Claude Code's Monitor feature re-invokes it only when that watched state changes: a job turns red, the review panel posts a finding, the queue merges the branch. Because the agent is woken by messages from the Monitor about each status update, it will take the next action accordingly, such as pulling a failed job's log and pushing a fix, replying to a review thread and marking it resolved, or enqueuing the branch once every gate is green.
None of these is specific to Muse. Any repository reachable by these three tools can be driven the same way, which is exactly why we chose them.
Trust is a build artifact
We don't trust the LLM itself. We trust the LLM agent inside a harness, and the harness can be engineered as the guardian for reliability. Concretely, the harness is our CI/CD pipeline plus the review layer on top of it. This is what every pull request goes through, no exceptions:
![]()
Every pull request passes the same harness: automated checks first, then the AI review panel.
The individual jobs, with typical times measured on recent real PR runs:
| Job | What it enforces | Typical time |
|---|---|---|
| Guardrails | Layering (no sibling imports), folder shape, the file-size ceiling, naming | ~60 s |
| Lint | ruff format and lint rules | ~30 s |
| Type check | pyrefly, across the whole project | ~20 s |
| Unit + integration tests | pytest against a real Postgres built from the schema | ~90 s |
| Visual tests | Playwright screenshots diffed against committed goldens, in CI's own renderer | ~140 s |
| Security | semgrep static analysis plus a secret scan | ~40 s |
| Container boot test | the app must boot, which it refuses to do unless every route declares who may call it | ~215 s |
The guardrails are the architecture we described turned into machines. A sibling import is not a review comment here; it is a red build. The visual goldens are captured in CI's own rendering environment, so "works on my machine" can never be an argument for merging a bug into main.
The AI review layer is where it gets interesting. Every PR is read by our own panel of three coding agent reviewers reading the same diff against the repo's written design principles, each with a different assigned persona. Here we show an outline of the prompts given to each reviewer.
The Principal Engineer:
Persona: Principal Engineer, design review, big scope
You review at system scope: does this change belong, fit the architecture, reuse what already exists, and stay additive, not whether a line is tidy.
You see the diff + the Guides, not the whole repo. Judge reuse and pattern-consistency ...; don't claim a repo-wide search you can't perform.
Your lens: Judge the diff against these parts of the provided Guides ...
Litmus: "If I were the principal eng for this codebase, would I send this back for living in the wrong place, duplicating an existing capability, or adding a second way to do a solved thing?" If no, return
[].
The Senior Engineer:
Persona: Senior Engineer, logic / implementation / quality
You review within the change: is the logic correct, clean, testable, and secure. This is the most thoughtful review, trace the code paths, don't skim. Focus on the diff and the files it touches (read them in full).
Your lens: Judge the diff against these parts of the provided Guides ...
Litmus: "Would I approve this logic, or would I find a bug, an untestable seam, or a security mistake on a careful read?" Be specific about the failing input or code path. If the logic is sound, return
[].
The QA Engineer:
Persona: QA Engineer, does it work, is it tested, will it break
You review behavior and risk: will this change do what it claims, is it covered by tests, and could it break something live or in production? Read the PR title and description, then check the diff delivers it.
Your lens: Intent-match ...; Test coverage ...; Regression risk ...; Production traps ...
Litmus: "If I were QA signing off, would I block this for not doing what it claims, for shipping untested behavior, or for a change that breaks prod?" If it's safe and covered, return
[].
Apart from looking at the same code diff from different angles, the 3 reviewers also share a common set of rules that govern the development of the entire codebase, i.e., the Guides:
- Things that change together live together; unrelated things stay separable. Changing or deleting one concern touches one place.
- Business logic doesn't depend on the outside world's details. A business-logic function runs in a test with fakes passed in, no real I/O. Swapping a backend touches no business logic. The app never calls its own HTTP endpoints.
- Reuse, don't reinvent. Each capability has one implementation; a new consumer calls the existing interface instead of copying it.
- Add features by adding, not by editing. Adding a feature is "new files + one registration line", and deleting it touches no other feature.
- One established way to do each thing. A newcomer copies the existing pattern rather than choosing, and tests need no server or DB.
- Delete code the moment it stops being used. Nothing ships that nothing calls; "keep it just in case" isn't a reason because git remembers.
- Prefer the simplest thing that works; add complexity only when forced. Every abstraction earns its place with a concrete current need. The change is the smallest that solves the problem.
The panel posts each blocking finding as an inline comment anchored to the exact line, and those comments gate the merge until they are resolved.
A review from a recent feature PR looks like this:
[medium] Regression risk. This function is the shared create-and-sync chokepoint; it now collapses view-suffixed files into one grouped row. Any pre-existing dataset ingested under the old scheme will, on its next sync, be silently regrouped, deleting the old sample identities that live evaluations still reference. No test covers re-syncing an existing dataset through the new grouping.
That finding was correct. The code had passed the full test suite and a first round of green CI. The tests were right for the code as written. The reviewer saw that the code as written was wrong for the data we actually have. Claude fixed it, added the missing regression test, replied in the thread, and resolved the finding. The whole exchange is public in the PR, exactly the same as how you would mentor a junior engineer through code review.
That was one PR. As we run the agent over time, we are further able to discover some other typical patterns:
- A fix causing a new problem. A diagnostic tool we shipped measured download speed. The review caught that the math used MB (1,000,000 bytes) but the server sent MiB (1,048,576 bytes), so every speed would have read 5% low. A few pushes later, the review flagged that the fix left two constants kept in sync by hand. So it requested calculating speed from the actual bytes received instead. The reviewer caught a bug that addressing its earlier request had created.
- Dead-end use cases. The same page shipped with its link visible to every signed-in user, even though the page itself needed a specific permission. Anyone without that permission would click and hit a 403 error. No test failed, since the link showed up and the permission gate worked. The QA reviewer compared the two and blocked the merge until the link only showed to people who could actually open the page.
- Hacking to pass tests. One fix's test manufactured the exact exception it was supposed to catch, so it passed even if the real error handling was broken, because the test received the "correct" exception message. The panel flagged it, and the rewritten test runs real errors through the real code. The same PR also had a test that passed on the developer's machine but failed in CI, because it quietly relied on a credentials file that only exists on our dev box. Both were bugs in the tests themselves, caught by the layer above them.
Our merge policy is 0 AI review findings. Every finding is fixed, or rebutted with evidence, before the queue takes the PR. We made rebuttal a viable option because the review panel over-flags at a tolerable rate. Writing down why a finding is wrong can, in turn, occasionally catch a mistake in the review panel's own reasoning.
Past the checks, the merge queue re-validates every branch against the moving tip of main, and a merge deploys to production automatically with no human in the loop.
![]()
Past every gate, the merge queue re-validates the branch and a merge deploys to production.
Lines of code is a small part of the story
Over the course of many self-driving PRs, we found a striking yet reasonable pattern: the time taken to iterate on checks and reviews is much longer than the time taken to write the first implementation, and the ratio varies greatly by the type of task as well. Here we compare two types of development tasks in the same week.
Case A: retire a dead feature. A small legacy feature went offline; its link, confirmation dialog, translation, tests, and screenshot baseline all had to go. From Linear issue filed to PR merged on main took only 16 minutes, with the first 2 minutes writing code. Reviews were advisory-only.
Case B: a major improvement on the network diagnostic feature. It was a similar-sized change by line count with the first version completed in 5 minutes, while total time to merge was about 3 hours. Over the course of it, the agent resolved 5 review rounds and 7 merge-blocking findings.
The timelines are quite different as well:
| Case A: retire a dead feature | Case B: improve the diagnostic tool | |
|---|---|---|
| Change size | +10 / -125, 8 files | +439 / -19, 9 files |
| First working version | 2 min | 5 min |
| Issue filed to merged | 16 min | 3 hours |
| How much longer to merge than the first version | 8x | 36x |
| Blocking review findings | none (advisory only) | 7, across 5 rounds |
| Worst case if the code is wrong | a link nobody should click | every user trusts a wrong number |
![]()
Two tasks of comparable line count, given verification effort in proportion to risk.
The second one takes eleven times longer not because the code was harder to write, but because the risk was different, and verification effort was spent in proportion. Case A was a deletion: the worst outcome was a broken link that was already not supposed to be visited. Case B was a measurement tool: the worst outcome was every user trusting a wrong number. The review panel found the errors, and then spent three more rounds refusing partial fixes until the error handling was typed, complete, and thoroughly tested. Each round made the implementation strictly tighter. None of those defects failed a test, and all of them would have shipped.
This is the part one-shot code generation gets wrong by construction. One-shot generation optimizes the metric that is easiest to see: time from prompt to code that compiles and runs. Both of our cases hit that metric in minutes, and if we had stopped there, Case B would have been done a few minutes after it started, and wrong for months.
The same pipeline that waved a deletion through in 16 minutes held a measurement tool for 3 hours, with no human deciding which treatment each deserved. When people ask whether AI-written code can be trusted in production, this is the answer: the code is exactly as trustworthy as the verification wrapped around it, and verification depth is now something you buy with agent hours instead of engineer days. A wrong speed report costs more than three hours the first time someone routes a network complaint by it.
What we learned and what's next
Today, autonomy is more of a harness property than a model property. Every model upgrade made our agents better; however, model capability itself does not ship with a guarantee that you may trust the agent to freely merge its pull requests. Instead of investing more human attention or blindly using the most expensive frontier model, we chose to build the "hands" and "eyes" so that agents are equipped with all they need to produce correct production code. And with model capability evolving, this also makes us ride the wave instead of rebuilding the scaffold to make up for models' limitations.
The human role concentrates, it doesn't disappear. What's left is the irreducible core: deciding what's worth building, writing it down clearly enough, and exercising judgment at the moments the machines flag as ambiguous. Design taste and specification quality are now the highest-leverage engineering skill in this team.
And the most interesting takeaway about agent teams is that the value of the team comes from conflict rather than collaboration. It might make sense to split a full-stack task by frontend and backend agents. It makes less sense to have a frontend agent and a backend agent talk with each other just to complete the task. The benefit of the team emerges when members have conflicting goals. Our Muse development agent wants to ship the code. Our AI review agents want to find where the code is wrong or poorly designed. It is their tension that makes the result trustworthy, and faithfully reveals the true amount of work of a development task.
More importantly, we find that our explorations lead us towards an emerging way to develop software: an autonomous engineering organization. Although we have demonstrated that agents can autonomously complete individual tasks with existing tools, a few missing pieces and open questions invite future endeavors:
- How much are we able to scale such systems up? As the autonomous organizations work on bigger and bigger tasks and projects with more parallel agents, what new problems will emerge?
- How can the harness's own outputs, such as first-shot CI pass rates and AI review findings, become evaluation signals? How do we measure the performance of an agent in the organization, and how do we make it self-evolving?
- How may humans' design and product tastes be baked into such systems? If engineering can be made autonomous with sparse human judgments, how far can we take this to solve the problems of who we should build the software for, and what it should look like?
Finally, thank you for reading all the way to the very end. The to-do list for finding out answers to these questions lives fittingly in Linear, where our next agent, or you, might pick it up.







