Reference
Work outside the task
block-off-task-work refuses a change to a repository the session never said the operator's current
instruction touches. It runs on the tools that change or publish a file — write, edit, refactor, apply_patch, and a
git commit or git push in bash. Reads are never refused.
#Why it exists
A session asked for one thing can deliver it and then spend an hour repairing installers, gateways, credential pools and sign-ins, and starting fleet builds, none of which anybody asked for. Each of those things may really be broken, which is what makes the drift invisible from inside: the work looks like work. The operator asked for a mechanism that blocks every action unrelated to the task.
Relatedness is not guessed. A gate that guessed it would be wrong in both directions — refusing a repair the instruction plainly needs, and waving through an hour of unrelated work because it looked useful. So it is declared, once per instruction, and checked mechanically.
#Declaring what the instruction touches
tama task scope set --session <SESSION> --repository tama --repository tama-landing --defect <DEFECT>
tama task scope set --session <SESSION> --repository oko --task task-0d468fb998d4df51
tama task scope set --session <SESSION> --task task-0d468fb998d4df51
tama task scope show --session <SESSION> [--json]
tama task scope clear --session <SESSION>
set binds the declaration to Tama's capture of the latest prompt, so a later turn cannot silently
reuse a stale scope. For --defect, it copies that prompt's words. For --task, it takes the
task's original operator words from Oko, even when the task predates a smaller instruction. An open earlier task
remains available after the smaller instruction is served: the agent can run
tama task scope set --session <SESSION> --task <EARLIER TASK ID> itself, without asking the
operator to say “continue”. show then names the earlier task and reports whether the declaration is bound
to the latest turn. It does not claim that Oko has independently marked the smaller task done.
A session whose prompt Tama never captured still declares its scope. A launcher can hand a session its first
prompt at spawn, and OMP raises its input event only for a prompt typed or sent over RPC, so
record-current-assignment records nothing for it. set then reads the session's transcript and
records its newest message from the operator's side (a user-role message the harness does not attribute to the agent)
as the instruction, saying on stderr session <SESSION> had no captured instruction …; its newest operator
message in the transcript is recorded as the instruction, and binds the scope to it. A transcript that cannot be
read, or holds no such message, refuses with no operator instruction is recorded for session <SESSION>, and
its transcript cannot give one: <cause> or … holds no message from the operator's side; the gate's
refusal never names a remedy that itself cannot run. The real journey tests/task-scope/transcript.rs runs the
built tama task scope set and show with a home of its own and a defect recorded through the
installed Oko: a transcript whose newest operator message follows an earlier one and precedes a harness continuation
binds the scope to that message, a transcript of harness messages only is refused and declares nothing, and a session
with no transcript is refused naming the session.
The open-work Stop refusal includes each open Oko task's identifier and the scope command. After recording evidence for a later defect, the agent reads the ledger, selects an earlier open task and continues it in the same session. Oko's pending independent verdict remains pending; selection is not a completion claim.
Every scope names an entry in Oko's register. An unknown task, a task already done, an unknown defect, or a defect closed by a revision is refused. The agent must select an open task whose work it is actually doing, not declare unrelated work under a convenient repository name.
Another session's --defect declaration does not lock the defect. Sessions work on one piece of code
together, so set declares the scope and, when another live session declared the same defect, prints on
stderr: defect <ID> is also declared by session <SESSION>; work on it together: read its notes
(`oko defects show <ID>`) and the uncommitted changes it holds, then build on them with anchored edits.
The exit status is still 0.
A task's scope lives in its Oko contract, not in the session. With --task and no
--repository, set takes the repositories the contract lists
(oko tasks show <task> --json, field current.contract.terms.scope). Given
repositories that differ from the contract's are written back into it with
oko tasks state <task> --scope <repo>… --by <SESSION>, so the next session
handed the same task declares the same scope, and the change appears in the contract's history. If Oko refuses that
write, set fails with Oko's sentence and declares nothing. A scope with no repository — none given and
none in the contract — is refused: work that touches nothing changes nothing.
#What is refused
| Situation | What the session is told |
|---|---|
| Nothing declared for the current instruction | The change is refused until the session says which defect or task the instruction is and which repositories it touches. It costs one command, and it makes the answer to "what are you working on" something the machine holds rather than something a model remembers. |
| A scope that names no defect and no task | Refused. A repository is not a subject: until an entry in Oko's registers stands for the work, nothing says the change is part of anything. |
| A repository outside the declaration | The refusal names the instruction being served and the repositories it was declared to touch. Something really
being broken is not the same as being asked to fix it: record it with oko defects record and say so in
the answer. |
| A scope declared for an earlier instruction | Refused as undeclared. The operator has spoken since, and that is exactly where a session drifts — one reasonable step at a time, under a scope nobody granted for the new prompt. |
| A completed or unknown earlier task | Refused by set. Resume an open registered task; do not relabel the latest prompt as its work. |
| A whole-file write over a file another session has uncommitted changes in | Refused, naming the file and the session that wrote it. Every file a session writes is logged under
~/.tama/session-writes/<session>.log; a whole-file write would replace that session's unfinished
change. An anchored edit of the same file is allowed: it applies to the file as it is, that session's change
included, and the file then counts as this session's too, so it may commit it. |
A broad git add (-A, --all, . or a directory) over an
untracked file another session wrote, unless that session is known to have stopped |
Refused, naming the file and the writing session: a session's unfinished work stays its own until it ends.
Stage this session's own changes with tama stage --session <session>. A file whose writer is a
recorded session whose process no longer runs may be staged: that session's unfinished work is taken over. The
commit gates admit it only when the staged bytes are what that session's recorded edits made of the file and its
transcript is found; a file changed after its writer stopped is refused at the commit. A writer that still runs, or
that no session record names, keeps the refusal; when the
stopped sessions cannot be read, the add is refused as unreadable. |
A git commit from a repository outside the declaration |
Refused like any other change there. What a commit takes is judged by pre-bash's
commit guard: a commit naming paths, or taking every tracked change
(-a), --include or a pathspec file, is refused, because it would stage whole files rather than
this session's recorded changes; the index a commit takes must hold exactly the changes the session's edit receipts
record, staged with tama stage --session <session> --repo <checkout> and committed with
git commit -m <message> in a separate invocation. A push carries commits already made and is judged
when they were made. |
Any change or commit in a repository by a session whose Oko role is supervisor |
Refused whatever its scope, naming the executors recorded under it (oko sessions role) to hand
the change to, or that none is recorded yet, with the commands that hand it over: oko defects record,
oko defects assign <id> --owner <executor> and oko sessions send <executor>
--defect <id>. Reads, Oko's registers and sends pass: a supervisor reads, records, assigns and sends. A
role register that cannot be read refuses with its cause. |
#What passes
Every read. Every change inside the declared repositories. Everything outside
~/Documents/CodingProjects/Wisent — an installed program, the operator's own state, a build directory —
which is other gates' subject: one-off repairs owns those.
#Where the refusal comes from
Source: rust/crates/tama-hook-tools/src/ports/block_off_task_work/mod.rs; the declaration CLI:
rust/crates/tama-hook-tools/src/ports/block_off_task_work/scope_cli/mod.rs; program:
rust/crates/tama-hook-tools/src/programs/block/ownership/block_off_task_work.rs; the declaration:
hook-declarations/block-off-task-work.json. Declared with
DEVICE_HOOK_EDIT_APPROVED=1 tama hooks declare --file <declaration> and installed with
DEVICE_HOOK_EDIT_APPROVED=1 tama hooks release; which hooks this machine runs is decided by
tama enforcement only <hook-id>…, which has to be run outside an agent session.
The real journey tests/task-scope/gate.rs runs the built prompt hook, the gate and tama task scope
set and show with a home and workspace of its own and a defect recorded through the installed Oko: a
change to a workspace repository is refused while no scope is declared for the current prompt and a read is not; once
the scope names the defect and tama, a change in tama passes and one in oko is
refused naming the defect and the declared repositories; a new prompt leaves the scope serving nothing and the next
change is refused again.