Skip to content

How it links together

Everything in Holophyte is one of four kinds of thing: a process that does work, a store that remembers it, a boundary that other code is kept behind, or a projection rendered from the store for someone to read. The store is the only thing every other part agrees on.

flowchart TB
  subgraph board[Linear · the board]
    L[project: ready tickets]
  end
  subgraph host[One machine]
    direction TB
    F[loop]
    SUP[supervisor]
    D[serve daemon]
    ST[(store.db)]
    subgraph run[one run]
      direction LR
      WT[worktree] --> IMP[implementer] --> VG[verify gate] --> RR[reviewer] --> M[merge --no-ff]
    end
    FD[FINDINGS.md]
    DR[drawer]
    OP[operator]
  end
  L --> F
  F --> run
  F <--> ST
  SUP <--> ST
  D --> ST
  M --> ST
  ST --> FD
  ST -. status, ledger .-> L
  DR -- /status /runs --> D
  OP -- git push --> GH[origin]
  OP -- file-ticket --> L

Solid arrows carry work. Dotted arrows are projections: the store is written by the loop and the supervisor only, and Linear, FINDINGS.md, the daemon's JSON and the drawer are all views of it. Everything in the box runs on one machine; splitting the drawer or the operator onto a second one is Across machines.

The four kinds, named

Processes

Process Started by Reads Writes Ends when
loop (factory.py TARGET) operator, in a terminal Linear, store, config store, worktrees, main, Linear, FINDINGS.md queue empty, a failed run, or after a self-merge (re-execs)
supervisor (--supervise) the loop, or the operator store store (strikes, releases, heartbeats) SIGTERM; re-execs itself when the factory checkout's HEAD moves
serve daemon (--serve) operator, or a systemd user unit store, read-only nothing never; restart to pick up new code
implementer the loop, per run the worktree, the ticket body the worktree budget or commit
reviewer the loop, per round a staged, read-only export of the candidate its verdict text verdict or timeout
drawer SwiftBar, every 10 s the daemons nothing never

Processes describes each in detail, including how they restart.

The store

One SQLite file per target, WAL mode, ten tables: projects, tickets, runs, review rounds, run events, sweep strikes, supervisor heartbeats, loop restarts, Linear deliveries, interventions. Two state machines live in it as data (TICKET_TRANSITIONS, RUN_PHASE_TRANSITIONS) and every transition goes through one function that refuses edges not in the table. Typed read views in store/read.py are the only SQL the rest of the code sees. Store and state has the tables and the diagrams.

Boundaries

  • The ticket body is frozen at claim time. The merge gate re-reads it and refuses to merge if it drifted.
  • The worktree is where the implementer works; the main checkout is never touched until the merge gate passes.
  • The verify gate is the ticket's own command, run clause by clause, before every review and again before the merge.
  • The review container sees a clean export of the candidate commit, read-only, with no credentials and no host home. It can witness only what is in that tree. See Reviewing.
  • The bind address is the daemon's entire access control: it listens on 127.0.0.1, or on one private-network address, and nowhere else.

Projections

  • Linear state is pushed from the store, one way, last write wins, never read back for status. The ticket body is read back, once, at claim and again at merge.
  • FINDINGS.md is the newest twenty-five entries below a marker, regenerated from runs and reviewRounds at every close-out. Nobody edits it.
  • /status, /runs, /attention are the store as JSON, one read-only connection per request.
  • The drawer is those endpoints as a menu.

What talks to what, and over which channel

From To Channel Direction
loop Linear HTTPS GraphQL, LINEAR_API_KEY outbound
loop implementer subprocess in its own process group local
loop reviewer docker run, staged repo mounted read-only; Codex reaches its backend outbound from inside local + outbound
loop origin nothing. The factory never pushes; the operator does none
supervisor loop only through the store: strikes, releases, loopRestarts local
daemon drawer HTTP on the bind address local, or inbound from a private network
operator Linear --file-ticket against the target's [board] outbound

Why it is shaped this way

Every gate is the fossil of a failure. The roadmap's failure lineage lists the wreck each one bought, from empty verify output to mid-run goalpost edits. The standing decisions that keep the shape small are on the same page: no mechanism without a demonstrated consumer, pluggable by shape rather than by system, tickets as atoms, every failure becomes a mechanical gate, and the factory never pushes.