[{"data":1,"prerenderedAt":48},["ShallowReactive",2],{"$fu01ywekf26n":3},{"href":4,"title":5,"description":6,"kind":7,"mark":7,"planned":8,"contributors":9,"provenance":7,"html":10,"headings":11},"\u002Fdocs\u002Fwork\u002Fhandoffs","Handoffs","How one task hands its result to the next: by outcome, by condition, after a delay, and back to you when a decision is needed.",null,false,[],"\u003Ch2 id=\"what-a-handoff-is\">What a handoff is\u003C\u002Fh2>\n\u003Cp>A task can name the tasks that run after it: its \u003Cstrong>successors\u003C\u002Fstrong>. When a run ends, the OS starts every successor whose\nrule matches how it ended. The successor's run joins the same \u003Ca href=\"\u002Fdocs\u002Fwork\u002Fexecutions\">execution\u003C\u002Fa>, so one piece of work\ncan move from task to task, member to member and team to team, and still read as one thing.\u003C\u002Fp>\n\u003Cp>Successors are part of the task's definition, so changing them publishes a new \u003Ca href=\"\u002Fdocs\u002Fwork\u002Ftasks#versions\">version\u003C\u002Fa>.\nA successor can be any task in your enterprise, on any team. Its run is done by that task's member.\u003C\u002Fp>\n\u003Cp>A handoff happens when a run has ended. The run that hands off does not wait for its successor, and does not\nresume afterwards.\u003C\u002Fp>\n\u003Cp>Each successor is one rule:\u003C\u002Fp>\n\u003Cdiv class=\"prose__table\">\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Field\u003C\u002Fth>\n\u003Cth>What it says\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ccode>slug\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>The task to start.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>on\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>The ending it follows. \u003Ccode>succeeded\u003C\u002Fcode> if you leave it out.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>when\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>A condition on the input the successor would receive. Optional.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>delayMinutes\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>Wait this long before starting it: 1 to 1440 minutes. Optional.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>maxRunsPerIssue\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>The loop cap for this rule. Optional; see \u003Ca href=\"#loops-and-the-loop-cap\">Loops\u003C\u002Fa>.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>resumeConversation\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>For a rule that follows your decision back to the same task: carry on the conversation that asked you. See \u003Ca href=\"#your-answers-resume-the-work\">Your answers resume the work\u003C\u002Fa>.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003C\u002Fdiv>\n\u003Ch2 id=\"by-outcome-on\">By outcome: \u003Ccode>on\u003C\u002Fcode>\u003C\u002Fh2>\n\u003Cdiv class=\"prose__table\">\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>\u003Ccode>on\u003C\u002Fcode>\u003C\u002Fth>\n\u003Cth>Fires when\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ccode>succeeded\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>The run succeeded and named no outcome, or named plain success.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>failed\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>The run failed.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>terminal\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>The run ended, however it ended.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>A named outcome\u003C\u002Ftd>\n\u003Ctd>The run succeeded and named that outcome, such as \u003Ccode>needs_changes\u003C\u002Fcode> or \u003Ccode>complete\u003C\u002Fcode>.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>ceo_approved\u003C\u002Fcode>, \u003Ccode>ceo_rejected\u003C\u002Fcode>, \u003Ccode>ceo_changes_requested\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>You decided on the run's question or gate. Only your decision sets these.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003C\u002Fdiv>\n\u003Ch3 id=\"named-outcomes\">Named outcomes\u003C\u002Fh3>\n\u003Cp>A task's skill can end its run on a word that says which way the work went. The model ends its final message with\na JSON object:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-json\">{ &quot;outcome&quot;: &quot;needs_changes&quot; }\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>The OS compares that word with each rule's \u003Ccode>on\u003C\u002Fcode>. A named outcome is a branch: it \u003Cstrong>replaces\u003C\u002Fstrong> \u003Ccode>succeeded\u003C\u002Fcode>, it does\nnot add to it. A run that ends \u003Ccode>needs_changes\u003C\u002Fcode> starts the rules on \u003Ccode>needs_changes\u003C\u002Fcode> and on \u003Ccode>terminal\u003C\u002Fcode>, and not the\nrules on \u003Ccode>succeeded\u003C\u002Fcode>.\u003C\u002Fp>\n\u003Cul>\n\u003Cli>Words that only mean &quot;finished&quot; (\u003Ccode>success\u003C\u002Fcode>, \u003Ccode>successful\u003C\u002Fcode>, \u003Ccode>okay\u003C\u002Fcode>, \u003Ccode>done\u003C\u002Fcode>, \u003Ccode>finished\u003C\u002Fcode>) count as plain success,\nunless the task has a rule on that exact word.\u003C\u002Fli>\n\u003Cli>A run that failed follows \u003Ccode>failed\u003C\u002Fcode> and \u003Ccode>terminal\u003C\u002Fcode>, whatever outcome it named.\u003C\u002Fli>\n\u003Cli>An outcome is only ever the model's word. \u003Ccode>ceo_approved\u003C\u002Fcode>, \u003Ccode>ceo_rejected\u003C\u002Fcode> and \u003Ccode>ceo_changes_requested\u003C\u002Fcode> come from\nyour decision; a run whose model types one fails instead.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>The task's instructions to the model list the outcomes its rules route, so a skill ends on a word that goes\nsomewhere. A rule on a word the OS does not recognise is saved with a warning (\u003Ccode>successor_on_unknown\u003C\u002Fcode>): it is\nusually a typo.\u003C\u002Fp>\n\u003Ch2 id=\"by-condition-when\">By condition: \u003Ccode>when\u003C\u002Fcode>\u003C\u002Fh2>\n\u003Cp>A condition is checked on the input the successor would receive:\u003C\u002Fp>\n\u003Cdiv class=\"prose__table\">\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>\u003Ccode>when\u003C\u002Fcode>\u003C\u002Fth>\n\u003Cth>True when\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ccode>has_issue\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>The input carries an issue number (\u003Ccode>issue\u003C\u002Fcode>).\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>has_pr\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>The input carries a pull request number (\u003Ccode>number\u003C\u002Fcode>).\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>no_pr\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>It does not.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003C\u002Fdiv>\n\u003Cp>Any other \u003Ccode>when\u003C\u002Fcode> is refused when you save the task.\u003C\u002Fp>\n\u003Ch2 id=\"what-a-successor-receives\">What a successor receives\u003C\u002Fh2>\n\u003Cp>A finished run builds one input, and every successor it starts receives the same one:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cstrong>The run's own input\u003C\u002Fstrong>, carried forward. What the OS uses to control a run (retry and resume markers) is\nleft out.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>From the run's result:\u003C\u002Fstrong> \u003Ccode>issue\u003C\u002Fcode>, \u003Ccode>repo\u003C\u002Fcode> and the pull request \u003Ccode>number\u003C\u002Fcode>, if its final JSON names them. A run that\nopens or finds a pull request names its \u003Ccode>number\u003C\u002Fcode> there: that is the only way its successors learn it, and a run\nwhose result names none hands no \u003Ccode>number\u003C\u002Fcode> on (so \u003Ccode>no_pr\u003C\u002Fcode> holds, unless its own input already carried one). A\nresult that names a pull request but no issue only fills in \u003Ccode>repo\u003C\u002Fcode> when the input has none; it never moves the work\nto another repository. If the result names a different issue or repository, the branch the work was on is not\ncarried forward.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Your decision\u003C\u002Fstrong>, if the run was decided by you: your note as \u003Ccode>ceoNote\u003C\u002Fcode>, and your answers as \u003Ccode>ceoAnswers\u003C\u002Fcode>.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>Nothing else in the result is passed on. Your note and answers reach the \u003Cstrong>next\u003C\u002Fstrong> task only: a task that needs them\nlater has to write them somewhere lasting, such as a comment on the issue.\u003C\u002Fp>\n\u003Ch2 id=\"delayed-successors\">Delayed successors\u003C\u002Fh2>\n\u003Cp>A rule with \u003Ccode>delayMinutes\u003C\u002Fcode> starts its successor later: it waits as \u003Ccode>pending\u003C\u002Fcode> and starts once the delay has passed.\nUse it for a check that should look again in a while, such as re-reading a build that is still running.\u003C\u002Fp>\n\u003Ch2 id=\"when-the-successor-is-busy\">When the successor is busy\u003C\u002Fh2>\n\u003Cp>A task has one run under way at a time, and so does a member. If the successor task already has a run under way,\nor every member holding its role is busy with other work, the successor waits as \u003Ccode>pending\u003C\u002Fcode> and starts when the task\nand one of them are free. A run waiting for your decision at a gate does not count as busy. The same successor is\nnot queued twice for the same input.\u003C\u002Fp>\n\u003Ch2 id=\"loops-and-the-loop-cap\">Loops and the loop cap\u003C\u002Fh2>\n\u003Cp>A rule may point back to an earlier task: review, fix, review again. A loop made only of \u003Ccode>terminal\u003C\u002Fcode> rules would\nnever stop, so it is refused when you save it (\u003Ccode>unconditional_loop\u003C\u002Fcode>).\u003C\u002Fp>\n\u003Cp>Every other loop is capped, for work about one issue or pull request (an \u003Ccode>issue\u003C\u002Fcode> or \u003Ccode>number\u003C\u002Fcode> in its input): each\nrule may start its successor \u003Cstrong>4 times\u003C\u002Fstrong> by default for that issue or pull request, and \u003Ccode>maxRunsPerIssue\u003C\u002Fcode> changes the\ncap for one rule. The count starts again after each decision of yours, and rules on \u003Ccode>ceo_approved\u003C\u002Fcode> or\n\u003Ccode>ceo_changes_requested\u003C\u002Fcode> are never capped. Work with neither an issue nor a pull request in its input is not capped,\nso give a loop over anything else its own way to stop.\u003C\u002Fp>\n\u003Cp>At the cap, the OS does not start another lap. It records a \u003Cstrong>blocked\u003C\u002Fstrong> run with the reason \u003Ccode>loop_cap\u003C\u002Fcode>, which\nappears on \u003Cstrong>Blockers\u003C\u002Fstrong> for a person to retry (another lap) or cancel. See \u003Ca href=\"\u002Fdocs\u002Fwork\u002Fruns#blocked-runs\">Runs\u003C\u002Fa>.\u003C\u002Fp>\n\u003Ch2 id=\"when-a-pipeline-stops\">When a pipeline stops\u003C\u002Fh2>\n\u003Cp>A run that ends on an outcome no rule routes is a dead end. The run itself still reads \u003Ccode>succeeded\u003C\u002Fcode>, but the OS\nrecords that the pipeline stopped there, and which successors it did not start, and \u003Cstrong>Blockers\u003C\u002Fstrong> lists the work as\nstopped so it is not lost.\u003C\u002Fp>\n\u003Cp>A run that names no outcome at all is a dead end only when the task has neither a \u003Ccode>succeeded\u003C\u002Fcode> nor a \u003Ccode>failed\u003C\u002Fcode> rule to\nfall back on. Before it ends like that, the model is asked once to name its outcome. A task whose only rules are on\n\u003Ccode>terminal\u003C\u002Fcode> declares no outcomes, and a run of it is simply the end of the pipeline.\u003C\u002Fp>\n\u003Cp>Rules that name a task your enterprise does not have are saved with a warning (\u003Ccode>successor_unknown_slug\u003C\u002Fcode>): the\npipeline ends there.\u003C\u002Fp>\n\u003Ch2 id=\"your-answers-resume-the-work\">Your answers resume the work\u003C\u002Fh2>\n\u003Cp>A task that needs a decision from you asks it and stops. It puts one or more questions to you, each with a\nrecommended answer, and waits at a \u003Ca href=\"\u002Fdocs\u002Fcontrol\u002Fgates#questions-for-you\">gate\u003C\u002Fa>. When you decide:\u003C\u002Fp>\n\u003Cdiv class=\"prose__table\">\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Your decision\u003C\u002Fth>\n\u003Cth>The run's outcome\u003C\u002Fth>\n\u003Cth>What its successors receive\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>Submit answers\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>ceo_approved\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>ceoAnswers\u003C\u002Fcode>, one per question, and your note as \u003Ccode>ceoNote\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Reject\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>ceo_rejected\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>Your note as \u003Ccode>ceoNote\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Request changes on a gate\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>ceo_changes_requested\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>Your note as \u003Ccode>ceoNote\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003C\u002Fdiv>\n\u003Cp>Route those outcomes like any other: an approved plan on to the task that carries it out, a request for changes back\nto the task that proposed it. Rejecting a gated tool call is different: it cancels the run, and nothing follows. See\n\u003Ca href=\"\u002Fdocs\u002Fcontrol\u002Fgates#what-happens-at-a-gate\">Gates\u003C\u002Fa>.\u003C\u002Fp>\n\u003Cp>A rule back to the \u003Cstrong>same\u003C\u002Fstrong> task can set \u003Ccode>resumeConversation: true\u003C\u002Fcode>. The successor then carries on the conversation\nthat asked you, instead of starting fresh: the model sees what it did before, then your decision, your note and each\nanswer, and is told how long the question waited.\u003C\u002Fp>\n\u003Cdiv class=\"prose__planned\">\n\u003Cp class=\"prose__flag\">Planned\u003C\u002Fp>\n\u003Ch2 id=\"waiting-on-another-team\">Waiting on another team\u003C\u002Fh2>\n\u003Cp>A run will be able to hand part of its work to another team's task and wait for it (\u003Ccode>waiting_on_run\u003C\u002Fcode>): the other\nteam's run does its part, and the first run resumes when it finishes, or fails with its reason. Today a handoff\nstarts its successor when the run has ended, and nothing waits for it.\u003C\u002Fp>\n\u003C\u002Fdiv>\n",[12,16,19,23,26,29,32,35,38,41,44],{"id":13,"text":14,"level":15,"planned":8},"what-a-handoff-is","What a handoff is",2,{"id":17,"text":18,"level":15,"planned":8},"by-outcome-on","By outcome: on",{"id":20,"text":21,"level":22,"planned":8},"named-outcomes","Named outcomes",3,{"id":24,"text":25,"level":15,"planned":8},"by-condition-when","By condition: when",{"id":27,"text":28,"level":15,"planned":8},"what-a-successor-receives","What a successor receives",{"id":30,"text":31,"level":15,"planned":8},"delayed-successors","Delayed successors",{"id":33,"text":34,"level":15,"planned":8},"when-the-successor-is-busy","When the successor is busy",{"id":36,"text":37,"level":15,"planned":8},"loops-and-the-loop-cap","Loops and the loop cap",{"id":39,"text":40,"level":15,"planned":8},"when-a-pipeline-stops","When a pipeline stops",{"id":42,"text":43,"level":15,"planned":8},"your-answers-resume-the-work","Your answers resume the work",{"id":45,"text":46,"level":15,"planned":47},"waiting-on-another-team","Waiting on another team",true,1791124519875]