September 8, 2026 · Tim Williams, Member Of Technical Staff, Orbital Industries

Everyone in AI engineering agrees the bottleneck has moved from the agent to the harness. We agree with the theory — we just disagree about how far the abstraction goes. Here's what happens when you take it all the way.
We've run the same production engine across nine domains since the start of May: 4,292 agent tasks, 461 pull requests reviewed, 107 tickets triaged. Software is one of those domains, not the whole of them.
For the last year, the story in AI software engineering has been about better agents: bigger models, longer context, higher benchmark scores. That's started to change, as Mitchell Hashimoto, LangChain, and Hugging Face's agent glossary have each pointed out. The bottleneck now sits in the harness around the agent: the system that carries a signal from the outside world through every step until something ships. Both Forrester and Microsoft have mapped this out for the software development lifecycle (SDLC) specifically. A bug report comes in and gets triaged into a change, then built, tested, reviewed, and shipped. Production gets watched, and the watching throws off the next signal. It's a loop that feeds itself, what Factory.ai has started calling the software factory.
We agree with that theory, but we disagree about the level of abstraction that's possible.
Because there's nothing specific to software in that loop, the primitives generalize. We know because the same engine that reviews our pull requests also runs a chemical-safety review, a CRM, and a cooling-fluid discovery campaign. Same engine, different file.
That's the trick: our software factory is one file, 120 KB of JSON.
Any workflow can be represented as a graph, and that's the core abstraction here. Agentic harnesses for software are usually drawn at the system level, as a data flow pipeline, but that's an opinionated abstraction built specifically for software. Peel away the layers and each element in the pipeline breaks down into one of three things: a typed node, a typed edge, or a trigger.
Nodes break down into two aspects: properties and a state machine. A pull request maps onto this generic structure, with the specific flow added in:
{
"type": "PullRequest",
"fields": [
{ "key": "number", "kind": "number", "required": true, "unique": true },
{ "key": "title", "kind": "string", "required": true },
{ "key": "author", "kind": "string", "required": true },
{ "key": "branch", "kind": "string", "required": true }
],
"states": {
"initial": "open",
"values": ["open", "reviewing", "reviewed", "needs_human_review", "amendment_review", "merged", "closed", "ready"],
"transitions": [
{ "from": "open", "to": "reviewed" },
{ "from": "reviewed", "to": "amendment_review" },
{ "from": "reviewed", "to": "merged" }
]
}
}
The edges define relationships between the data, linking to tickets, feedback, and more:
"edgeTypes": [
{ "type": "FIXES_FEEDBACK", "from": "FeedbackTicket", "to": "PullRequest" },
{ "type": "FIXED_BY", "from": "ErrorGroup", "to": "PullRequest" }
]
And triggers define how data transitions and which actions fire. If a pull request's status changes to open, that starts a review, and once it's reviewed, that starts the code fix.
[
{ "name": "PR Review Workflow", "trigger": { "event": "node_created", "condition": { "nodeType": "PullRequest" } } },
{
"name": "PR Re-Review on Update",
"trigger": { "event": "status_changed", "condition": { "nodeType": "PullRequest", "status": "open" } }
}
]
There's nothing inherent in the engine that encodes these states. The schema for our software factory encodes all the state machine rules, nodes, edges, and triggers that define the flow.
So all a builder needs is a concept of the machine they want to build. We never write engine code or workflow conditions by hand. Everything is defined in JSON and encapsulated in three core concepts.
The SDLC is well understood, and it maps cleanly onto a looped workflow: signal, triage, build, test, review, ship, monitor.

And here's the software factory for the repo that builds the engine:

Solid arrows are typed edgeTypes; dashed are listeners and triggers.
This is the same fundamental cycle. Datadog monitors the code, and Linear tracks user requests and bugs. Both get automatically acknowledged and investigated before being converted to pull requests. Those pull requests trigger reviews, which either get the code merged or start a loop to fix feedback. Merging triggers deployments and regression runs, and the cycle restarts.
Adding a new feature or a new loop is a no-code change; all it takes is tweaking the rules in the JSON.
What most companies are shipping as products, we ship as small data files.
Our engine doesn't care about the data. It only cares about the graph primitives, so the scope of what it can run is effectively open-ended. We've been running it since the start of May, and it now carries 118 schemas with live data across nine domains, covering areas like chemical-safety reviews, zeolite screening, CRM, procurement, and hiring. Software engineering is just one of them. We run multiple software factories, each scoped to a different repo.
And these aren't demos. The busiest domains have each run thousands of agent tasks on the same engine:
| Domain | Agent tasks |
|---|---|
| Software engineering & agent ops | 7,165 |
| Finance & procurement | 3,871 |
| GTM / sales & marketing | 3,016 |
The software factory is the busiest, but that's us dogfooding. The two domains right behind it, procurement and sales, have nothing to do with code, and each has run thousands of tasks on the identical engine.
As far as our engine is concerned, a pull request is no different from a chemical candidate.
{
"type": "PullRequest",
"fields": [
{ "key": "title", "kind": "string" },
{ "key": "author", "kind": "string" }
],
"states": {
"initial": "open",
"values": ["open", "reviewing", "reviewed", "needs_human_review", "merged"],
"transitions": [
{ "from": "open", "to": "reviewed" },
{ "from": "open", "to": "needs_human_review" },
{ "from": "reviewed", "to": "merged" }
]
}
}
{
"type": "CandidateBlend",
"fields": [
{ "key": "blend_label", "kind": "string" },
{ "key": "component_a", "kind": "string" }
],
"states": {
"initial": "Proposed",
"values": ["Proposed", "PropertyCalc", "Screened", "AwaitingApproval", "Simulating", "Scored"],
"transitions": [
{ "from": "Proposed", "to": "PropertyCalc" },
{ "from": "Screened", "to": "AwaitingApproval" },
{ "from": "AwaitingApproval", "to": "Simulating" }
]
}
}
They even pause for a human in the same place: needs_human_review for a pull request, AwaitingApproval for a blend. The semantics differ, but the core primitive, a state machine plus properties, is identical. Any team can point the same engine at their own work.
That's the whole point: the thing the whole industry is racing to build (the software factory) is one file out of 118 for us. Everyone's building a factory, but we built the machine that stamps them out, and software is just one of the stampings.
One of the best examples in action is the factory we run on our own engine code. We've built a recursive loop: as more people use the platform, raising feedback and generating logs, the platform improves itself.
This works in three ways:
The continuous learning is inherent in the schema. We can keep adding new ways to do it, but the engine already has a built-in method for recursive self-improvement, all defined in JSON.

The whole industry is racing to build the software factory. The factory was never the hard part, and it was never really about software; it's a loop, and a loop is a graph.
We're not trying to win the agent race. The models, ours and everyone else's, will keep getting better, and every time they do, this bet pays off a little more. We're betting on a deep abstraction, on the thing that doesn't change with the model and isn't welded to a domain: the harness. More than that, we believe the harness is a fully extendable and generalizable engine.
In 'Code Mode vs MCP: The Case for Taking the Model Out of the Loop', I wrote that any workflow is just a graph. This is the proof: we run 118 of them, and software only takes up a small proportion.
Everyone's building a factory. We built the machine that stamps them out, and the primitive underneath all of it is the graph.