[{"data":1,"prerenderedAt":60},["ShallowReactive",2],{"$f2u8ty429modxe":3},{"href":4,"title":5,"description":6,"kind":7,"mark":7,"planned":8,"contributors":9,"provenance":7,"html":10,"headings":11},"\u002Fdocs\u002Fcontrol\u002Fgates","Gates and tool scopes","What a task may touch at all, and which of its calls wait for your approval before they happen.",null,false,[],"\u003Ch2 id=\"two-different-controls\">Two different controls\u003C\u002Fh2>\n\u003Cp>A task is held by two controls, and it needs both:\u003C\u002Fp>\n\u003Cdiv class=\"prose__table\">\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Control\u003C\u002Fth>\n\u003Cth>The question it answers\u003C\u002Fth>\n\u003Cth>What it is not\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Tool scope\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>May this task call this tool, with these values, at all?\u003C\u002Ftd>\n\u003Ctd>A review of what this run is about to send.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Gate\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>May this particular call happen now? You decide, on the exact payload.\u003C\u002Ftd>\n\u003Ctd>A substitute for connections with the least access they need.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003C\u002Fdiv>\n\u003Cp>A connection that can publish does not mean a task may publish. A task that lists a tool does not mean the call goes\nout unseen. \u003Ca href=\"\u002Fdocs\u002Fwork\u002Ftasks#anatomy\">Guardrails\u003C\u002Fa> are neither: they are instructions to the model, and they stop\nnothing on their own.\u003C\u002Fp>\n\u003Ch2 id=\"tool-scopes\">Tool scopes\u003C\u002Fh2>\n\u003Ch3 id=\"which-tools-a-task-may-call\">Which tools a task may call\u003C\u002Fh3>\n\u003Cp>A task lists every tool its runs may call. A run is offered those tools and no others.\u003C\u002Fp>\n\u003Cdiv class=\"prose__table\">\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Entry\u003C\u002Fth>\n\u003Cth>What it allows\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ccode>github\u003C\u002Fcode> or \u003Ccode>github.*\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>Every tool the GitHub connection offers, merging included, so the task must also declare the merge gate (see \u003Ca href=\"#merging-waits-for-you\">Merging\u003C\u002Fa>).\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>github.create_pull_request\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>That one tool.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>os.set_gate_summary\u003C\u002Fcode>, \u003Ccode>memory.ask\u003C\u002Fcode>, \u003Ccode>workspace.git_push\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>One of the OS's own tools. They need no connection, but a task still has to list them. See \u003Ca href=\"\u002Fdocs\u002Ftools\u002Fbuilt-in\">Built-in tools\u003C\u002Fa>.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003C\u002Fdiv>\n\u003Cp>If the model calls a tool the task does not list, the run fails (\u003Ccode>tool_not_on_task\u003C\u002Fcode>); a name no tool answers to is\nhanded back to the model to correct instead. If a listed tool has no\nconnection, the run is blocked (\u003Ccode>tool_not_bound\u003C\u002Fcode>) until you connect it, and then carries on. See\n\u003Ca href=\"\u002Fdocs\u002Ftools\">Tools\u003C\u002Fa> for connecting one.\u003C\u002Fp>\n\u003Cp>The connection's own permissions still apply underneath. A read-only token cannot write, whatever the task lists, so\nconnect each tool with the least access the work needs.\u003C\u002Fp>\n\u003Ch3 id=\"narrowing-a-tool-to-some-values\">Narrowing a tool to some values\u003C\u002Fh3>\n\u003Cp>A task can pin a field of a tool to the values it may use, after a \u003Ccode>#\u003C\u002Fcode>. Values are separated by \u003Ccode>,\u003C\u002Fcode> and fields by\n\u003Ccode>;\u003C\u002Fcode>:\u003C\u002Fp>\n\u003Cpre>\u003Ccode>github.pull_request_review_write#method=create,submit_pending;!event=COMMENT\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>This task may create and submit reviews, and every review it submits is a comment: it can never approve a pull\nrequest or request changes on one.\u003C\u002Fp>\n\u003Cp>The model is shown the tool with only those values, so it is never offered the rest. A call with any other value is\nrefused before it reaches the tool, and handed back to the model to correct (\u003Ccode>tool_value_not_declared\u003C\u002Fcode>). A field\nwith \u003Ccode>!\u003C\u002Fcode> before it, like \u003Ccode>event\u003C\u002Fcode> here, ends the run instead (\u003Ccode>tool_value_refused\u003C\u002Fcode>).\u003C\u002Fp>\n\u003Cp>An argument the connection itself fixes, such as the organisation it works in, is taken out of what the model sees,\nso it cannot ask for another.\u003C\u002Fp>\n\u003Ch2 id=\"gates\">Gates\u003C\u002Fh2>\n\u003Ch3 id=\"declaring-a-gate\">Declaring a gate\u003C\u002Fh3>\n\u003Cp>A task version lists its gates, one per tool:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-json\">{ &quot;tool&quot;: &quot;github.add_issue_comment&quot;, &quot;owner&quot;: &quot;ceo&quot;, &quot;approval&quot;: &quot;specific&quot; }\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>A gate on a whole vendor (\u003Ccode>github\u003C\u002Fcode>) covers every tool it offers except the merge, which needs a gate of its own.\n\u003Ccode>approval\u003C\u002Fcode> is \u003Ccode>specific\u003C\u002Fcode>: every call asks. The one other value, \u003Ccode>own_changes\u003C\u002Fcode>, is for merging; see\n\u003Ca href=\"#merging-waits-for-you\">Merging\u003C\u002Fa>.\u003C\u002Fp>\n\u003Cp>A gate on a \u003Ccode>workspace.*\u003C\u002Fcode> or \u003Ccode>memory.*\u003C\u002Fcode> tool is saved but never waits: the OS answers those calls before it reads\nthe task's gates, so they go ahead as the task's tool scopes and workspace rules allow. What a workspace can and\ncannot do without you is in \u003Ca href=\"\u002Fdocs\u002Ftools\u002Fworkspaces#what-waits-for-you\">Workspaces\u003C\u002Fa>, and which built-in tools can\nwait is in the \u003Ca href=\"\u002Fdocs\u002Ftools\u002Fbuilt-in\">built-in tools reference\u003C\u002Fa>.\u003C\u002Fp>\n\u003Ch3 id=\"what-happens-at-a-gate\">What happens at a gate\u003C\u002Fh3>\n\u003Cp>When a run calls a gated tool, the OS stops the call before it reaches the tool, stores exactly what would be sent\n(the post, the email, the merge), and sets the run to \u003Ccode>waiting_on_gate\u003C\u002Fcode>. Nothing more happens in that run until you\ndecide. While it waits it holds neither its task nor its member, so other work can go on.\u003C\u002Fp>\n\u003Cdiv class=\"prose__table\">\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Decision\u003C\u002Fth>\n\u003Cth>What happens\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Approve\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>The stored call runs, exactly as stored: the OS checks that what it sends is what you approved. The run carries on.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Reject\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>The call never runs. The run is cancelled and starts no successors.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Request changes\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>The call never runs. The run ends \u003Ccode>succeeded\u003C\u002Fcode> with the outcome \u003Ccode>ceo_changes_requested\u003C\u002Fcode>, and your note goes to the task that follows. Offered only when the task \u003Ca href=\"\u002Fdocs\u002Fwork\u002Fhandoffs#your-answers-resume-the-work\">routes that outcome\u003C\u002Fa>, and only with a note saying what must change.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003C\u002Fdiv>\n\u003Cp>A decided gate cannot be decided again.\u003C\u002Fp>\n\u003Ch3 id=\"gates-with-their-own-choices\">Gates with their own choices\u003C\u002Fh3>\n\u003Cp>Some gates ask what should happen, not whether a call may run. Those offer their own choices in place of \u003Cstrong>Approve\u003C\u002Fstrong>\nand \u003Cstrong>Reject\u003C\u002Fstrong>, each with a line saying what it does. A mail a member escalates to you offers three:\u003C\u002Fp>\n\u003Cdiv class=\"prose__table\">\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Choice\u003C\u002Fth>\n\u003Cth>What happens\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Reply\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>A reply is drafted and comes back to you as its own gate before anything is sent. You can say what it should say.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Snooze\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>Nothing is sent. The mail is set aside until the date and time you pick.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Leave it\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>No reply is sent and the mail is archived.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003C\u002Fdiv>\n\u003Cp>Each choice is still an approval or a rejection underneath, so it resumes or cancels the run in the same way. The\ntask that runs next is told which one you chose.\u003C\u002Fp>\n\u003Ch3 id=\"where-you-decide\">Where you decide\u003C\u002Fh3>\n\u003Cp>\u003Cstrong>Gates\u003C\u002Fstrong> lists every gate waiting on you, oldest first, with the task, the tool, how long it has waited, and a\nsummary when the run wrote one. A gate waiting more than an hour says so, and one waiting more than 24 hours is marked\naged. You can approve or reject from the list, or pick one of a gate's own choices; \u003Cstrong>Request changes\u003C\u002Fstrong>, with its\nnote, is on the run's page.\u003C\u002Fp>\n\u003Cp>The same decision appears on the run's page, and in a \u003Ca href=\"\u002Fdocs\u002Fwork\u002Fchat\">chat\u003C\u002Fa> when the work was started from one.\nIt can also be made from another system with the \u003Ca href=\"\u002Fdocs\u002Fapi\u002Fwebhooks#answer-a-gate\">webhook\u003C\u002Fa>, or with an\n\u003Ca href=\"\u002Fdocs\u002Fapi\u002Fauthentication\">API token\u003C\u002Fa> scoped \u003Ccode>gates:write\u003C\u002Fcode>.\u003C\u002Fp>\n\u003Cp>Every gate is yours. Anyone you \u003Ca href=\"\u002Fdocs\u002Fcompany\u002Fpeople\">give access\u003C\u002Fa> to \u003Cstrong>Gates\u003C\u002Fstrong> can decide one, and so can any\nholder of a \u003Ccode>gates:write\u003C\u002Fcode> token or of your webhook secret. A member never decides a gate: the OS's tools let a member see what is waiting,\nnever answer it.\u003C\u002Fp>\n\u003Ch3 id=\"when-nobody-decides\">When nobody decides\u003C\u002Fh3>\n\u003Cp>A gate is never approved by silence. After \u003Cstrong>72 hours\u003C\u002Fstrong> it is cancelled, its run is cancelled, and \u003Cstrong>Blockers\u003C\u002Fstrong> shows\nthat a decision timed out, so someone can pick the work up again.\u003C\u002Fp>\n\u003Ch3 id=\"the-gate-summary\">The gate summary\u003C\u002Fh3>\n\u003Cp>A payload is evidence, not an explanation. Before it asks, a run can write what the decision is about with\n\u003Ccode>os.set_gate_summary\u003C\u002Fcode>: Markdown, with at least one bullet, 40 to 6,000 characters. The gate copies it when it opens,\nand \u003Cstrong>Gates\u003C\u002Fstrong> and the run's page lead with it. Today a summary is attached to merge gates; any other gate shows its\npayload, and \u003Cstrong>Gates\u003C\u002Fstrong> asks you to open the run before you decide.\u003C\u002Fp>\n\u003Ch2 id=\"questions-for-you\">Questions for you\u003C\u002Fh2>\n\u003Cp>A task can put questions to you and stop, with \u003Ccode>os.ask_ceo\u003C\u002Fcode>. This is always a gate, whatever the task declares.\nEach question carries the task's recommended answer, so you decide on a view rather than from nothing:\u003C\u002Fp>\n\u003Cdiv class=\"prose__table\">\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>On each question\u003C\u002Fth>\n\u003Cth>What it does\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Accept\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>Take the recommended answer.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Answer\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>Give your own.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Explain\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>Say what you need to know first. The answer goes on marked as needing more detail.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003C\u002Fdiv>\n\u003Cp>\u003Cstrong>Submit answers\u003C\u002Fstrong> needs a reply on every question, and ends the run with the outcome \u003Ccode>ceo_approved\u003C\u002Fcode>; \u003Cstrong>Reject\u003C\u002Fstrong> ends\nit with \u003Ccode>ceo_rejected\u003C\u002Fcode>. Either way the run is answered, not cancelled, and your answers and note go to the task that\nfollows (see \u003Ca href=\"\u002Fdocs\u002Fwork\u002Fhandoffs#your-answers-resume-the-work\">Handoffs\u003C\u002Fa>). Answer questions in the portal or over\nthe API: the webhook cannot carry answers.\u003C\u002Fp>\n\u003Ch2 id=\"merging-waits-for-you\">Merging waits for you\u003C\u002Fh2>\n\u003Cp>Merging a pull request is the decision to go live, and it is held to stricter rules:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>A task that can reach \u003Ccode>github.merge_pull_request\u003C\u002Fcode> has to declare a gate on it, and has to list\n\u003Ccode>os.set_gate_summary\u003C\u002Fcode>. Listing all of GitHub (\u003Ccode>github\u003C\u002Fcode> or \u003Ccode>github.*\u003C\u002Fcode>) reaches the merge as surely as naming it.\nA version without either is refused when you save it.\u003C\u002Fli>\n\u003Cli>The gate has to name \u003Ccode>github.merge_pull_request\u003C\u002Fcode> itself. A gate on \u003Ccode>github\u003C\u002Fcode> or \u003Ccode>github.*\u003C\u002Fcode> does not count.\u003C\u002Fli>\n\u003Cli>A run whose task has no gate on the merge is ended at the merge call, and nothing is merged.\u003C\u002Fli>\n\u003Cli>Which tools are held to this rule is your enterprise's setting, not the OS's. Each connection on \u003Cstrong>Tools\u003C\u002Fstrong> carries\nits own must-gate list. A GitHub connection starts with \u003Ccode>github.merge_pull_request\u003C\u002Fcode> on it, and connections made\nbefore this setting existed were given the same list. A task that can reach any tool on any of your connections'\nlists needs a gate naming that tool. Only the CEO can change a list: send the whole list as \u003Ccode>mustGate\u003C\u002Fcode> to\n\u003Ccode>PATCH \u002Fv1\u002Ftools\u002Fbindings\u002F{id}\u003C\u002Fcode>. \u003Ccode>[]\u003C\u002Fcode> clears it, and connecting again with a new token keeps the list as it is.\u003C\u002Fli>\n\u003Cli>The merge is handed back to the model until the run has written its gate summary, so a merge never reaches you as\na bare payload.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>With \u003Ccode>&quot;approval&quot;: &quot;own_changes&quot;\u003C\u002Fcode> on the merge gate, your approval carries forward to a later push of the same pull\nrequest, as long as what changed since is only what the main branch already carries, and every check on the new\nhead is green. Any change of the pull request's own asks you again, with what changed since you approved. With checks\nnot green, the merge is handed back to the model and nobody is asked.\u003C\u002Fp>\n\u003Cp>A member operating a system through a browser, like a person, waits for you only on the browser actions its task\ngates, like any other tool.\u003C\u002Fp>\n\u003Cdiv class=\"prose__planned\">\n\u003Cp class=\"prose__flag\">Planned\u003C\u002Fp>\n\u003Ch3 id=\"browser-saves-wait-by-themselves\">Browser saves wait by themselves\u003C\u002Fh3>\n\u003Cp>A browser action that saves or submits something will wait for you whatever the task declares.\u003C\u002Fp>\n\u003C\u002Fdiv>\n\u003Ch2 id=\"over-the-api\">Over the API\u003C\u002Fh2>\n\u003Cdiv class=\"prose__table\">\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Call\u003C\u002Fth>\n\u003Cth>Scope\u003C\u002Fth>\n\u003Cth>What it does\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ccode>GET \u002Fv1\u002Fgates\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>gates:read\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>Every gate waiting on you.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>POST \u002Fv1\u002Fgates\u002F{id}\u002Fdecision\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>gates:write\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>Decide one. \u003Ccode>decision\u003C\u002Fcode> is \u003Ccode>approve\u003C\u002Fcode>, \u003Ccode>reject\u003C\u002Fcode> or \u003Ccode>request_changes\u003C\u002Fcode>, with an optional \u003Ccode>note\u003C\u002Fcode>, and \u003Ccode>answers\u003C\u002Fcode> for questions. A gate already decided is a \u003Ccode>409\u003C\u002Fcode>.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003C\u002Fdiv>\n",[12,16,19,23,26,29,32,35,38,41,44,47,50,53,57],{"id":13,"text":14,"level":15,"planned":8},"two-different-controls","Two different controls",2,{"id":17,"text":18,"level":15,"planned":8},"tool-scopes","Tool scopes",{"id":20,"text":21,"level":22,"planned":8},"which-tools-a-task-may-call","Which tools a task may call",3,{"id":24,"text":25,"level":22,"planned":8},"narrowing-a-tool-to-some-values","Narrowing a tool to some values",{"id":27,"text":28,"level":15,"planned":8},"gates","Gates",{"id":30,"text":31,"level":22,"planned":8},"declaring-a-gate","Declaring a gate",{"id":33,"text":34,"level":22,"planned":8},"what-happens-at-a-gate","What happens at a gate",{"id":36,"text":37,"level":22,"planned":8},"gates-with-their-own-choices","Gates with their own choices",{"id":39,"text":40,"level":22,"planned":8},"where-you-decide","Where you decide",{"id":42,"text":43,"level":22,"planned":8},"when-nobody-decides","When nobody decides",{"id":45,"text":46,"level":22,"planned":8},"the-gate-summary","The gate summary",{"id":48,"text":49,"level":15,"planned":8},"questions-for-you","Questions for you",{"id":51,"text":52,"level":15,"planned":8},"merging-waits-for-you","Merging waits for you",{"id":54,"text":55,"level":22,"planned":56},"browser-saves-wait-by-themselves","Browser saves wait by themselves",true,{"id":58,"text":59,"level":15,"planned":8},"over-the-api","Over the API",1791124519592]