Command reference
oko defects operations bind
Block the functionality an open defect breaks, until the defect is fixed or withdrawn, for every session but the defect's owner while it is repairing that defect.
Invocation#
oko defects operations bind DEFECT (--command "PROGRAM WORDS" | --tool NAME) --cause TEXT [--json]Inputs and options#
- A functionality is what an open defect breaks, named once for every session: a command path, the program and its command words as its CLI spells them (--command "oko asks ask"), or a harness tool that is not a shell (--tool NAME). A shell call reaches a command path when one of its commands runs that program, by name and from any directory, and its words that are not options begin with the command words, in order. Options are set aside wherever they stand. A program whose global options are declared (Oko's defects/operations/grammar/grammars.json, each with the page it is taken from) is read by that grammar first: its global options that take a value in the next word step over that word, so it is never a command word, and its subcommand is the first word after them. A program the declaration marks as a wrapper (`wraps`, below) is stepped over with its options and the assignments after it, so the program it runs is the one read: env -u X HOME=/h git fetch reaches git fetch. That declaration is the only one: Oko publishes it verbatim beside the register as <register>.grammars.json (defects.grammars.json) at every write of the register and at oko defects snapshot, and Tama's admission reads that file, so both read a call alike; Tama refuses a call that may run a bound path when the register exists and the published declaration is missing or unreadable, naming the file and oko defects snapshot. git (git(1): -C, -c, --git-dir, --work-tree, --namespace, --config-env) and cargo (cargo(1): --explain, --color, -C, --config, -Z) are declared; any other program is read with its options set aside. --help is read like any other argument, since it can be an option's value or a directory: a call that runs the path with it reaches the path, while the program's own help (git --help, git help fetch) is another path. oko asks ask --from x --question y, oko asks ask --help, ~/.stado/bin/oko asks ask and true && oko asks ask all reach oko asks ask; git -C <repository> fetch, git -C --help fetch and git -c fetch.prune=true fetch reach git fetch; cargo --color always build reaches cargo build; oko asks list, git --help, git help fetch, git log --grep fetch, git -C fetch status, git --no-pager log fetch and cargo --color build check do not. oko defects operations check reads a call the same way Tama's admission does.
- A program the declaration marks as a wrapper (env, from GNU coreutils' env manual) runs the program after its options, so the call is read from that program by the role the declaration gives each option: a value option's value is stepped over (-C, --chdir, -P, -a, --argv0), an unset option's value is a name the program's environment loses (-u, --unset), a clear option starts that environment empty (-i, --ignore-environment, -), whether the value stands in the next word, after `=`, or in the rest of a bundle of short options (`-uHOME`), and `--` ends the options. With assignmentOperands, every operand holding `=` before the program sets a variable whatever its name, since env accepts any name without `=` or NUL, the empty one included: env 'A-B=x' git fetch and env =x git fetch reach git fetch. Its split-string options (-S, --split-string) are not values set aside: their value is split into words by env's own rules (env.c build_argv: only space, tab, newline, vertical tab, form feed and carriage return separate outside quotes; single and double quotes group, and an apostrophe inside double quotes is a character; the manual's escapes; `\c` and a leading `#` end the string; `${NAME}` adds a present variable's value, an empty one included, and nothing at all, not even an empty word, for an absent one) and read in their place, so env -S "git fetch", env --split-string='git fetch', env -vS'git fetch', env -S "git fetch \"it's\"" and env -i env -S '${ABSENT} git fetch' reach git fetch, while env -i EMPTY= env -S '"${EMPTY}" git fetch' runs an empty program; a split string env refuses, such as `$NAME` without braces, runs nothing. `${NAME}` expands in the environment the executed env was started with, which is the one the call builds: the shell's assignments before the command (a name of letters, digits and underscores not beginning with a digit) and every outer env's -i, -u and assignments over the inspecting process's own, so FOO=git env -S '${FOO} fetch' and env FOO=git env -S '${FOO} fetch' reach git fetch, while FOO=git env -u FOO env -S '${FOO}k fetch' does not. Where the shell itself expands (a `$` or backtick unquoted or in double quotes, not escaped), the value is one only the running shell knows: a split string expanding a variable set so (FOO=$GIT, a command substitution) is refused by Tama's admission, by oko defects operations check and by the native check with "which program this call runs cannot be read" naming the variable, never read as running nothing or anything. A single-quoted or escaped `$` is a character: FOO='$GIT' sets the four characters $GIT, so FOO='$GIT' env -S '${FOO} fetch' is a string env refuses and runs nothing. Each command's words are read as the shell hands them to the program, quotes removed, so git "fetch" and git -C "/path with spaces" fetch reach git fetch, and the command line handed to a shell after -c is read the same way. Oko's check and Tama's admission read a call by these same rules from the one published declaration; a call whose first program is no declared wrapper is read from its own words, without copying them.
- Oko's check, the native check and Tama's admission read a whole call by one walk (Oko's defects/operations/grammar/reading, which Tama carries as its command_path/reading): the call is parsed by the bash grammar (tree-sitter-bash), and every simple command its syntax tree holds is one command, wherever it stands — in a list, a pipeline, a subshell, a group, an if, while or case body, or a command or process substitution at any depth, inside double quotes or a here-document included. Comments, reserved words and operators are syntax, never a command or a word of one: printf ok # ; git fetch runs no Git, and 'if' fetch runs a program named if. A command's words are the assignments written before its name, its name and its arguments, in order; redirections are not words. A line the grammar cannot parse (a quote never closed) is refused naming it. A program /etc/shells declares a shell runs its command string, found by the invocation grammar grammars.json declares for it (sh, dash, bash, zsh, ksh: options first, short bundles letter by letter, long ones whole, -o, +o, -O, --rcfile and --init-file stepping over their value, -- or - ending them, and with -c the first operand is the command string), parsed the same way in turn; a shell the machine declares with no declared grammar is refused naming it. So true && git fetch, (git fetch), true && { git fetch; }, if git fetch; then true; fi, echo "$(git fetch)", sh -c 'git fetch' and bash --norc -c 'git fetch' reach git fetch in all three. Each word follows the Open Group's Quoting rules: inside double quotes a backslash removes itself before $, `, ", \ and a newline and stays before anything else, so FOO=git env -S "\${FOO} fetch" reaches git fetch. A word only the running shell knows is neither a match nor a miss: a program ($GIT fetch), a word standing where a command word is compared (git $SUB), a split string the shell computes (env -S "$CMD") and a shell's command line the shell computes (sh -c "$CMD") are refused with "cannot be read" and what was unread. Every character of a split word remembers the expansion it came from, the value it expanded to and the whole environment it expanded in, so expanding a variable to the same value in the same environment from text that state produced (X='-S${X}' env -S '${X}') is refused as splitting without end, while the same name rebound by a later env (X='-S${X}' env -S 'X=git env ${X} fetch', which runs git fetch), the same value where a later env changed another variable it expands (X='-S${Y}' with a later Y=git), and two finite split options written alike (env -S 'printf x' -S 'printf x') are read as written. env -S 'printf %s x;git fetch' runs printf with a literal semicolon, not a second command. A command whose first program is no declared wrapper is read from its own words, without building the words that remember expansions.
- Exactly one of --command and --tool names the functionality. The --command text (and each --blocks of oko defects record) is read into words as the shell would hand them to the program, so a quoted word stays one word and is then refused as not a single command word, and a quote never closed is refused by name instead of being split on spaces. A program is named without its directory, a command path holds no option, and a shell tool (bash, Bash, run_shell_command, exec_command, as Oko's shell.json declares them) cannot be bound as a tool: binding it would refuse every command of every session.
- Every open defect is sorted in one of three ways: it binds the functionality it breaks (bind), names the open defect it is a symptom of (root), or states why no call could reach what it breaks (none). oko defects record requires one of --blocks "PROGRAM WORDS" (repeatable; each is bound with the evidence as its cause), --root ROOT and --blocks-nothing REASON, and the native record action the same blocks, root and blocks_nothing fields. A defect another Oko path writes unsorted (an operator moment, a stall, a loop of waits) is handed out by oko work right after the build-first pool until it is sorted. Quoting an output with record --observed or defects observed binds nothing: the call that printed it may be any command.
- The native defects operations action accepts the same verbs with identity {command: [words]} or {tool: name}. Oko Desktop → Defects → Functionality blocked by defects reads, binds, sorts, checks and withdraws the same records through the resident native API.
- The cause names the observed failure, not an inferred provider classification.
Output and state effect#
- Writes the functionality on that defect under the register lock and updates the open snapshot; a statement that the defect blocks nothing is replaced and kept in its notes. Tama's common pre-tool dispatch reads the snapshot locally before any tool starts and refuses every call that reaches the functionality with "Open defects block this functionality: <id> (<title>) blocks `<command path>`: <cause>; no repair of <id> is claimed yet; the call stays refused until one is published and it closes, or it is withdrawn. Do not use it until the defect is fixed or withdrawn, and do not investigate the failure again: where a root cause is named, the repair belongs there. …" A defect whose repair is claimed says instead "its repair is claimed at <revision>; the call stays refused until that repair is published and <id> closes". When the defect is a symptom (root), the reason sends the caller to the deepest root still open along its chain: "; it is a symptom of defect <root> (<root title>), owned by <owner>: repair that, not this; <what releases the root>", and defects[].root carries the root's id, title, owner, repair and claimedIn. No model or network request runs in this gate.
- The operator's standing rulings on the defect, and on its root when one is named (Oko's rulings register, `oko rulings record`), travel with the refusal: each reason ends with "; the operator already answered this, do not ask him again: «<his words>» …" and defects[].operatorAnswers lists them, so a session that meets a defect learns at the refusal what he already decided instead of asking him again. A rulings register Tama cannot read or decode refuses every tool with TAMA_HOOK_INFRASTRUCTURE naming the path and cause; a machine with no register quotes nothing.
- The defect's owner is admitted only while its declared work for the operator's current instruction is that defect (`tama task scope set --session <owner> --defect <id>`, read from `~/.tama/task-scope/<session>.json` by the reader Tama's off-task-work gate uses): it has to run the functionality to reproduce the failure and prove the repair, and nothing else. An owner working on anything else, or whose declaration was written for an earlier instruction, is refused like every other session until it declares again, and a declaration that cannot be read refuses with its path and cause. Assigning the defect (oko defects assign <id> --owner <session>) and declaring it is how a session takes the functionality over to repair it. `oko defects operations check --session S` lists a defect S owns under `owned` with the declaration that admits it (`admittedWhile`); Oko does not hold the declaration, so it answers allowed: false with allowedWhileDeclared: true instead of allowing the call.
- The Oko CLI refuses a bound command itself too, before it reads a service or writes anything, so a terminal no Tama gates, a script or a harness whose Tama did not load meets the same refusal: the command's words, `oko` first, are checked as `oko defects operations check` checks a call, and an open defect covering them refuses with "`<command>` is refused: open defect <id> (<title>, owned by <owner>) breaks <functionality>: <cause>; it runs again once the defect is fixed or withdrawn (`oko defects show <id>`), and the defect register's own commands naming <id> are never refused by it; nothing was run" (exit 1). A command of the defect register that names the defect itself is never refused by it, so closing, assigning or unbinding it stays possible. The defect's owner runs the command only while a trial of it is named and not yet observed (`oko defects operations trial`); otherwise its refusal says `owned by this session, which reproduces it only through a trial it names first`.
- The CLI exemption uses the defect identifier in the command's subject position, after the operation verb for defects operations. An identifier mentioned only in --reason, --text, --cause or another option value exempts nothing. When admission needs a second reading to identify the caller, any closed-root verification is taken from that same final admission reading, not the earlier one.
- Native API admission, including requests from Oko Desktop, reads the authoritative defect register directly. It does not infer snapshot freshness from modification timestamps: equal timestamps or a concurrent publication cannot establish which register contents the snapshot represents. A register read refusal is returned with its actual cause; it is not an empty set of open defects.
- Native admission uses the functionality explicitly declared by each typed request, not the request's serialized action name with underscores replaced by hyphens. Nested verbs and different CLI groups keep their real paths: stats inputs is checked as `oko telemetry inputs`, blockers work_queue as `oko work queue`, tasks record with a state mutation as `oko tasks state`, routines set_enabled as `oko routines pause` or `oko routines resume`, and sessions policy_limit_set as `oko sessions policy limit set`. The same bindings therefore refuse Oko Desktop and direct native API calls before those consumers run. Adding a request requires an exhaustive path declaration; unknown serialization cannot invent an unbound command name.
- Overlapping bindings of one symptom can match the same call more than once. Oko reserves and settles each symptom, root and closure receipt only once per call; matching both a broad command path and its more specific subcommand does not turn a successful operation into a duplicate-settlement refusal.
- A repeated binding of the same functionality updates its cause and retains the previous observation in the defect notes. A repair claim does not lift the refusal: the defect must leave the open snapshot, fixed by a published release or withdrawn.
Refusals and safe exits#
- defect <id> is closed; an affected operation requires an open defect
- oko defects operations takes --command or --tool, not both: one functionality is a command path or a tool; nothing was changed
- an affected operation names the program <program> without its directory: Tama compares the program a call runs by its name, so `<name>` is what reaches it
- an affected operation is a command path, and <option> is an option: options change how a command runs, not which one; name the words before it
- <name> is a shell tool; binding it would refuse every command of every session: name the command path the defect breaks with --command
- A missing functionality, blank cause or unknown defect is refused. Register and snapshot failures name the failed operation and path.