Gates and tool scopes

What a task may touch at all, and which of its calls wait for your approval before they happen.

Two different controls

A task is held by two controls, and it needs both:

Control The question it answers What it is not
Tool scope May this task call this tool, with these values, at all? A review of what this run is about to send.
Gate May this particular call happen now? You decide, on the exact payload. A substitute for connections with the least access they need.

A connection that can publish does not mean a task may publish. A task that lists a tool does not mean the call goes out unseen. Guardrails are neither: they are instructions to the model, and they stop nothing on their own.

Tool scopes

Which tools a task may call

A task lists every tool its runs may call. A run is offered those tools and no others.

Entry What it allows
github or github.* Every tool the GitHub connection offers, merging included, so the task must also declare the merge gate (see Merging).
github.create_pull_request That one tool.
os.set_gate_summary, memory.ask, workspace.git_push One of the OS's own tools. They need no connection, but a task still has to list them. See Built-in tools.

If the model calls a tool the task does not list, the run fails (tool_not_on_task); a name no tool answers to is handed back to the model to correct instead. If a listed tool has no connection, the run is blocked (tool_not_bound) until you connect it, and then carries on. See Tools for connecting one.

The connection's own permissions still apply underneath. A read-only token cannot write, whatever the task lists, so connect each tool with the least access the work needs.

Narrowing a tool to some values

A task can pin a field of a tool to the values it may use, after a #. Values are separated by , and fields by ;:

github.pull_request_review_write#method=create,submit_pending;!event=COMMENT

This task may create and submit reviews, and every review it submits is a comment: it can never approve a pull request or request changes on one.

The model is shown the tool with only those values, so it is never offered the rest. A call with any other value is refused before it reaches the tool, and handed back to the model to correct (tool_value_not_declared). A field with ! before it, like event here, ends the run instead (tool_value_refused).

An argument the connection itself fixes, such as the organisation it works in, is taken out of what the model sees, so it cannot ask for another.

Gates

Declaring a gate

A task version lists its gates, one per tool:

{ "tool": "github.add_issue_comment", "owner": "ceo", "approval": "specific" }

A gate on a whole vendor (github) covers every tool it offers except the merge, which needs a gate of its own. approval is specific: every call asks. The one other value, own_changes, is for merging; see Merging.

A gate on a workspace.* or memory.* tool is saved but never waits: the OS answers those calls before it reads the task's gates, so they go ahead as the task's tool scopes and workspace rules allow. What a workspace can and cannot do without you is in Workspaces, and which built-in tools can wait is in the built-in tools reference.

What happens at a gate

When a run calls a gated tool, the OS stops the call before it reaches the tool, stores exactly what would be sent (the post, the email, the merge), and sets the run to waiting_on_gate. Nothing more happens in that run until you decide. While it waits it holds neither its task nor its member, so other work can go on.

Decision What happens
Approve The stored call runs, exactly as stored: the OS checks that what it sends is what you approved. The run carries on.
Reject The call never runs. The run is cancelled and starts no successors.
Request changes The call never runs. The run ends succeeded with the outcome ceo_changes_requested, and your note goes to the task that follows. Offered only when the task routes that outcome, and only with a note saying what must change.

A decided gate cannot be decided again.

Gates with their own choices

Some gates ask what should happen, not whether a call may run. Those offer their own choices in place of Approve and Reject, each with a line saying what it does. A mail a member escalates to you offers three:

Choice What happens
Reply A reply is drafted and comes back to you as its own gate before anything is sent. You can say what it should say.
Snooze Nothing is sent. The mail is set aside until the date and time you pick.
Leave it No reply is sent and the mail is archived.

Each choice is still an approval or a rejection underneath, so it resumes or cancels the run in the same way. The task that runs next is told which one you chose.

Where you decide

Gates lists every gate waiting on you, oldest first, with the task, the tool, how long it has waited, and a summary when the run wrote one. A gate waiting more than an hour says so, and one waiting more than 24 hours is marked aged. You can approve or reject from the list, or pick one of a gate's own choices; Request changes, with its note, is on the run's page.

The same decision appears on the run's page, and in a chat when the work was started from one. It can also be made from another system with the webhook, or with an API token scoped gates:write.

Every gate is yours. Anyone you give access to Gates can decide one, and so can any holder of a gates:write token or of your webhook secret. A member never decides a gate: the OS's tools let a member see what is waiting, never answer it.

When nobody decides

A gate is never approved by silence. After 72 hours it is cancelled, its run is cancelled, and Blockers shows that a decision timed out, so someone can pick the work up again.

The gate summary

A payload is evidence, not an explanation. Before it asks, a run can write what the decision is about with os.set_gate_summary: Markdown, with at least one bullet, 40 to 6,000 characters. The gate copies it when it opens, and Gates and the run's page lead with it. Today a summary is attached to merge gates; any other gate shows its payload, and Gates asks you to open the run before you decide.

Questions for you

A task can put questions to you and stop, with os.ask_ceo. This is always a gate, whatever the task declares. Each question carries the task's recommended answer, so you decide on a view rather than from nothing:

On each question What it does
Accept Take the recommended answer.
Answer Give your own.
Explain Say what you need to know first. The answer goes on marked as needing more detail.

Submit answers needs a reply on every question, and ends the run with the outcome ceo_approved; Reject ends it with ceo_rejected. Either way the run is answered, not cancelled, and your answers and note go to the task that follows (see Handoffs). Answer questions in the portal or over the API: the webhook cannot carry answers.

Merging waits for you

Merging a pull request is the decision to go live, and it is held to stricter rules:

  • A task that can reach github.merge_pull_request has to declare a gate on it, and has to list os.set_gate_summary. Listing all of GitHub (github or github.*) reaches the merge as surely as naming it. A version without either is refused when you save it.
  • The gate has to name github.merge_pull_request itself. A gate on github or github.* does not count.
  • A run whose task has no gate on the merge is ended at the merge call, and nothing is merged.
  • Which tools are held to this rule is your enterprise's setting, not the OS's. Each connection on Tools carries its own must-gate list. A GitHub connection starts with github.merge_pull_request on it, and connections made before this setting existed were given the same list. A task that can reach any tool on any of your connections' lists needs a gate naming that tool. Only the CEO can change a list: send the whole list as mustGate to PATCH /v1/tools/bindings/{id}. [] clears it, and connecting again with a new token keeps the list as it is.
  • The merge is handed back to the model until the run has written its gate summary, so a merge never reaches you as a bare payload.

With "approval": "own_changes" on the merge gate, your approval carries forward to a later push of the same pull request, as long as what changed since is only what the main branch already carries, and every check on the new head is green. Any change of the pull request's own asks you again, with what changed since you approved. With checks not green, the merge is handed back to the model and nobody is asked.

A member operating a system through a browser, like a person, waits for you only on the browser actions its task gates, like any other tool.

Planned

Browser saves wait by themselves

A browser action that saves or submits something will wait for you whatever the task declares.

Over the API

Call Scope What it does
GET /v1/gates gates:read Every gate waiting on you.
POST /v1/gates/{id}/decision gates:write Decide one. decision is approve, reject or request_changes, with an optional note, and answers for questions. A gate already decided is a 409.