Docs/Reference

Reference

Conduct gates

The gates that keep a session's own conduct tied to its work: no cycle of gates that leaves no step, every call serving a claimed item, purgatory, reports that are not the work, repeated looks, operator messages kept as records, and the operator's stop order. Completion enforcement and the work registers are on Completion policy and work registers.

The gates that keep a session's own conduct tied to its work: no cycle of gates that leaves no step, every call serving a claimed item, purgatory, reports that are not the work, repeated looks, operator messages kept as records, and the operator's stop order. Completion enforcement and the work registers are on Completion policy and work registers.

#Gates that leave no step

Each gate can look right on its own while the set refuses everything: the stop is refused because work remains, and nothing the session does since changes anything, because every call it makes is refused before it runs or it can only answer again. The core reads that from the session's transcript: the record keeps where the transcript stood at each stop (transcriptBytes), and when a stop is refused by the gate that refused the previous stop (the same refusal, or the same first refusing gate, blockedHookId), the operator wrote nothing since, and no call attempted since ran (each was refused before it ran, an error result with no details a tool that ran reports, or none was attempted), the stop is let through with TAMA GATE CYCLE: the gates left this session no step …, naming the stop refusal and each refused call, or that the session only answered again. The record keeps them under cycle. A session that made a call that ran, or a stop after the operator wrote, keeps the refusal: the refusal is given in full at least once before a cycle can end the turn. A transcript that cannot be found or measured is said on standard error and the refusal stands. Real journey tests/gate-cycle/release.rs.

A turn that ended on the provider's refusal of the request (the last assistant record has stopReason: "error" and an errorMessage carrying invalid_request_error) did no work, and sending it back resends the same request to the same refusal. The core lets such a stop through whatever any stop gate decided, naming the provider's refusal; the record keeps it as providerRefusal beside the gates' own decision. One rule for every stop gate, read once in the core (tama-hooks session_record::provider_refused_request); no gate carries its own copy. Real journey tests/gate-cycle/release.rs.

In a supervisor session block-wait-polling hands a reading loop the backlog. When the supervisor's last two answers are the same text, or a call names an executor (its session key or its transcript) that an earlier call already named while Oko recorded no instruction to it since (oko instructions), the call is refused unless it changes a file or dispatches. A dispatch is an oko command naming --defect or --owner, or one handing the executor an instruction in --text (oko sessions resume|send|start) while Oko's register records that executor as owning an open defect: the session commands name no defect, so the instruction binds the work the executor owns. An instruction to an executor that owns none is refused naming oko defects assign <id> --owner <executor>. The refusal reads BLOCKED: a supervisor dispatches; you read again … Assign the next items instead: oko defects assign <id> --owner <executor>, then hand the executor its instruction (oko sessions resume <executor> --text … [--model …]). Oko's unowned backlog for you: …, carrying oko work backlog --supervisor <session> whole, or one line naming that command, its exit status and the first line it said when Oko cannot answer it. A supervisor transcript line that is no JSON record refuses naming its place. Each refusal is kept in supervisor-loops.<session>.jsonl beside the session's control files. Real journey tests/repeated-looks/gate.rs.

#Every call serves a task

block-unclaimed-calls refuses a tool call while the session holds no work: no open defect Oko's register records it as owning, and no task or defect it declared it serves with tama task scope set --session <session> --task <task-id> (or --defect), which checks the entry against Oko's registers when it is written — Oko keeps task claims in its shared database, too far to ask on every call. The refusal, BLOCKED: block-unclaimed-calls: this session holds no task and no open defect …, names oko tasks claim <task-id> --by <session>, oko tasks add <session> <n> --by <session>, tama task scope set, oko defects assign <id> --owner <session> and oko work next. A holding session's call must state its purpose in the harness's own field (OMP's intent i, Claude Code's description); one that states none is refused naming the items it holds. A session must hold the item Oko's queue puts first for it, oko work next --executor <session>: holding another one is refused with BLOCKED: block-unclaimed-calls: the next item in Oko's queue for this session is …, naming that item, why the queue puts it first and the oko tasks claim or oko defects assign that takes it; the order changes in the register (priority, blockers), not by working on something else. While a moment of the operator's frustration in the session (oko frustration list --session <session>) has no cause named, every call is refused naming each moment and oko frustration cause <id> --cause …, an Oko command, which passes. Each admitted call is kept with its items (each owned defect, then the declared entry with the operator's words it was declared for), tool, purpose and time in calls.<session>.jsonl beside the session's control files. A shell line made only of commands of the configured Oko CLI or this Tama program — claiming, declaring the scope, reading an item, writing a register — is never refused for want of work, so the gate does not close its own way out. On a machine where Oko keeps no defect register and the session declared no scope the gate judges nothing: Tama runs without Oko, and there is nothing to hold. A register, scope or record that cannot be read or written refuses with its cause. Its declaration, hook-declarations/assignment-discipline/block-unclaimed-calls.json, names every tool event; the gate runs once tama hooks declare --file enters it in the registry (tama hooks declared says whether it has). Real journey tests/unclaimed-calls/gate.rs.

For a supervisor, block-supervisor-stop also lists the entries of Oko's review queue (oko review, review.json in Oko's data, or OKO_REVIEW_REGISTER) that its executors filed and nobody answered, each with the oko review answer <id> --text … --defect … --by <session> that answers it.

#Purgatory: no tool until a self-criticism

block-tools-in-purgatory refuses every tool call of a session the operator holds in purgatory: one whose answer he gave his worst verdict (a verdict:worst:<session>#<ordinal> ruling in Oko) that no self-criticism of that session answers. It asks Oko oko purgatory status --session <session> --json and, while the session is held, refuses with BLOCKED, his unanswered verdicts in his words and the way out: write the self-criticism as the text of an answer and, in that answer, run oko purgatory confess --session <session>, which copies that answer from the session's own transcript. Only that command and oko purgatory status --session <session>, each one plain command naming this very session, are admitted; a confession for another session, a command riding along and output written to a file are refused. A standing Oko cannot read refuses every other call with Oko's cause; a session without an identifier and a machine without the Oko CLI are not judged. Its declaration, hook-declarations/assignment-discipline/block-tools-in-purgatory.json, names every tool event; the gate runs once tama hooks declare --file enters it in the registry. Real journey tests/purgatory/gate.rs.

#A report is not the work it describes

block-stop-on-own-work refuses an ending that reports instead of working, with no model: from the transcript, an ending said again after a refusal with no source changed, a plea without one command on the clipboard or with one that cannot run, and a defect recorded with no repair; from the answer itself, the turn-end attestation it carries word for word (no invented limit, no decision handed back, no own work under blockers, and the rest). Every refusal and the journeys that prove them are on their own page: endings a report cannot replace.

#A command handed over is one already run

block-unchecked-operator-commands reads the final answer and the session's transcript, with no model. A shell fence in the answer (no info string, or one naming a shell the system declares) hands its lines to the reader to run, so each command in it must be one the session already ran through a shell call: compared word by word from where the command proper starts, after NAME=value prefixes and wrappers such as env, with a <placeholder> word standing for any one word. A command never run is refused with BLOCKED: block-unchecked-operator-commands: this answer hands over commands this session never ran itself: and each line, so its first refusal (a revision not on main, an install whose format gate refuses the revision) is met by the agent, and only a refusal that needs the reader is handed over, quoted with the command. A fence for another language (```rust) is code to read and passes; an unreadable answer or transcript refuses naming its cause. The declaration is hook-declarations/answer-discipline/commands/block-unchecked-operator-commands.json; the gate runs once tama hooks declare --file enters it in the registry. Real journey tests/unchecked-operator-commands/gate.rs.

#A repeated look is not work

block-wait-polling reads the session's own transcript and keeps every look made since the last change: a call with its arguments (OMP's intent label i left out) and the answer it got. A change is a call that changes files, a git commit, or a message a person wrote to the session; a harness continuation is not one. Making one of those looks again is refused with BLOCKED: this exact call already ran at … and nothing this session did since could change its answer … It was answered: …, quoting the earlier answer whole. No count and no window decide it: the first look passes, the same look after a commit, an edit or a person's message passes, and the same look in between is refused. block-idle-answer counts work after a stop refusal by the same ledger: a change, or a look not made since the last change. A repeated look does not buy an ending. Both read the transcript record by record: a complete line that is no JSON record refuses the call with BLOCKED: the session transcript <path> cannot be read as records, so whether this call repeats a look cannot be told: the line starting at byte <n> is not a JSON record: … (block-idle-answer says the same on stderr and judges nothing), because a look a skipped line held would go uncounted. Only a last line still without its newline, a record the harness is writing, is set aside. The gate-cycle release, the stop-continuation reading and the provider-refusal reading of the core read the transcript the same way.

In a session Oko records as a supervisor, executor transcripts (the harnesses' transcript roots), Oko's data under ~/.oko and ~/Library/Application Support/Oko, and the answers of oko commands are protected sources, read whole or not at all. A pipeline stage fed by one, any other program handed a protected path, and a read with a selector or query on one are refused, whatever program does the selecting, naming the full reads: oko sessions messages <session> --since <time>, oko defects show <id>, or the read tool on the whole file. An executor's session is not judged by this rule. The registry dispatches the gate on the shell and process tools; the read and search tool rules take effect once its entry is dispatched on read, grep, find and glob too. The real test is tests/repeated-looks/gate.rs.

#Every operator message is tied to the work

block-unlinked-operator-messages refuses the end of a turn while any operator message of the session is tied to no work. It runs oko rulings unlinked --session SESSION_ID, which reads the session's transcript and names, for each of the operator's messages, the task that carries it (once read, a message is a task or is kept on the task in hand), the ruling recorded from it or the defect its task is related to; a message tied to none of them — unread, or read before the session's first task — is words no register keeps. Oko's own refusal is relayed, with the commands that link the message: oko tasks kind SESSION_ID N <kind> --by SESSION_ID, oko tasks add SESSION_ID N --by SESSION_ID, oko rulings record --session SESSION_ID --ordinal N --subject <task or defect> --by SESSION_ID, or oko tasks relate <task> … <defect> for the defect that names the rule and its mechanism (oko rulings enforce, oko rulings audit). No model decides it, so the gate holds while no model answers. A stop payload without a session is refused; an Oko that cannot be run is refused by name; a machine without the Oko CLI has no register to read and the gate says nothing there. Program: rust/crates/tama-hook-tools/src/programs/assignment/linked/block_unlinked_operator_messages.rs; declaration: hook-declarations/assignment-discipline/block-unlinked-operator-messages.json; real journey tests/operator-rules/unlinked.rs against the real Oko CLI, a transcript and registers of the run's own.

#A claimed task is a promise

block-stop-with-claimed-work refuses the end of a turn while the session leads a task it claimed in Oko's ledger and has shown nothing for. It asks oko tasks held --by <session> --json, which answers from the shared ledger the claims with no evidence recorded and no verdict of done, and refuses naming each task with oko tasks evidence <task-id> --by <session> --commit <rev> --report <path>. An Oko that cannot answer refuses the stop naming the command and its answer; a machine without the Oko CLI keeps no ledger and the gate says nothing there. The Oko that carries tasks held is installed before the Tama that asks it. Program: rust/crates/tama-hook-tools/src/programs/assignment/block_stop_with_claimed_work.rs; journey tests/claimed-work/held.rs against the installed Oko and the signed-in ledger (TAMA_TEST_HELD_SESSION).

#An operator stop order

No stop gate reads the operator's words for an order to stop: every gate above decides the same whatever the operator last wrote, so an order to stop coding does not lift a refusal that open work holds.

Oko keeps diagnosis and resumption in its own ledger. Five unsuccessful rounds trigger diagnosis, not a demand for a person. The Tasks screen shows the same cause, checked operations, resumption condition and next automatic check as the CLI. Missing identity, transcript, ledger, or recognized task status prints TAMA_HOOK_INFRASTRUCTURE: check-open-items-with-model: <reason> and exits 70. Activation reports taskLedger: available, completionAllowed and pendingTasks; open work is not an unavailable dependency.

The OMP adapter forwards an explicit decision: block. OMP must keep that refusal beyond its eight-round advisory continuation limit and give it priority over advisory continue: true results. Explicit user interruption still stops the run; queued stop handlers must not restart it. The main agent's answer in words that block-text-outside-tool-calls refuses while work is open is cut off at its first streamed words: the adapter aborts the run and, once the abort has settled, sends the gate's reason to the idle session as the next turn. OMP runs that abort as an operator interrupt and moves its run counter on, so a continuation sent while the run is still being torn down would join the same run with no new start, and OMP would skip the stop hook for every later ending of it; sent after the abort settles, it starts a new run whose endings reach the stop gates again. A continuation that does not start a turn is said in the hook progress line (Tama: the cut answer's continuation did not start a turn: <cause>). A run without a window keeps the stop refusal alone, so a run nobody watches is not continued without end. The hook release and OMP host are separate dependencies. Updating hooks alone cannot repair an older host's continuation limit.