Gavin Bintz ← Writing
Writing · 02

The Black Hole Architecture

How I used written direction, offset schedules, and file ownership to run autonomous coding loops in Helios.

For humans

The experiment, the constraints that held, and the limits I found.

I built Black Hole Architecture after getting tired of managing the managers in my coding-agent experiments. The bet was simple enough: maybe the repository could pull the work forward without a coordinator sitting above it.

I tried it inside Helios with scheduled tasks on Jules. There were ten roles, split between planning and execution. Each one woke up, read the repo, did a narrow job, left behind a plan or a commit, and went away again. No group chat. No shared memory server. No agent waiting around for its boss to assign the next ticket.

Black Hole Architecture is a dramatic name for machinery made mostly of Markdown, folders, schedules, and strict file ownership.

Why I Stopped Adding Orchestrators

My first workflow was ordinary: ask a model for one change, review it, and do the same thing again. When the queue grew, I added manager roles, handoff state, routing, and little recovery rules. Soon I was spending as much time thinking about the coordinator as I was about Helios.

Worse, the extra communication did not make me more confident. Now a bad assumption could be hiding in a task, a message, the manager's memory, or the worker's interpretation of all three.

So I took the manager out. A run would inspect the current checkout, make one bounded change, and leave a different checkout for whoever arrived next. The agent itself did not need to persist. The useful parts of its work did.

The Repository Supplies the Gravity

Every loop needs something that defines the direction of travel. In Helios, that was the README: a high-level description of the system I wanted, more abstract than a normal task list or PRD.

On each planning run, an agent compared that vision with the code in front of it and asked, "What is missing now?" The gap became a small spec. I started thinking of the vision as gravity because it did not issue commands; it made some next states more likely than others.

This distinction mattered. I was not maintaining a long queue of tickets for the agents to exhaust. I was describing an end state clearly enough that a fresh planner could find one useful gap on its own. When progress stalled, the first debugging target was usually the vision or a boundary, not the agent's motivation.

Defying Gravity

The metaphor has limits, of course. A vague README is weak gravity. If the document says only "make this excellent," the system will produce a tour of whatever each model happens to find interesting. The vision needs testable promises, architectural constraints, and enough detail to distinguish unfinished work from taste.

Time Does Part of the Coordination

I separated planning and execution by schedule. Planners ran in one window and wrote specs. Executors ran later and consumed those specs. The two groups did not operate on the repo at the same time.

That is the origin of the phrase "time is the mutex." The schedule is a coarse lock. It prevents an executor from implementing against a plan that another process is still changing, and it gives each run time to finish, test, open a pull request, and merge before the next wave starts.

In my Jules setup, the loop used two-hour cycles. Planning roles ran on even hours; execution roles ran on odd hours. I left roughly an hour between individual jobs because I wanted plenty of room for tests and pull requests. There is idle time in that design. I accepted it in exchange for a system that could run unattended without agents tripping over a half-finished batch.

Time alone is not enough, though. Two agents can wake up at the same hour and still collide. The stronger rule was spatial.

File Ownership Prevents the Collisions

Each planner/executor pair owned a distinct part of Helios: core, renderer, player, studio, documentation, or skills. A planner could read the entire repo but wrote only to its own plan folder. Its matching executor could implement the plan only inside the files assigned to that lane.

A cycle looked roughly like this:

WindowActive rolesOutput
PlanningFive planners with separate concernsSpecs under .sys/plans/{role}/
ExecutionFive matching executorsCode and tests inside owned directories

The exact count changed as Helios changed. The invariant did not: agents active in the same window could overlap in analysis, but not in writable files.

That is why the runs avoided merge conflicts between lanes. Parallelism was not the dangerous part. Overlapping authority was. Once two executors were allowed to "help" in the same shared file, coordination had leaked back into the architecture.

Planning and execution also stayed separate inside each lane. The planner described the intent, affected files, success criteria, and exclusions. It did not write production code. The executor implemented the spec and did not reinterpret the whole vision.

I learned to keep pseudocode out of most plans. Detailed pseudo-implementations consumed the planner's context and went stale as soon as another lane changed a related assumption. A good plan told the executor what had to become true and how to verify it. The executor still had room to work out the code.

Plans, Memory, and Failure

The specs were contracts small enough to finish in one execution run. They named the files to create or modify, the boundaries not to cross, and the checks that would count as done. Loose plans produced wandering pull requests. Tight plans produced boring ones, which was the goal.

Agent memory also lived in files. Each role had a small memory document for recurring traps and useful lessons, along with status and progress documents. I did not want a transcript of every run. I wanted the one fact that would stop a future planner from making the same mistake.

File-based memory is plain, inspectable, and versioned. It also joins the next vision-to-reality comparison. If an agent keeps hitting the same problem, the lesson becomes part of the state every later run can see.

This makes a failed execution less dramatic. The spec remains in the repo. A later executor can try again, or the next planner can replace the plan with tighter constraints. There is no long-lived coordinator holding a half-completed conversation in memory.

"Failure is harmless" would overstate it. An agent can still write bad code, burn a task allowance, or merge a defect if the tests and boundaries are weak. The narrower claim is that one failed run does not have to corrupt the control system. Recovery is another repo state, not a special social protocol among agents.

Jules Made Some Choices for Me

Jules fit the experiment because it supplied isolated cloud workspaces, GitHub integration, scheduled tasks, tests, and pull requests. It also imposed useful limits. Sessions per day were bounded by the plan, so a confused loop could waste part of the allowance but not run forever.

The awkward part was schedule setup. At the time, I could not programmatically create and manage all of the scheduled jobs cleanly. Five planning roles and five execution roles, each repeated throughout the day, meant a lot of manual forms. I wanted to configure that machinery once and then leave it alone.

That led to another practical rule: keep the system prompt boring. Each scheduled task received a short instruction to read its prompt file in the repo. All behavior lived in versioned files such as .sys/prompts/planner-core.md or .sys/prompts/executor-docs.md. Changing a role then became an ordinary Git edit instead of a tour through dozens of scheduler forms.

The dense implementation reference in the other tab includes the folder layout, prompt patterns, merge guardrails, and operating details. This version is about why the pieces exist.

Autonomous and Supervised Modes

I ran the system in two modes.

In autonomous mode, passing pull requests merged through GitHub Actions and the next scheduled wave inspected the result. Progress was slower than a continuous single-agent loop because of the gaps between jobs, but it continued while I was offline. The system traded bursts of speed for predictable convergence.

In supervised mode, pull requests waited for human review. That felt safer and sometimes was safer, especially while a boundary or test suite was immature. It also interrupted the feedback loop. A planner had already narrowed the task, and an executor had already run the checks, so routine reviews could become a human confirmation of work the system was designed to bound.

I do not think "never review agent code" is a sensible general rule. In this experiment, small pull requests, disjoint ownership, tests, and repeated planning made later correction cheap. Production risk, data migrations, security changes, and weak test coverage all raise the standard. The architecture should not be used as a reason to wave those differences away.

Where I Would and Would Not Use It

Helios was a very friendly place to run this experiment. I sometimes call it a "ghost ship" project because there was no active team changing the same files during the day. The agents had the repo to themselves.

That matters. I would not put ten scheduled agents into a busy product repository and expect the same result. Human developers would cross the file boundaries, plans would go stale between runs, and an agent could merge code based on a version that no longer exists.

Where would I use it again?

Libraries, command-line tools, documentation, tests, and services with clear modules all make sense to me. A planner can find one missing piece. An executor can stay inside one package. A test can give a useful answer when the work is finished.

Documentation may be the easiest example. One role could watch each package for changes and update only the related pages. The source is visible, the owner is obvious, and two jobs have little reason to edit the same file.

A shared database makes me nervous. Two folders can still depend on one schema, and a bad migration is much harder to undo than a bad paragraph. I would also avoid using this as the only design process for a detailed interface. Tests can tell me that a menu opens. They cannot tell me if the animation feels awkward or the spacing looks wrong.

The pattern works when the work can actually be divided. Drawing boxes around a tangled system does not make the dependencies disappear.

What Remains Unsolved

There is one question I still cannot answer with another scheduled job: when is the project ready for people to use?

The system knows when the plan folders are empty. It knows when the tests pass. Those are both useful checks, but they only prove that the agents finished the work they noticed.

What if the problem lives between two lanes? What if the tests are green because nobody wrote one for the broken case? What if all the features work and the product still does not make sense?

I thought about adding a release role. It could read every status file, run the full test suite, and write a report. I may still do that because the report would save me time. It would not make the final decision for me.

For now, production readiness stays a human decision. I am comfortable with that.

Growing Software

The experiment changed the order in which I debug an agent loop.

I used to assume that a stalled loop needed a better manager. Now I open the README first. Is the target clear? Then I check the ownership rules. Can two roles edit the same file? After that I check the schedule. Did an executor begin while a planner was still changing its instructions?

The problem is often in one of those three places. Fixing a paragraph or moving a job by an hour is much easier than building a new coordinator.

I would still start the next project in supervised mode. I want to read the early pull requests and write down the mistakes that repeat. Anything expensive to undo needs better tests and probably a human approval step.

When the pull requests become routine, I can step away. Routine is exactly what I want here. If an agent does something impressive, I usually start looking for the part it misunderstood.

The name is dramatic. The useful idea is not. Put the memory in files I can read. Keep simultaneous jobs out of the same code. Separate the person making a plan from the person implementing it. Then let the next agent read the repo and figure out what is still missing.

Try It in Your Repo

If your repository has clear boundaries and work that can be divided safely, I packaged the prompts, templates, and operating rules into a reusable agent skill.

Set up Black Hole Architecture →