Context

AI Factory started from a practical reason: I did not want to spend the whole summer at my computer, but I also did not want AI Coach development to stop. So I began building an experimental system that could take over part of the work for me: selecting tasks, editing the repository, opening pull requests, and returning feedback.

At the same time, I wanted to understand the real limits of AI. Not in an isolated chat where an agent answers one prompt, but in a longer development process where it has to keep context, respect project rules, work with branches, pass verification, and leave a trace that a human can understand later.

Problem

Codex could already help me design and implement AI Coach, but it still behaved mostly like a tool that needed close guidance. Every task, every change, and every decision required me to sit at the computer, provide the next step, check the result, and move the work forward. In practice, that meant coming back to the computer every few minutes, which defeated the original goal.

It also became clear that AI was strong at well-scoped individual tasks, but weaker across a longer development process. It did not naturally coordinate multiple pieces of work, preserve long-term continuity, or notice when small mistakes were starting to compound. An older bad decision or an imprecise piece of code could easily become precedent for a new session: if it was already in the project, the agent tended to assume it was correct.

Approach

I started building a layer above everyday AI-agent usage. The goal was not to create one smarter agent, but a process in which an agent receives a clearly bounded piece of work, project rules, input context, verification, and a way to hand the result back.

The foundation was tasks, branches, pull requests, and documented runs. The agent was not supposed to merely answer in chat. It had to make a concrete repository change, run verification, and leave a record of what it did, why it did it, how it ended, and what could be improved next time.

What the system did

AI Factory gradually connected several parts of a normal development workflow. It could work with a task board, choose a suitable task, create a branch and worktree, give the agent the required context, let it make the change, run verification, and prepare a pull request.

Feedback was just as important. A run was not meant to end with only “done” or “failed”. It also needed to capture what happened. Those records made it possible to look for recurring problems: where agents lost context, where they misunderstood the task, where rules were missing, and where deterministic scripts were better than AI decision-making.

Principles

The principles were behavioral rules for agents across the Factory. They did not say how to implement a specific feature. They described how to think about the work: how to handle sources, how to preserve intent, when to ask, when to suggest a process change, and what must remain visible after a run.

I did not start by writing a rulebook for AI in the abstract. The principles emerged when I analyzed where an agent made a mistake, why it happened, and how I would handle the same situation myself.

Over time, this became a strange kind of reverse engineering of my own way of working. I tried to name things that I often do automatically as a programmer: when I verify a source, when I stop and ask a question, when I look for the problem in the process rather than in one isolated mistake, when I distrust a result even though “something passed”, and when the work needs to leave a clear trail.

One important principle was to protect the chain of intent. AI tends to lose meaning along the way: my request becomes a task, the task becomes a prompt, the prompt becomes implementation, and each step can quietly change the original meaning. That made it important to name sources, make reasoning visible, and avoid letting vague references like “this” or “that” decide what would be built.

Another group of principles dealt with evidence. An agent can write a convincing explanation, but convincing does not mean true. So I added a rule: no conclusion without traceable evidence. If a claim could not be grounded in code, documentation, logs, history, or a concrete artifact, it should not be presented as fact.

A closely related principle was to look for existing solutions before inventing new ones. This was not only about external libraries or familiar patterns, but also about the internal history of the project: whether someone had already solved a similar problem, why they made that decision, and whether anything could be reused. AI tends to propose its own solution quickly, but good engineering usually starts by understanding what already exists and what tradeoffs it carries.

Some principles were about learning. When a problem appeared, I did not want to fix only that one case. I wanted to understand the class of problem: why it happened, where it might happen again, and which rule or process change could prevent it next time. The goal was not to collect a list of exceptions, but to distill recurring patterns.

That made AI Factory interesting beyond automation itself. It forced me to think about what actually makes a good developer good, and how part of that can be translated into principles that an agent can follow.

Task board

The Factory was built around a versioned task board stored directly in the repository. Tasks were not kept in an external tool. They were Markdown files separated by domain and state: Draft, Ready, and Done for AI Coach and AI Factory.

Each task had priority, dependencies, a goal, scope, acceptance criteria, verification, recommended reading, and a touch set: the area of files where changes were expected. That helped the agent understand not only what to do, but why the task existed, how to recognize that it was done, and where its work might conflict with other work.

I did not use a classic in progress state. The live lock was the branch and worktree. An agent reserved a task by creating a dedicated branch, working outside the main checkout, and returning the result through a pull request. Even a blocked or superseded result had to end as a visible change through a PR, so the system would not be left with an abandoned branch and a task that still looked free on main.

Skills

I split skills mainly so the agent knew what it was and was not allowed to do in a given mode. A typical distinction was between Project owner and Developer: without clear boundaries, planning, documentation, implementation, and product decisions easily collapsed into one blurry activity.

Project owner helped define how the application should behave, which decisions were still missing, and how a change fit into the wider system. That required broader context: documentation, epics, related decisions, existing tasks, and parts of the implementation that affected the design.

Developer, on the other hand, was not supposed to rediscover the entire product. It received a concrete task, a narrower scope, acceptance criteria, and likely files to touch. Its job was to implement, verify, and prepare a pull request, not to rewrite the product brief along the way or invent missing decisions by itself.

There was also Task picker / Task coordination, which handled selection and reservation of work. The agent was not supposed to randomly take “something from the board”. It had to consider priority, dependencies, open pull requests, active branches, worktree locks, and conflicts in the touch set.

And Merge policy worked as a safety boundary for completed pull requests. The question was not whether the agent liked the change, but whether the whole effect could be safely reverted by one revert. If not, or if the change touched more sensitive areas, it had to wait for my review.

Orchestrator tick

The basic unit of work was the orchestrator tick: one bounded pass through the system. It did not mean “run AI and see what happens”. It meant a concrete sequence of steps around one task.

The tick first checked whether it could start at all: whether another tick was running, whether there were blocking pull requests, whether a suitable task existed, and whether its touch set conflicted with other work in progress. If it was safe to continue, it selected a task, created a branch and worktree, prepared context for the agent, and started it.

The agent then worked on one concrete task. After it finished, the tick captured outputs, logs, and run metadata, ran verification, prepared or processed a pull request, and posted the result to Discord. Only then could follow-up observation steps run, such as the learner or candidate discovery.

What mattered to me was that the orchestrator should not become another agent that interprets everything in its own way. It should be a reliable pipeline: work selection, locks, branches, worktrees, logging, verification, and handoff. AI should solve the change itself, not reinvent the mechanics around it every time.

Triage tick

Alongside the orchestrator tick, there was a triage tick. It did not implement ready tasks. It handled inputs that were not yet clear enough to build safely from.

Typical inputs were bugs or suggestions sent through Discord. I did not want a raw report to immediately become an implementation task, because it often lacked context, decisions, or a clear expectation. The triage tick took one such input, created a branch and worktree, and let an agent determine what should happen next.

The result did not always have to be a development task. Sometimes the right answer was to reject the report or mark it as a duplicate. Sometimes it needed a question to me with concrete options. And when the request was clear enough, triage could update documentation or prepare an implementation task for the regular orchestrator.

This mattered because of the boundary between an input and work. A bug report or idea is not a task yet. A task needs a goal, scope, acceptance criteria, verification, and enough context that the agent does not have to invent the missing pieces during implementation.

Learning layer

After each run, the system was not supposed to only move on. It was also supposed to learn. That was the purpose of the learning layer.

The learner looked at captured run outputs: what the agent did, where it got stuck, what was unclear, where rules were missing, or where a known class of problem repeated. The result did not always need to be an action. Sometimes the correct outcome was that nothing important was found. Other times it produced a finding or a candidate: a proposal to improve the process.

A candidate was not automatically a new task. It went through a separate approval layer, because I did not want the Factory to freely rewrite its own rules. If the proposal made sense, it could later become a task, a principle update, a workflow change, or a simple script.

There was also a strategy review in addition to the per-run learner. It looked beyond one run at the broader history of the system: recurring problems, the backlog of candidates, the state of principles, and the process as a whole. It was a way to occasionally step back from individual tasks and ask whether the Factory was improving in the right direction.

Discord as the control surface

Discord was the human interface for the Factory. I did not want to open the repository or sit at my computer for every step, so I built a way to monitor and partly control the system from my phone.

/run started one orchestrator tick, and /status showed the current or previous run. Progress went to #runs, short results to #messages, and items that genuinely needed my attention to #alerts.

The most important channel was #inbox. It collected things waiting for a decision: improvement candidates, triage questions, or results that needed my reaction. Instead of free-form chat with the system, I received concrete cards with options: accept, reject, open detail, or answer a question.

Discord was not only for me. Other users could also submit bugs or suggestions through it. Those were first stored as raw inputs and only after approval entered triage, where they could become added context, a question, or a prepared task.

Discord was not the source of truth. It was a convenient control layer above repository files, the task board, pull requests, and run logs. It was deliberately not a remote shell. The bot did not execute arbitrary text written in Discord; every action led to a predefined script with limited inputs.

Outcome

AI Factory let me move part of AI Coach development into a mode where agents could work more independently than in a normal chat. That did not mean I could let development run without oversight. The type of work I did changed: instead of constantly giving the next instruction, I spent more time on rules, boundaries, output review, and improving the process itself.

The most valuable part was not only that agents could open pull requests. It was seeing where autonomy worked and where it fell apart. Some parts were easy to automate. Others needed more precise tasks, better context, or stronger guardrails. And sometimes the right answer was not another AI instruction, but a plain deterministic script.

It also became more than an AI experiment. It helped me understand my own development process better. If I wanted an agent to work more independently, I had to name things I usually do intuitively: how I define work, how I verify conclusions, how I recognize done, when I want a decision, and when automation is enough.