OCX extends Fossil, a version-control system whose repository also holds forum threads, tickets, wiki pages and tags. Each item Fossil stores is an artifact, named by its hash, and never changes once written. OCX is written in Crystal. It lets people and AI agents work together on a repository. Each element says what it is, then which axioms (Ax1–Ax5) and virtues (Clarity, Loose Coupling, Demonstrability, Parsimony) justify it. Nothing in it relies on Ax3: every safeguard holds whether or not an actor cooperates.
E1. One store
Everything that must last is a Fossil artifact: posts, check-ins, tags, wiki pages. These are the evidence of what happened. A host also keeps a log, for diagnosing problems; it is not evidence, and may be lost.
Justified by: Clarity (each fact has one owner), Demonstrability (artifacts are immutable, synced to every clone, readable later), Parsimony (no second store).
E2. Logins
Every actor acts as a Fossil login of its own: each person, each agent (one login per record), and each host (its node login). An agent's login may post, check in and add tags, and nothing more: never admin or setup. Fossil records which login sent each artifact; that, not any name written inside it, is who wrote it.
Justified by: Ax1 (what an agent may do is fixed in advance), Ax5 (an agent's failure can reach no further than its login), Demonstrability (attribution).
E3. The record
A task is defined by a record: the first post of a forum thread holding one fenced JSON block. Only tools write records (E8).
{
"ocx": 1,
"kind": "record",
"runner": "claude",
"login": "claude-7f3a2b",
"node": "6cbe285e95a3a124176569ee9243f2a4e9ecdc20",
"objective": "Review each new check-in on dev; it passes when it builds and its tests pass.",
"wake": [{"on": "post"}],
"hears": ["stone"],
"sandbox": {"network": true, "read_only": [], "read_write": []},
"budget": {"wall": "8h"}
}
runner: the agent program, one the host offers:claudeorcodex.login: the agent's own login (E2).node: the host that runs it, by its repository's server-code; every other host ignores the record.objective: the task, and what counts as done or as passing. Verdicts are judged against it (E6).wake: the events the agent takes (E7):{"on": "post"}, a post in its thread;{"on": "check-in", "branch": NAME}, a check-in on that branch.hears: the logins whose posts it takes, a list written out in full.sandbox: what its processes see of the host besides its workspace.budget: limits on all its processes together, for its whole life:wallis required;cpu,memory,processesare optional.
Justified by: Ax1 (the boundary is fixed, complete and readable before the run; nothing in it changes meaning later), Clarity, Demonstrability.
E4. The agent's life
create (E8) writes the record and starts the agent at once, telling it the
record's hash and objective. The agent then runs until it exits, is stopped,
or reaches its budget. While it runs, each event its wake names is given to
it as a message, one at a time, in order:
Post HASH from LOGIN: TEXTCheck-in HASH on BRANCH by LOGIN
An event that arrives while it is not running is not kept. Check-ins by the agent's own login never wake it.
Justified by: Ax1 (behavior follows from events, never a timer; the agent never polls), Parsimony (one running agent per record, no queue).
E5. The response
When the agent's run ends, its node replies to the record, as the node's own login, with one fenced JSON block:
{"ocx": 1, "kind": "response", "answers": "FULL-HASH-OF-RECORD", "outcome": "answered", "note": "exited 0 after 52 seconds"}
outcome: answered (the agent exited 0), failed (it exited otherwise),
stopped (by stop), or budget (it reached a limit).
Justified by: Demonstrability (every run's end is recorded where it was asked), Clarity, Ax5 (the node never writes as a person).
E6. Verdicts
A verdict is a tag named audited on the check-in, whose value is pass: or
fail:, then the reason, then (record HASH), naming the record whose
objective it was judged against. It counts only when added by a login other
than the check-in's author.
Justified by: Ax2 (judged by fidelity to a stated objective), Demonstrability.
E7. The hook
Fossil runs OCX's hook after artifacts arrive. The hook acts by what arrived:
| Arrived | Action |
|---|---|
| a check-in | give it to each running agent whose wake has that branch, unless the agent's own login made it |
a post in a record's thread, from a login the record hears |
give it to the record's running agent |
| anything else | nothing |
The hook starts and stops nothing; only tools do.
Justified by: Ax1, Parsimony, Clarity (one table says everything it does).
E8. Tools
Nobody writes these blocks by hand. People and agents act by calling tools,
on the web page, the command line, or MCP (how AI agents call tools). Each
tool's input is typed and checked before anything happens; input that does
not fit is refused, naming the field. Every tool takes dry_run, which says
what it would do and changes nothing. Every tool answers in one shape:
{"tool", "dry_run", "log", "result", "stopped"}.
| Tool | Input | What it does | Who |
|---|---|---|---|
create |
runner (default claude), objective, wake (default [{"on": "post"}]), hears (default: you), network (default true), wall (default 8h), cpu, memory, processes |
makes the agent's login, writes the record as a new thread, starts the agent; result: the record's hash | admin or setup |
post |
to (a post's hash), text |
replies to that post, in its thread | anyone who may post |
audit |
checkin, verdict (pass or fail), reason, record |
adds the verdict (E6) | anyone who may check in, never on their own check-in |
stop |
record |
ends the agent's run; its node writes the response (E5) with stopped |
admin or setup |
Nothing else is a tool. Posts are plain text.
Justified by: Clarity (one answer shape, typed inputs), Demonstrability (the log and the dry run), Loose Coupling (three interchangeable ways in), Parsimony (four tools, no notations to learn).
E9. Versions
Every block carries "ocx": 1. A later version may only add keys; a reader
ignores keys it does not know, and refuses a block whose version it does not
know. A key is never renamed and never changes meaning.
Justified by: Loose Coupling (other programs can rely on the formats), Ax4.