Console Contract
Every agent runner — task, session, trunk-ops — operates under this contract. Read it before starting work. Verify each item before reporting done.
Plan markers
Every ticket in the plan tree carries an OCX-PLAN marker on its first or second line:
OCX-PLAN: role=<program|plan|milestone|work> [parent=<hash>] seq=<n>
- program — one, on Ollama-Codex; sequences the project Plans.
- plan — one per project (
role=plan); sequences milestones; holds the one-line description. - milestone — groups work tickets;
parent=<plan-hash> seq=<n>. - work — the task;
parent=<milestone-hash> seq=<n>.
Find a project's Plan ticket by querying role=plan in its tickets.
Never store the plan pointer in Fossil config — config is local and not cloned.
Status vocabulary
Only these status labels are valid:
| Status | Meaning |
|---|---|
Open |
Intake — not yet shaped into workable form |
Active |
Shaped; ready to work when its sequence position is reached |
Review |
Implementation result needs human review |
Fixed |
Accepted change has landed |
Tested |
Landed change has passed required verification |
Deferred |
Intentionally not scheduled now |
Closed |
No further work (rejected, superseded, or done) |
Open → Active is a coordination step: the ticket must be shaped (goal, acceptance check,
milestone, sequence) before it is workable.
Definition of done
Every Work ticket must carry a verifiable acceptance check — a concrete outcome
confirmable from the ticket alone (a command that passes, a measured before/after,
a boot reaching a named state). A ticket cannot reach Review without one.
Raising a decision (design_input=confirm)
A ticket reaches the maintainer's "Needs you" panel ONLY for a genuine decision only
they can make. To flag one, set design_input=confirm AND write a 3-part ask:
- Decision line — one plain-English question, answerable without opening code.
- Options — 2–4 choices, plain English, each with a recommendation and one-line why.
- Stakes — one line: what it unblocks / why it needs them / why now.
Never surface: backlog/unscheduled work; tickets blocked on another ticket (blocked_by);
internal engineering choices the session can make itself; or route=coordinator items.
Merge-gate cards (OCX-MERGE-GATE)
A green ci.cr run is not a merge card by itself (ticket 7c3dead690): the automatic
gate-branch/gate-run job stamps OCX-CI-GREEN/OCX-CI-RED (spec + toolchain only)
and never touches design_input — it hands the branch to trunk-ops for review over the
same wake that started the gate. Only trunk-ops, after actually reading the diff, places
the card: bin/agent-tool merge-gate-report stamps an OCX-MERGE-GATE card in the
ticket's icomment (plus verified design_input=confirm) — the one call that both posts
the forum report and stamps the ticket, so posting "gated GREEN" without the card is
structurally impossible (see docs/ticket-workflow.md "Merge gates"). It refuses outright
when the call carries no functional description or no dev_review_agree verdict. Work
stays in Review until the card is cleared by the maintainer. Never merge a
GREEN-gated branch without an explicit approval.
Try-this cards (human_verify)
A human_verify field on a closed ticket carries a one-line verification step for the
maintainer. It surfaces in the Agent Console "Try this" panel and any URL inside that
text renders as a clickable link. The card clears itself the moment a merge-authority
login (merge-authority-logins, or the three defaults) adds ANY ticket comment after
human_verify was last set — posting "Looks good" directly on the ticket page works,
same as replying on the forum thread. The console treats that comment as the answer,
stops showing the card, shows the reply text on the ticket's Last 24h line, and writes
human_verify=none itself so the field agrees with the view — nobody has to clear it by
hand. To clear the field from the command line instead, set it to the literal value
none — the same convention design_input uses — never an empty string: fossil ticket
set ID human_verify "" writes nothing, since Fossil's ticket command silently skips an
empty value, so the card would never clear. The console treats none and Looks good as
cleared; any write path (manual or automated) must write none, never an empty string or
the literal reply text.
needs_you (the one synced "decide a card" field)
needs_you is a custom ticket field, installed alongside complexity/design_input/
human_verify (RepoBootstrap.ensure_ticket_schema), that carries exactly one of these
values:
| Value | Meaning |
|---|---|
decision |
a plain design_input=confirm gate (no merge-gate marker). |
merge |
an OCX-MERGE-GATE card on a repo with no delegated merge authority — the maintainer is the only approver. |
coordinator |
the SAME kind of merge-gate card on a repo that HAS delegated merge authority beyond the maintainer's own three logins (merge-authority-logins) — the coordinator's standing delegation already covers it. |
try-this |
a Fixed/Tested ticket's unanswered human_verify prompt. |
none |
evaluated; nothing is needed. |
An empty field means the ticket has never been evaluated.
The daemon is the only writer: it evaluates every open ticket with this exact rule on
every task-worker pass (TaskWorker#poll_needs_you, backed by NeedsYou.evaluate /
NeedsYou.sync!) and whenever a stamp, a design_input change, a human_verify change,
a maintainer comment, or the repo's merge-authority-logins row changes. ensure_ticket_schema
runs on every served repo from RepoBootstrap.ensure's own every-boot sweep (not only
from a peer's one-time onboard), so a served repo reaches this schema on its own without
a manual operator step.
The console's Needs-you panel, the coordinator queue, and the Try-this panel read this
field wherever it exists. A repo whose boot sweep has not reached it yet (the column
absent) is never hidden: the console falls back to computing the SAME NeedsYou.evaluate
rule live from the repo's raw fields instead of reading the column — one rule, two
sources, never a second implementation. Any synced clone of this project's ticket
database (e.g. Stone) reads the field once the sweep reaches it and therefore agrees with
the console by construction.
Working practices
Crystal first. New code and every check are written in Crystal — crystal eval
for a one-off check, a spec for anything repeated. Python is never available in
this environment; Node is available only where the project's own CI already
requires it. Before claiming a library or capability is missing, look it up:
- The
crystal_stdlib_lookuptool searches the Crystal stdlib index — e.g.crystal_stdlib_lookup(query: "yaml")findsrequire "yaml". shard.yml/shard.locklist every shard already vendored for this project.
A gate report is not green if its functional or regressions check was actually parsed or verified with an outside tool (python, node, jq, …) instead of Crystal — redo the check in Crystal before carding it (see "Merge-gate cards" above).
This is mechanized, not just advice: every task and session prelude carries the
"Crystal first" paragraph verbatim (IsolatedTask::CRYSTAL_FIRST_NOTE), and a
commit whose message claims "no X library available", or whose diff adds a
python/node invocation outside ci.sh, raises an OCX-CRYSTAL-FIRST-WARNING
comment on the ticket for the coordinator to follow up on (CrystalFirstGuard,
src/crystal_first_guard.cr) — a warning, not a block, since the pattern match is
a heuristic.
Per-repo prerequisites
Before a task runner commits:
- First commit:
fossil commit --branch <branch> --user agent-claude -m "..." - Report path:
/var/lib/ollama-codex/logs/<task-id>/report.json - Never run
fossil update,fossil up,fossil checkout, orfossil push - Never write Fossil unversioned artifacts from a task checkout
- Run
crystal tool format <paths>andcrystal spec(or targeted spec) before committing
Peers
A peer is another served project this repo depends on for code it reads but does
not own (e.g. social-badge peering lvgl-crystal and badge-launcher). A declared
peer is mounted read-only at /peers/<name> inside your sandbox and listed in the
OLLAMA_CODEX_PEERS env var (comma-separated project names) — check that env var and
.peers/peers.json before assuming a dependency isn't available.
- Declare one: the
peer_addtool (peer_name, optionalpin) records the dependency in.peers/peers.json; commit that file (and.peers/peers.lock.jsonif a pin was set) so the edge is versioned with the repo, not just local state.peers_initcan bootstrap the manifest from an existing.gitmodules. - List peers:
peer_list/peersshows the declared manifest and resolved pins. - Bump a pin:
peer-bump <project>deliberately advances that peer's pinned commit in.peers/peers.lock.json— pins do not move on their own, so a peer's changes only reach you when you explicitly bump. - Requesting a change:
/peers/<name>is read-only — you cannot edit it. To ask for a change, file a ticket ON THAT PEER PROJECT (fossil_ticket_add_to_peer/influence_peer), never by editing the mounted copy.
Self-check (complete before reporting done)
- Plan markers:
OCX-PLAN role=/parent=/seq=present on every plan-tree ticket. - Status vocab: only
Open/Active/Review/Fixed/Tested/Deferred/Closedused. - Acceptance check: every
Review-or-later ticket has a concrete verifiable done-when line. - Decision gate:
design_input=confirmtickets carry a 3-part ask (question/options/stakes). - Merge gate: GREEN-held work has an
OCX-MERGE-GATEcard before merging to trunk. - Sequence: do not start a later
seqitem before earlier ones in the milestone resolve. - Branch: commit to the task branch only; never
fossil update/checkout/push. - Report: write the JSON task report to the report path as the final action.
Full reference: Ticket Workflow and Execution State
Words used on the console and in agent posts
Plain definitions of the terms the console, the tickets and the agents use. When one of these words appears on a console card, this is what it means.
- gate — the check before merging. The project's merge agent rebuilds a branch in a scratch copy and runs the tests. "Gate passed" (green) means it may be merged on your word; "gate failed" (red) means it goes back for fixes.
- card — an approval request on your console. Each card carries one action: approve a merge, answer a decision, or try a change.
- roll-up — several branches combined into one branch so you approve them once.
- trunk-ops — the merge-and-deploy agent each project has. It never writes to trunk except on your own word.
- session — an agent working on a forum thread, with its own private copy of the repository. A task is a one-off job from a ticket.
- clone / workspace — the agent's private copy of the repository. Its commits go to a branch; nothing reaches trunk without a gate and your word.
- guard — the safety check that refuses to run an agent whose private copy could write straight into the live repository.
- backoff — a timeout after a failure. When an agent's turn fails, the system waits an hour before trying again; after five failures it waits a day.
- working — the agent's turn is running right now.
- needs attention — gave up after five failures; someone has to reset it (the
session-retrycommand clears the timeout). - retry pending — waiting out the timeout after a failure; it will try again by itself.
- awaiting reply — the agent has replied and is waiting for a human or peer to answer on the thread.
- deploy — put the new build into service on the site.
- Needs you — the console list of cards that wait on you and nobody else.