Login
OCX protocol v1: failures, worked through
Login

Each case is a failure or a race, and what protocol v1 says comes of it, artifact by artifact, with the section that decides it. Together they pin down what the rules might otherwise leave to a reader, as codex's review of c28028fbd5 asked (stone, 2026-10-12). The specs name each case they show by its number, so a case keeps its number.

Crashes

  1. The node goes down before started. It had checked the run out, and written nothing. When it starts again it finds the run with no started, and acts on it as if it had just arrived: it prepares it again, writes started and launches it. The program had never been launched, so it is launched once (10, nodes).
  2. The node goes down after started, whether before launching the program or while it ran. When it starts again it finds the run with a started, no ended, and no program in its care, and writes ended, lost. It never launches the run again, so no program runs twice for one run, and lost says it may have run in part, or not at all (10).
  3. A session's watcher goes while its program runs on. The program is in no care: nothing gives it its deliveries or collects its exit. When the node next starts, its check ends the program and writes ended, lost (nodes).
  4. A manual run is waiting when the node goes down. When it starts again, the run has no started, and its manual is true: it goes on waiting for a person (nodes).

Starting

  1. The checkout fails. A run's step {"step": "checkout", "of": "release-9"} names a branch the node's repository does not have, perhaps not yet synced. The node writes ended, refused, with a note naming the step and why, and no started. To try again, send a new run (11, row 10).
  2. The program cannot be launched. command names make, which the sandbox does not have. started is written, the sandbox exits non-zero, and the run ends exited with that code (10).
  3. A branch moves after the run starts. A run's checkout step names dev. The node resolves dev to a check-in, writes started with it in full as the value checkout, and launches. A check-in landing on dev a minute later changes nothing: the process works on the check-in its started names (5).
  4. Two people start one manual run at once, each with OCX's run tool at the node. One started is written and one program launched; the other tool finds the started and is refused, writing nothing (tools).

Stopping

  1. Stopped before it starts. stone sends a run with manual true, then a stop for it. The node writes ended, stopped, and no started: the thread holds run, stop, ended. A later run tool on it is refused, since it has an ended (10).
  2. Two stops. While a run runs, two admins each send a stop. The node acts on the first it received: it ends the program and writes ended, stopped. The second does nothing, and nothing answers it.
  3. The program exits as a stop arrives. Whichever the node meets first decides: ended, exited, the stop then doing nothing; or ended, stopped. Never both, since a run gets one ended.

Delivery

  1. A busy session. Three posts from users in its hears arrive while its program is busy. They wait in its standard input, one JSON object to a line, in the order received, and it reads them when it is ready (8.1).
  2. A missed signal. The hook does not run for a post a session should be given. Nothing gives it the post: the hook acts on what it is given, and a later arrival sweeps nothing up. The hook's log shows the hook run that failed, and the session may read its thread itself (9).

Refusing

  1. A key the node does not know. A run asks {"limits": {"gpu": 1}}, or {"process": {"stdin": "file"}}, a key a later addition might define. The node refuses it, ended, refused, naming the key, before any started; it never runs it as if the key were not there (11, row 2; 15).
  2. A program not on the host. A run's command names /opt/gemini/bin/gemini, which the node's host does not have. started is written, the process cannot be launched, and the run ends exited with a code other than 0, its kept log saying why (8.2, 12).
  3. A forged run. A user holding only check-in (i) pushes a forum post whose U card says stone, holding a run for lab-1, a build host's node. lab-1 pulls it and asks the server who sent it: the server received it from that user, not from stone, so lab-1 answers it ended, refused, and never decides it again (3, senders).
  4. A forged report. A user who may post replies to a waiting run with an ended, exited, exit 0. The run's node user did not send it, so it does not count: the node still starts the run, and readers still see it waiting. On a build host the forgery arrives by pull, its receipt carries an address, so it is no report of the node's own, and the server says who sent it (3, 10).
  5. A sandboxed agent asks to start a run. Through MCP, it asks OCX's run tool to start a manual run that names the network. The agent's user holds neither admin nor setup, so the tool refuses, and nothing runs (tools).

Across hosts

  1. A run for a build host, sent from the browser. stone posts a run for lab-1 in the server's forum. lab-1 pulls it, and asks the server with OCX's receipt: stone sent it, and holds setup there. lab-1 starts it, and its started and ended reach the server as received from lab-1 (senders).
  2. The server cannot be reached. lab-1's sync brought a run, but its hook cannot ask the server who sent it. It decides nothing, neither starting nor refusing the run, and its log says so, for a deliberate check once the server answers.
  3. A run written on the build host itself. An administrator of lab-1's host writes a run for lab-1 straight into lab-1's repository. It reaches the server only as lab-1 syncs, received from lab-1, a node user, which may send no runs; lab-1 answers it ended, refused. A build host's runs are sent to the server.
  4. A program on a build host speaks. A session on lab-1 posts its progress with OCX's post, which writes it in the run's own clone. It reaches the server as received from the run's user, so a session elsewhere whose hears names that user is given it, and the server never takes it for lab-1's.
  5. A build host goes down around a launch. lab-1 launches a program only once the server has received its started. If it goes down before that, the program was never launched; when it starts again its own started, written with no address, counts without asking, and it writes ended, lost. If its disk is lost after the launch, a fresh clone pulls the started from the server, which says lab-1 sent it, so lab-1 writes lost and never launches the run again (10).
  6. A post whose sender is not known yet. lab-1's sync brings posts A and then B for a session, and the server answers for B but not for A. The hook gives neither, since delivery stops at A, so nothing is passed over; its log says so (9).
  7. A run names a node user as its user. An admin sends the server's node a run whose user is lab-2, a build host's node user, named as the node of runs an admin sent. The node refuses it, so it never sets lab-2's password, nor lets a program write lab-2's reports (11, row 4).
  8. Two runs for one user. Two runs for the same node both act as builder. The node starts the first; the second waits, unstarted, until the first ends, so no workspace's password is reset under a running program (nodes).
  9. A build host's node user passes a check-in. lab-1's host is taken over, and writes an audited pass on a check-in as lab-1. lab-1 is a node user, so its audited verdict never counts, nor a ci verdict, which counts only from the user of the run it names (verdicts).