Repository navigation
Show workflow hand-offs under a chat answer in V2 (Phase 7b) - #1683
Merged
Paul Lizer (paullizer) merged 30 commits intoOct 8, 2026
Conversation
A plan's workflow_handoff step now gets a hand-off card under its answer: what the one-time workflow will review, its tasks, and when the offer expires, with Accept, Edit and Decline. Accept sends mode as_proposed. Edit opens the prepared draft in the workflow editor, and saving sends an edited accept that keeps the draft open on a coded refusal. After accept, the card follows the run through the run tracker, joined by the accepted run's id, and shows the hand-off's own over-limit pause text. Hand-off plans need manual approval, show a plan-card notice, and show the blueprint as a summary instead of raw arguments. Part of microsoft#1549. Part of microsoft#1543. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Describe the hand-off card under a plan's answer: what it discloses, Accept, Edit with Save and start, Decline, Start its run, live run status, the over-limit pause, refusal text and Try again. Add the user guide section and troubleshooting rows, the chat controls reference, the admin setting's V2 behavior, the feature doc's V2 section, files, tests and known limitations, and the v0.261.256 release notes. Part of microsoft#1549. Part of microsoft#1543. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Part of microsoft#1549 Part of microsoft#1543 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Brings in V2 at 485d51a (VERSION 0.261.273) so the PR can be checked. Conflicts: - application/single_app/config.py: took V2's file (VERSION 0.261.273). This branch changed only the VERSION line; the renumber commit sets the final number. - docs/explanation/release_notes.md: V2's file byte-for-byte, with this branch's own (v0.261.256) section inserted at the very top, above V2's first section, (v0.261.271). Every V2 section is unchanged. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
7a's feature doc said any workspace query that matches more documents than its limit pauses before reviewing any (in "The one-time lifecycle" and in "Known limitations"). Only a query for all matching documents does: - functions_workflow_loop_inputs.py L459-462: only an unranked (all_matches) query checks its count against the limit; a best_n query trims to its own count instead. - functions_workflow_flow_runner.py L432-433: that limit error is what pauses the run before any item. - functions_workflow_handoff_builder.py L180-186: the card's disclosure says the same, limit_behavior 'best_n' versus 'pause'. Corrected those two lines, and made the pause's cause name the all-matches query in the V2 card section, the trigger-a-workflow troubleshooting row and the admin troubleshooting row this branch already changes. Docs only, so no version bump. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Brings in V2 at 919375b (microsoft#1682, VERSION 0.261.275), which also includes microsoft#1681 (VERSION 0.261.274). Conflicts: - docs/explanation/release_notes.md: V2's file byte-for-byte, with this branch's own (v0.261.256) section inserted at the very top, above V2's first section, (v0.261.275). Every V2 section is unchanged. - application/single_app/config.py merged cleanly to V2's file (VERSION 0.261.275), because this branch hadn't changed it since the last merge. The renumber commit sets the final number. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
e65ab0d removed the agent delegation card and, with it, deleted ui_tests/fixtures/agent_delegation/harness_build.py, which other V2 browser tests still import. Without it, these 7 files fail at collection with ModuleNotFoundError: - ui_tests/test_chat_workflow_results.py - ui_tests/test_foundry_consent_v2.py - ui_tests/test_v2_notifications_bell.py - ui_tests/test_v2_workflow_alert_notices.py - ui_tests/test_v2_workflow_run_card.py - ui_tests/test_v2_workflow_run_tracker_spa.py, which imports the bell and run card tests - ui_tests/test_v2_orchestration_workflow_handoff_run_status.py, this PR's run status test, which imports the same two Restore only that file, byte-identical to e65ab0d^ (blob 9dd4f84, 1,539 bytes). The five direct importers pass entry= and bundle= into their own fixture directories, each with its own .gitignore, so harness.html, harness_entry.tsx and the old .gitignore stay deleted. Refs microsoft#1549 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Renumber only this PR's own references to 0.261.277 after merging paullizer-react-v2-ui at 919375b (0.261.275): config.py, the release-notes section header, the doc lines that give the V2 card's version, the Version/Implemented in headers of this PR's new tests, and the card test's fake bootstrap version. 0.261.277 sits above the numbers that open PRs already claim, so landing them doesn't force another renumber. V2's own 0.261.256, 0.261.274 and 0.261.275 references are unchanged, and the test floor stays 0.261.253. Refs microsoft#1549 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Brings in V2 at 705774d (microsoft#1679, VERSION 0.261.276). Conflicts: - docs/explanation/release_notes.md: V2's file byte-for-byte, with this branch's own (v0.261.277) section inserted at the very top, above V2's first section, (v0.261.276). Every V2 section is unchanged. - application/single_app/config.py: kept this branch's VERSION 0.261.277, which is above V2's 0.261.276. The two files differed only on the VERSION line. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Brings in V2 at b3c0b7b (microsoft#1684, VERSION 0.261.277, and microsoft#1685, VERSION 0.261.278). Conflicts: - docs/explanation/release_notes.md: V2's file byte-for-byte, with this branch's own (v0.261.277) section inserted at the very top, above V2's first section, (v0.261.278). Every V2 section is unchanged. The next commit renumbers this branch's section, because microsoft#1684 already uses 0.261.277. - application/single_app/config.py: kept this branch's file (VERSION 0.261.277) for the merge. The two files differed only on the VERSION line. The next commit renumbers it above V2's 0.261.278. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Renumber only this PR's own references from 0.261.277 to 0.261.279 after merging paullizer-react-v2-ui at b3c0b7b (0.261.278), because microsoft#1684 landed on V2 with 0.261.277: config.py, the release-notes section header, the doc lines that give the V2 card's version, the Version/Implemented in headers of this PR's new tests, and the card test's fake bootstrap version. These are the same 20 lines 82377db renumbered. 0.261.279 is the next number above V2's 0.261.278 and every open PR's claim. V2's own 0.261.256 and 0.261.274 to 0.261.278 references are unchanged, and the test floor stays 0.261.253. Refs microsoft#1549 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
CodeQL flagged HandoffRunHarness.__init__ for overwriting RunHarness's link_items attribute (py/overwritten-inherited-attribute). Clearing the per-instance list keeps the same behavior: a hand-off plan has no workflow_run step, so Phase 5's run-links list stays empty. Refs microsoft#1549 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Brings in V2 at 7de8fce (microsoft#1689, VERSION 0.261.279, and microsoft#1686, VERSION 0.261.280). Conflicts: - docs/explanation/release_notes.md: V2's file byte-for-byte, with this branch's own (v0.261.279) section inserted at the very top, above V2's first section, (v0.261.280). Every V2 section is unchanged. The next commit renumbers this branch's section, because microsoft#1689 already uses 0.261.279. - application/single_app/config.py: kept this branch's file (VERSION 0.261.279) for the merge. The two files differed only on the VERSION line. The next commit renumbers it above V2's 0.261.280. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
V2 now uses 0.261.279 (microsoft#1689) and 0.261.280 (microsoft#1686), so this branch's version moves above them. Renumbers the same 20 lines in 15 files as the previous bump: config.py, this branch's release-notes section, the documentation version notes and the new tests' headers. The tests' version floor stays 0.261.253. Refs microsoft#1549 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Brings in V2 at f1c3dda (microsoft#1688, which kept VERSION 0.261.280, and microsoft#1691, VERSION 0.261.281). No textual conflicts. Both sides set config.py's VERSION to 0.261.281, so git took it as the same change. The release notes merged with this branch's (v0.261.281) section at the very top, above V2's first section, (v0.261.280). microsoft#1691 added its entries to V2's existing sections, so V2's file has no (v0.261.281) section of its own. The next commit renumbers this branch to 0.261.282, because V2's VERSION is now 0.261.281. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
V2 now uses 0.261.281 (microsoft#1691), so this branch's version moves above it. Renumbers the same 20 lines in 15 files as the previous bump: config.py, this branch's release-notes section, the documentation version notes and the new tests' headers. The tests' version floor stays 0.261.253. Refs microsoft#1549 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Brings in V2 at 01c4959 (microsoft#1692, VERSION 0.261.282 and 0.261.283, microsoft#1695, VERSION 0.261.284, and microsoft#1693, VERSION 0.261.285 and 0.261.286). Conflicts: - docs/explanation/release_notes.md: V2's file byte-for-byte, with this branch's own (v0.261.282) section inserted at the very top, above V2's first section, (v0.261.286). Every V2 section is unchanged. The next commit renumbers this branch's section, because microsoft#1692 already uses 0.261.282. - application/single_app/config.py: V2's file, including its new activity-log indexing policy, with this branch's VERSION line (0.261.282) kept for the merge. The VERSION line was the only conflicting hunk. The next commit renumbers it above V2's 0.261.286. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
V2 now uses 0.261.286 (microsoft#1693), and microsoft#1692 already uses 0.261.282, so this branch's version moves above both. Renumbers the same 20 lines in 15 files as the previous bump: config.py, this branch's release-notes section, the documentation version notes and the new tests' headers. V2's own (v0.261.282) release-notes section is untouched. The tests' version floor stays 0.261.253. Refs microsoft#1549 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Brings in V2 at 05fb0ce (microsoft#1687, VERSION 0.261.287). Conflicts: - docs/explanation/release_notes.md: V2's file byte-for-byte, with this branch's own (v0.261.287) section inserted at the very top, above V2's first section, microsoft#1687's own (v0.261.287). Every V2 section is unchanged. The next commit renumbers this branch's section, because microsoft#1687 already uses 0.261.287. application/single_app/config.py merged cleanly: both sides set VERSION to 0.261.287, so the merged file equals V2's. The next commit renumbers it above V2's 0.261.287. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
V2 now uses 0.261.287 (microsoft#1687), so this branch's version moves above it. Renumbers the same 20 lines in 15 files as the previous bump: config.py, this branch's release-notes section, the documentation version notes and the new tests' headers. V2's own (v0.261.287) release-notes section is untouched. The tests' version floor stays 0.261.253. Refs microsoft#1549 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Brings in V2 at d4cfde7 (microsoft#1694, microsoft#1696, VERSION 0.261.289). Conflicts: - application/single_app/config.py: only the VERSION line. Took V2's file (0.261.289). - docs/explanation/release_notes.md: V2's file byte-for-byte, with this branch's own (v0.261.288) section inserted at the very top, above V2's first section, its own (v0.261.288). Every V2 section is unchanged. docs/admin/orchestration.md merged cleanly: V2's edits and this branch's two lines don't touch each other. The next commit renumbers this branch's section and VERSION to 0.261.290, above V2's 0.261.289. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
V2 now uses 0.261.289 (microsoft#1694, microsoft#1696) and has its own 0.261.288 release notes section, so this branch's version moves above both. Renumbers the same 20 lines in 15 files as the previous bump: config.py, this branch's release-notes section, the documentation version notes and the new tests' headers. V2's own (v0.261.288) release-notes section is untouched. The tests' version floor stays 0.261.253. Refs microsoft#1549 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Brings in V2 at 56fd78c (microsoft#1700, VERSION 0.261.290). Conflicts: - docs/explanation/release_notes.md: V2's file byte-for-byte, with this branch's own (v0.261.290) section inserted at the very top, above V2's first section, its own (v0.261.290). Every V2 section is unchanged. application/single_app/config.py merged cleanly: V2 and this branch both set VERSION to 0.261.290. V2's other changes, to Sidebar.tsx plus microsoft#1700's fix doc and two tests, don't touch this branch's files. The next commit renumbers this branch's section and VERSION to 0.261.291, above V2's 0.261.290. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
V2 now uses 0.261.290 (microsoft#1700) and has its own 0.261.290 release notes section, so this branch's version moves above it. Renumbers the same 20 lines in 15 files as the previous bump: config.py, this branch's release-notes section, the documentation version notes and the new tests' headers. V2's own (v0.261.290) release-notes section and microsoft#1700's fix doc and tests are untouched. The tests' version floor stays 0.261.253. Refs microsoft#1549 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Brings in V2 at 36f0af1 (microsoft#1701, VERSION 0.261.291, and microsoft#1702, VERSION 0.261.292). Conflicts: - application/single_app/config.py: V2's file byte-for-byte (VERSION 0.261.292). This branch changes only that line. - docs/explanation/release_notes.md: V2's file byte-for-byte, with this branch's own (v0.261.291) section inserted at the very top, above V2's first section, its (v0.261.292). Every V2 section is unchanged, including microsoft#1701's own (v0.261.291). docs/admin/orchestration.md merged cleanly: microsoft#1701's notes and troubleshooting rows and this branch's settings and hand-off pause rows are separate hunks. V2's other changes, to the Ask an agent step's access check, the V2 Control Center and their docs and tests, don't touch this branch's files. The next commit renumbers this branch's section and VERSION to 0.261.293, above V2's 0.261.292. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
V2 now uses 0.261.291 (microsoft#1701) and 0.261.292 (microsoft#1702), with their own release notes sections, so this branch's version moves above them. Renumbers the same 20 lines in 15 files as the previous bump: config.py, this branch's release-notes section, the documentation version notes and the new tests' headers. V2's own (v0.261.292) and (v0.261.291) release-notes sections and microsoft#1701's 0.261.291 notes in orchestration.md are untouched. The tests' version floor stays 0.261.253. Refs microsoft#1549 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Brings in V2 at 49a52bb (microsoft#1703, VERSION 0.261.293). Conflicts: - docs/explanation/release_notes.md: V2's file byte-for-byte, with this branch's own (v0.261.293) section inserted at the very top, above V2's first section, microsoft#1703's (v0.261.293). Every V2 section is unchanged. application/single_app/config.py merged cleanly: both sides set VERSION to 0.261.293. OrchestrationRunView.tsx, orchestrationPlan.ts and docs/admin/orchestration.md merged cleanly. microsoft#1703's changes there pass the server's after-run chart and diagram checks to the deliverables list and add its own notes. This branch's changes there are the hand-off approval floor, the hand-off step's argument text and its capability label, and its own settings and pause rows. They are separate hunks and don't interact. The next commit renumbers this branch's section and VERSION to 0.261.294, above V2's 0.261.293. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
V2 now uses 0.261.293 (microsoft#1703), with its own release notes section, so this branch's version moves above it. Renumbers the same 20 lines in 15 files as the previous bump: config.py, this branch's release-notes section, the documentation version notes and the new tests' headers. V2's own (v0.261.293) release-notes section and microsoft#1703's 0.261.293 notes are untouched. The tests' version floor stays 0.261.253. Refs microsoft#1549 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The foundry_consent fixture's .gitignore listed only harness.bundle.js. esbuild also writes harness.bundle.css beside the bundle, and the test's teardown removes only the .js, so a run left the CSS untracked in the working tree. The gap was exposed by bee0515, which restored the agent_delegation harness_build.py helper that test_foundry_consent_v2.py imports. Without that helper the test failed at import and never built anything. Add the four entries every sibling fixture's .gitignore has: harness.bundle.js.map, harness.bundle.css, harness.bundle.css.map and __pycache__/. Refs microsoft#1549 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
microsoft#1704, an open draft into V2, also claims 0.261.294, so this branch's version moves above every open pull request's claim. V2 is still at 0.261.293. Renumbers the same 20 lines in 15 files as the previous bump: config.py, this branch's release-notes section, the documentation version notes and the new tests' headers. V2's own release-notes sections are untouched. The tests' version floor stays 0.261.253. Refs microsoft#1549 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Paul Lizer (paullizer)
marked this pull request as ready for review
October 8, 2026 01:31
Paul Lizer (paullizer)
merged commit Oct 8, 2026
167096e
into
microsoft:paullizer-react-v2-ui
11 checks passed
Paul Lizer (paullizer)
added a commit
to paullizer/simplechat
that referenced
this pull request
Oct 8, 2026
Phase 7b (microsoft#1683, v0.261.295) merged, so both halves of Phase 7 are in V2 and the capability works end to end. Record 7b's scope and follow-ups, mark 6c and Phase 8 optional, add three V2 test follow-ups and the deferred count-before-Accept decision, and refresh the stale release-notes count. Docs only; VERSION unchanged. Refs microsoft#1543 microsoft#1546 microsoft#1549 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
14 of 24 tasks
Paul Lizer (paullizer)
added a commit
that referenced
this pull request
Oct 8, 2026
The base reached 0.261.295 with the workflow hand-off card (#1683) and the orchestration workflows capability (#1648), so this change moves to 0.261.296. config.py and the release notes conflicted on the version; the base's v0.261.295 section is kept as is, with this change's entry in a new v0.261.296 section above it, and the change's test headers, version floor and docs are renumbered to match. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
This was referenced Oct 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A hand-off card under the answer. When a plan's
workflow_handoffstep prepared a one-time workflow, V2 now shows a Workflow hand-off card under that answer. The card shows:Accept, Edit and Decline each ask for confirmation. Edit opens the prepared draft in the workflow editor, and saving it sends an edited accept. A button shows only when the server lists it in the item's
actions.Live run status after accept. The queued run shows as a "Handed-off workflow" row, which reuses 6b-2's
LiveRunRow, so it has the same Cancel run, Retry, Review and approve, Reconnect and Open run controls.run.id, then checked against the conversation, step and workflow. It's never matched on the orchestration run the answer is mounted on, because the run that prepared the hand-off can be an earlier attempt.The plan side.
workflow_handoffjoins V2's approval floor, so timed and auto plans with an enabled hand-off step wait for the user even if the server's floor marker is missing.No server changes. 7a's setting,
enable_chat_orchestration_workflow_handoff, stays off by default. With it off, the planner never offers the step, so answers look as they did before. An answer made while it was on shows its card as unavailable, with the reason, instead of vanishing.Linked issue
paullizer-react-v2-ui.Release Notes & Latest Features
Is this visible to end users?
Is this admin-facing (Admin Settings, governance, deployment, config)?
The setting, Hand Off Large Work From Chat, is 7a's. This PR changes only two rows of
docs/admin/orchestration.md, which now say what V2 shows.Should this become a Latest Feature card?
I've left that call to the coordinator. The feature is off by default and V2-only.
Screenshot needed for the card?
Version bump
application/single_app/config.pyVERSIONthird segment bumped, or not needed because this is docs-onlydeployers/version.txtbumped, or not needed becausedeployers/was not changedVERSIONis0.261.295, set in its own commit,1b2598f52, the last commit on the branch.The new tests assert the floor
assert_app_version_at_least("0.261.253").deployers/is unchanged.Why .295. V2's
config.pyand its newest release-notes section are both at .293, which Fix orchestrated action charts that were drawn but never shown #1703 set. Before that, V2: Fix left sidebar links opening the classic UI instead of V2 #1700 used .290, and Fix V2 Orchestrate Ask an agent steps refused before the agent runs #1701 and Fix V2 Control Center Dashboard, Users and Groups 500s; remove Data health #1702 used .291 and .292. Azure Files Search action with live file permission checks, and File Sync Azure Files fix #1704, an open draft into V2, claims .294, and the other open PRs into V2, Shared map server core: persistent, phase-tagged maps for agents (Phase 1, 0.261.254) #1638 (.254) and Update roadmap status: 6b-2 and 7a done, with the #1647 lineage follow-up #1648 (.252), are lower. So .295 is the next number above V2 and every open PR's claim.How the number moved. After each merge in which V2 had reached this PR's number, I moved this PR's number above V2's, in a commit of its own. The last move, to .295, didn't follow a merge: V2 hadn't moved, and the coordinator moved this PR above Azure Files Search action with live file permission checks, and File Sync Azure Files fix #1704's .294.
14fccd1ebset the coordinator's provisional .256 inconfig.py. The code and docs commits already used .256.82377db8b), .279 (92dc0e1d9), .281 (ba4706b50), .282 (e17426b63), .287 (bd4e774cc), .288 (aa6632c9d), .290 (b8b49f121), .291 (3c9adb702), .293 (e21bb7526), .294 (4417d28a8) and .295 (1b2598f52).config.py, the release-notes heading, 4 other docs, 6 functional and probe files, and 3 UI tests. A script checked that each changes only the version number on those lines.V2 merges. V2 kept moving while this PR was open, so I merged it 12 times, always with a merge commit and never with a rebase or force-push.
6faf72867485d51aafconfig.py, release notes9ec0d2457919375b4126e1ccbd7705774de1config.py, release notes304b91548b3c0b7b0dconfig.py, release notesfdd33343e7de8fcee3config.py, release notes260a044c9f1c3ddab0d4f506c0a01c49592bconfig.py, release notesae2ee7c1305fb0ced7041b96639d4cfde733config.py, release notes991a3882556fd78c1479092ac8336f0af182config.py, release notescd688038049a52bb5cconfig.pydiffers from V2's by at most the VERSION line. In the last three it's identical to V2's.What V2 changed near this PR. I read each batch before merging it.
route_backend_orchestration.pyis byte-identicalMessageList.tsx,OrchestrationPlanCard.tsxandorchestrationPlan.ts(describeRunProgress). None of its hunks touches the card mounts, the notice orAPPROVAL_FLOOR_CAPABILITIES.WorkflowEditorDialog.tsx. It keepsinitialDraftandonSaveOverride(draft, original), still sends 401, 403 and 404 toonAccessLost, and still has the labels the card test drives. Its newpresentationprop defaults to'dialog'.workflowEditorHrefandworkflowListHref, and approved them inscripts/check_xss_sinks.py.route_backend_orchestration.py(planning in shared conversations) andfunctions_orchestration_schema.py(integration_unavailable). The hand-off routes' block is byte-identical.config.pyand the release notes, with two exceptions, both outside this PR's hunks:MessageList.tsx.docs/admin/orchestration.md, where this PR changes two rows.config.pyand the release notes.docs/admin/orchestration.mdin three hunks (+12/−1), none of them in this PR's two rows.gatherstep exceptworkflow_runto feed the answer.workflow_handoffhas the registry rolereason(functions_orchestration_registry.pyL1625), so the rule doesn't apply to it, and the deliverables' hand-off block is untouched.orchestration_workflow_handoff_off_golden.jsonfor new planner-prompt wording, and 7a's off-golden test passes.lib/orchestration.ts,orchestrationPlan.ts(deliverableRowsgainedchecks?),OrchestrationRunView.tsx(achecksprop) anddocs/admin/orchestration.md(+10 lines), all outside this PR's hunks.WorkflowEditorDialog.tsx, Restyle V2 Workflows in the Admin Settings design language #1677's restyle above.components/ui/primitives.tsx, where onlyTogglegained two class props. The card usesGlassButton.lib/workflowRunLink.ts, where Restyle V2 Workflows in the Admin Settings design language #1677 added the two builders above.workflowRunHrefis unchanged.lib/apiClient.ts, where Add V2 Terms of Use and Approval Requests pages #1687 added a Terms of Use redirect. A 403 whoseerroristerms_of_use_requirednow sends the tab to the terms page, then throws as before. Hand-off refusals never carry thaterror: they put a sentence there, or "Forbidden" for the decorator's 403.ApiErrorandreadErrorMessageare unchanged.lib/orchestration.ts, where Fix orchestrated action charts that were drawn but never shown #1703 only added code:OrchestrationDeliverableCheck,deliverable_states?andnormalizeDeliverableChecks. The plan notice imports only theOrchestrationPlantype, which is unchanged.config.py) add and remove exactly the lines of the original code commit,e86d282dd. The other three are:bee051525(see Testing)foundry_consentfixture's.gitignore,0e8222f92(see Testing)f7e4033a9(see CodeQL)Testing / validation
Which head. The build, Node, UI and functional results below are from
4417d28a8. The two commits after it change onlyui_tests/fixtures/foundry_consent/.gitignore(+4 lines) and the same 20 version lines, so at this head,1b2598f52, I ran only these light checks and didn't rerun the UI, functional or Node suites:1b2598f52git check-ignore -vonharness.bundle.js.map,harness.bundle.css,harness.bundle.css.mapand__pycache__/inui_tests/fixtures/foundry_consent/.gitignore, lines 2 to 5git status --porcelaingit grep 0.261.2940.261.295, and none of its0.261.253floor lines changed(v0.261.295)section is on top, above V2's(v0.261.293), and the rest of the file is V2's, byte for byteEnvironment.
<venv python>below, withPYTHONPATH=application\single_app;functional_tests,PYTHONIOENCODING=utf-8andPYTHONUTF8=1.<flags>is-p no:langsmith_plugin -p no:cacheprovider -W ignore.Build.
npm --prefix .\application\v2_ui run typechecknpm --prefix .\application\v2_ui run buildnpm --prefix .\application\v2_ui run build -- --outDir ..\..\ui_tests\artifacts\orchestration-plan-editor(the bundle the UI tests load; the folder is git-ignored)Node.
node --test --test-concurrency=1over these 21functional_tests\files: 205/205 passed.test_v2_orchestration_{approval_logic,deliverables,merge_arguments,model_routing_logic,phase_groups,planner_display,planner_model_logic,progress_lane}.mjstest_v2_orchestration_workflow_{deliverable,handoff_client,results_naming,run_floor,run_links}.mjstest_v2_workflow_{delivery_messages,flow_node_reuse_logic,merge_task,proposal_merge,run_action_clients,run_link_routing,run_status,run_tracker}.mjstest_v2_orchestration_run_progress_status.mjscoversdescribeRunProgressinorchestrationPlan.ts, a file this PR also changes.test_v2_orchestration_streaming_surface.mjscovers the chat and orchestration stores and the stream controller.The restored test helper. #1672 (
e65ab0dbc) deleted theui_tests/fixtures/agent_delegation/folder. At V2's tip, five UI tests still importensure_bundlefrom itsharness_build.pyat module level, so they fail at import there:test_v2_workflow_run_card.py,test_chat_workflow_results.py,test_foundry_consent_v2.py,test_v2_notifications_bell.pyandtest_v2_workflow_alert_notices.py.RunHarnessfromtest_v2_workflow_run_card.py, so it would fail the same way.bee051525restores onlyharness_build.py, identical to this branch's base,16f42e4d2..gitignore. So the folder's other deleted files (harness_entry.tsx,harness.htmland.gitignore) aren't needed.test_foundry_consent_v2.pyruns again, and its run leftui_tests/fixtures/foundry_consent/harness.bundle.cssuntracked. esbuild writes the CSS the bundle imports next to the bundle, and the test's teardown deletes onlyharness.bundle.js, which was the only entry in that folder's.gitignore.0e8222f92adds the four entries the other fixture folders already have:harness.bundle.js.map,harness.bundle.css,harness.bundle.css.mapand__pycache__/. The test itself is unchanged.UI tests.
<venv python> -m pytest <flags> ui_tests\test_v2_orchestration_workflow_handoff_card.py ui_tests\test_v2_orchestration_workflow_handoff_plan.py ui_tests\test_v2_orchestration_workflow_handoff_run_status.py ui_tests\test_v2_orchestration_workflow_proposal_card.py ui_tests\test_v2_orchestration_workflow_run_floor.py ui_tests\test_v2_orchestration_workflow_run_links.py ui_tests\test_v2_orchestration_plan_card.pytest_v2_orchestration_plan_card.py. It sets_PAGEonly inmain(), so it fails the same way under pytest at the base.<venv python> ui_tests\test_v2_orchestration_plan_card.py: 7/7 passed.<venv python> -O -m pytest <flags>over the three new hand-off UI files: 92 passed.<venv python> -m pytest <flags>, one file at a time, over seven more UI files: 218 passed, 11 errors.test_v2_workflow_run_card.pytest_v2_workflow_run_tracker_spa.pytest_v2_orchestration_workflow_handoff_run_status.py, again on its owntest_chat_workflow_results.py, the rerun the coordinator required after Show a workflow's reply once in the conversation it created (0.261.253) #1637 mergedtest_foundry_consent_v2.pytest_v2_notifications_bell.pytest_v2_workflow_alert_notices.pyThe 11 errors aren't this PR's. All 11 tests pass, then fail their console check at teardown.
/api/m365/preferences,/api/m365/chat/connection,/api/m365/connections,/api/m365/bindings?page_size=20and/api/profile/fact-memory. Bring V2 User Settings to profile parity with an admin-style layout #1691 added those requests, inM365Cards.tsxandFactMemoryBench.tsx.49a52bb5c, with only the restored test helper added, the bell and alerts files give the same result: 107 passed and 11 errors, on the same 11 tests. A temporary pytest plugin logged the same five URLs there.Fix orchestrated action charts that were drawn but never shown #1703's tests. Fix orchestrated action charts that were drawn but never shown #1703 added or changed these. I ran them because it changed which plan steps must feed the answer and re-baselined 7a's off golden:
<venv python> -m pytest <flags> functional_tests\test_orchestration_action_chart_delivery.py: 15 passed, and 15 again as a script.<venv python> -m pytest <flags> functional_tests\test_orchestration_workflow_results_capability.py: 70 passed.<venv python> -m pytest <flags> ui_tests\test_v2_orchestration_dependency_plans.py: 34 passed.Functional tests.
<venv python> -m pytest <flags>over 19functional_tests\files: 525 passed. The files:test_v2_orchestration_workflow_handoff_xss_guardrail.py,test_v2_orchestration_workflow_run_links_xss_guardrail.py,test_v2_workflow_run_tracking_xss_guardrail.py,test_xss_guardrails_checker.pyandtest_orchestration_workflow_run_approval_floor.pytest_orchestration_workflow_handoff_editor_round_trip.py,test_orchestration_workflow_proposal_editor_round_trip.pyandtest_workflow_handoff_status_route.pytest_orchestration_workflow_handoff_{adapter,capability,imports,off_golden,planner,routes}.pyandtest_workflow_handoff_{builder,end_to_end,lifecycle,origin,result_reader}.py-O -m pytest, as scripts, and as-Oscripts:<venv python> functional_tests\test_v2_notifications_bell.py: 10/10 passed.Docs, run through
__main__.test_docs_app_surface_coverage.pytest_docs_site_quality.pytest_docs_latest_release_catalog_parity.pytest_docs_link_integrity.pytest_docs_release_notes_integrity.py49a52bb5c, and the brief lists both as known base failures:features.ymllinks (of 76). The broken sets are identical, so none of this PR's links is broken.reference/chat-controls-workflow-handoff-card.png, which shows a placeholder until someone captures it. Outstanding media doesn't fail the test.python .\scripts\build_docs_inventory.pyregenerated the inventory with no content change.Also: mutation testing killed 37/37, all six guardrails reported no failures at this head, and CodeQL reports no new alerts at this head. All three are below.
Documentation
Release notes updated, or not needed
Feature documentation updated, or not needed
Fix documentation updated, or not needed
Release notes: a new
### **(v0.261.295)**section above(v0.261.293)indocs/explanation/release_notes.md.Feature doc: a new "The V2 hand-off card" section in
docs/explanation/features/CHAT_ORCHESTRATION_WORKFLOW_HANDOFF.md, plus additions to Usage, the test table and Known limitations.f41c26cefalso corrects two of 7a's sentences there: only anall_matchesquery pauses over its limit (see Follow-ups).Fix doc: not needed, because there's no bug fix.
Other pages:
docs/admin/orchestration.md: the setting's row now names the V2 card. The over-limit pause row points to Cancel run on the card and says only an all-matches query pauses.docs/guides/trigger-a-workflow.md: a new "Hand large work off to a workflow" section, and six hand-off rows in the "Other run problems" table.docs/reference/chat-controls.md: a new "Workflow hand-offs (V2 interface)" section, with a screenshot slot.Checked and left unchanged:
docs/_features/workflow-results-in-chat.md,docs/_features/workflow-ai-assistant.mdanddocs/guides/ask-about-workflow-results.md. The roadmap is untouched.Security checklist
New Flask routes include
@swagger_route(security=get_auth_security())Settings sent to non-admin frontends use
sanitize_settings_for_user()Browser JavaScript is served from local SimpleChat static assets only; no CDN-hosted JS
No secrets, keys, connection strings, or local-only artifacts are included
Routes: no Flask routes were added or changed, so the guardrail's swagger check skipped itself.
Settings: no settings are sent anywhere new.
Browser code: no new dependencies, CDN references or dynamic imports. Everything ships in the V2 bundle.
Fixtures: they use fake ids and
http://simplechat.test. The build output underui_tests/artifacts/is git-ignored.XSS:
dangerouslySetInnerHTML.workflowProposalLinkandworkflowRunHref.encodeURIComponent.Decisions made autonomously
WorkflowRunCard.tsxnow exportsLiveRunRow, with two optional props:heading(default "Started workflow") andwaitingText(defaultworkflowWaitingText). A hand-off's run reads and acts exactly like 6b-2's, and 6b-2's own rows are unchanged.code, and the decorator 403'serroris "Forbidden", soApiError.messageis never shown for them.capabilities_used, and the server's list decides what it shows. With a gate closed, every item readsunavailable, so the card says why instead of vanishing.onAccessLost.EDIT_LIMITSshows under the buttons, and a refusal adds up to 3 of the server'serrors[]. There are no field locks, because adding them would change the editor for every caller.docs/guides/trigger-a-workflow.md, after "Run a workflow from chat"._reject_mixed_workflow_handoff). It also keepsWorkflowRunCard'sextrafrom listing the hand-off's run a second time with the generic pause text.A finding about
task_prompt.workflowForSavesets a v3 workflow'stask_promptfrom its first task's instructions, but the server set the hand-off's from its name. Sending the editor's value back would make an untouched save count as an edit.workflowHandoffEditdropsidand, for v3,task_prompt.functional_tests/test_orchestration_workflow_handoff_editor_round_trip.py::test_saving_the_handoff_untouched_is_not_an_editcovers it. The test bundles the realnormalizeWorkflowDefinitionandworkflowHandoffEditwith the app's esbuild, runs them under node, and passes the result between the real draft and accept routes.Error and state mapping
States
A button shows only when the item's
actionslist it. The Buttons column shows what the server lists for each state.pendingcreatingcreatedqueuedchat_delivery, it adds "The run's outcome is posted in this chat when it ends."deniedexpiredunavailableinvalidcreatedorqueuedwith reasonworkflow_deletedPolling while
creating. The card checks again every 3 seconds, at most 40 times or for 120 seconds. It skips checks while the tab is hidden and checks again when the tab is shown. After that it says "This is taking longer than expected." with Check again.The details, which show only for a disclosed item:
disclosure.textThe card's name is
workflow.name, elsesummary.name, else "Workflow hand-off". The eyebrow above it reads "Workflow hand-off".Buttons:
created, and "Starting…" while busy.workflowProposalLink) shows when the workflow exists, isn't deleted, and the hand-off isn't queued with a run.EDIT_LIMITSshows under the buttons: "Edits keep the trigger manual and keep the For each over documents or a workspace search. Tasks can use only your own local agents, and URL Access and Run as aren't available."Reasons
The first seven are the server's
WORKFLOW_HANDOFF_REASON_TEXT, verbatim. The server has no text for the rest, so V2 has its own.workflow_handoff_invalidworkflow_context_unavailablehandoff_loop_limithandoff_agent_unsupportedhandoff_unavailablehandoff_sources_unavailablehandoff_prepare_failedworkflow_handoff_disabled(V2)workflow_results_disabled(V2)workflow_role_required(V2)workflow_shared_conversation(V2)content_review(V2)workflow_deleted(V2)Refusals with a code
The text is V2's copy of the server's
ERROR_MESSAGES, verbatim.legacy_planusesLEGACY_PLAN_MESSAGE.invalid_requesthandoff_edit_invalidworkflow_handoff_disabledworkflow_role_requiredworkflow_shared_conversationhandoff_results_offhandoff_access_loststate:'created'run_not_foundhandoff_not_foundhandoff_unavailablestate:'created'handoff_expiredhandoff_deniedhandoff_acceptedhandoff_busyhandoff_kind_mismatchhandoff_run_conflictworkflow_run_not_started)state:'created'workflow_deletedhandoff_limit_changedlegacy_planhandoff_invalidhandoff_agent_unsupportedhandoff_daily_limithandoff_queue_failedstate:'created'service_unavailablehandoff_run_conflictreasons (V2's copy of the server'sWORKFLOW_RUN_REASON_TEXT):workflow_unavailableworkflow_already_runningworkflow_definition_changedworkflow_run_tombstonedworkflow_run_not_startedHow a coded refusal reads. The text above comes first, followed by up to 3 of the server's
errors[]messages. Each is cut to 240 characters and ends with a period, and repeats are dropped. An unknown code reads the fallback for where it happened.Try again (
workflowHandoffRetryable) is offered only for the three codes marked yes, and only while the item iscreatingor still offers accept. Decline and draft errors never offer it (coordinator note 1). It sends the same accept again. Withstate:'created', the server queues the same run, because the run's request id comes from the user and the hand-off.Refusals without a code, and other failures
allow_user_workflowsor personal-workflows decorators)TypeError)Response checks. Any other response reads as invalid.
handoff_idwithstate:'queued', and itscreatedmust agree with the status: true for 201, false for 200.handoff_idwithstate:'denied'.handoff_idand aworkflowobject.Fallbacks:
Where errors show:
role="alert"danger box.role="status"warning banner, with Try again to read the list again.Editing
…/draftand opensWorkflowEditorDialogon it.{conversation_id, mode:'edited', workflow}, whereworkflowis the editor's payload withoutidand, for v3, withouttask_prompt. An as-proposed accept sends{conversation_id, mode:'as_proposed'}.url_access_note.ApiError("<text> Your draft has been retained.", 400), so the editor keeps the draft. The list is then refreshed. A 401 still reaches the editor'sonAccessLost.The queued run
LiveRunRowheaded "Handed-off workflow", with 6b-2's controls, "Checked <time>" and "Check now".WORKFLOW_STATUS_HALTED_TEXT), inrole="status".paused): "Paused before reviewing any documents because more matched than one hand-off can review. Cancel it, then ask again with a narrower request." The row offers Cancel run and Open run, with no Approve and no Retry. A pause past the deadline reads as 6b-2's "Timed out", with "It reached its time limit.", instead.queuedrunning,ready_to_resume,resuming,waiting_recovery,cancellingwaiting_approval,waiting_output,paused,awaiting_approval,awaiting_sharing_approval,awaiting_analysis_approval,awaiting_run_as_approval,awaiting_sign_incompletedcompleted_partialfailed,invalid,incomplete,skippedcancelled,canceledConfirmations
created)The plan card and Review view
The notice has
role="note"and the label "Workflow hand-off in this plan". It shows when any enabled step isworkflow_handoff, and reads: "This plan hands work off to a one-time workflow, so it always waits for your approval." and "Approving this plan prepares a one-time workflow. Nothing runs until you accept it on the hand-off card that appears under the answer."The approval floor:
APPROVAL_FLOOR_CAPABILITIESis['workflow_run', 'workflow_handoff'], so a timed plan never counts down and an auto plan never runs itself, with or without the server's floor marker. A plan whose hand-off step is turned off, and that has no floor marker, counts down and runs as before.The chip reads "Hand off large work".
The step's arguments show three entries:
The name and titles render as text and have a matching
title.Sentinels (coordinator note 3). These tests assert that no instruction, handle, filter, tag, raw key, hand-off id, document id, digest, sidecar text or
result_outputsreaches the page as text or markup:ui_tests/test_v2_orchestration_workflow_handoff_plan.py::test_the_card_explains_the_hand_off_and_only_a_click_runs_it_oncecovers the Review view and the step's Named outputs, which show only thehandoffdeclaration and "Kind: structured-v1".::test_a_streamed_hand_off_step_shows_only_its_summaryand::test_a_stored_hand_off_step_shows_only_its_summarysend the step's retained result and the server-only sidecar on the step itself, and only the adapter's summary shows.Delivery footer, running tag and bell
6b-1 and 6b-2 are unchanged. A hand-off run is a chat-started run: accept queues it with
trigger_source='chat_orchestration'and achat_invocationnaming the producer plan run, the step and the hand-off. Here is what's checked, and by which test.functional_tests/test_v2_orchestration_workflow_handoff_client.mjs→ "a hand-off run posts back like any chat-started run: its footer, running tag and bell notice all find it". It runs the server's own status-route and delivery code through an inline Python probe, then V2's own functions on the result.Delivery footer.
workflow_deliverymetadata equals what 6b-1'sbuild_delivery_metadatawrites from accept'schat_invocation.parseWorkflowDeliveryMetadataround-trips it, including a nullorchestration_run_id,step_idorrequested_at.workflowDeliveryRunfinds the tracked row and the delivered row, whoseorchestration_run_idis the producer. It finds nothing for another chat, another workflow, a row that names the plan run the answer shows, another step, another conversation or an empty snapshot.ui_tests/test_v2_orchestration_workflow_handoff_run_status.py::test_a_handoff_result_posted_to_the_open_chat_reloads_it_and_names_the_workflow:Running tag.
workflowRunningLabelandworkflowRunningTagLabelread "Running <name>" while the run is queued, running or paused, and nothing once it's delivered.…run_status.py::test_the_running_tag_names_the_handoff_workflow_and_gives_way_to_the_unread_dot: the tag and its title show on the other chat's row, and the unread dot replaces them once the result is posted.Bell.
NOTIFICATION_TYPES,workflow_chat_deliveryis{bi-activity, info}, which V2'sdescribeNotificationreads as "Workflow results" (info).chat_response_completeis{bi-chat-dots, success}, which V2 reads as "AI responded" (ok)._send_chat_noticecallscreate_chat_response_notification, which sendschat_response_complete, and_send_noticesendsNOTIFICATION_TYPE.…run_status.py::test_a_handoff_result_posted_to_another_chat_marks_it_unread_and_notifies_once:{conversationId, messageId, runId, source:'workflow'}simplechat-conversation-<id>Gap. No test renders a hand-off's own row in the bell panel. The rendered "Workflow results" row is covered only by the generic
ui_tests/test_v2_notifications_bell.py::test_a_chat_run_notice_reads_as_workflow_results_and_opens_its_run_in_v2, and "AI responded" by::test_bell_sits_beside_the_brand_and_its_panel_lists_every_kind_of_notice. It's listed under Follow-ups.The rest of the run row is covered by
…run_status.py:test_the_queued_handoff_run_shows_live_from_the_run_that_prepared_ittest_an_over_limit_pause_says_why_and_offers_cancel_rather_than_approvetest_a_pause_past_the_deadline_reads_as_timed_out_not_as_the_over_limit_pausetest_a_handoff_answer_shows_its_run_once_and_never_the_proposal_or_run_cards, for both the mounted run and an earlier attempttest_a_tracked_run_the_handoff_did_not_queue_is_not_shown_as_its_runtest_with_workflow_runs_off_the_list_says_unavailable_and_nothing_reads_statustest_with_personal_workflows_off_the_card_says_so_and_nothing_reads_statustest_a_page_loaded_with_a_tracker_flag_off_shows_the_queued_run_staticallytest_handoff_and_run_names_render_as_textfunctional_tests/test_workflow_handoff_status_route.pyruns the real status route for an accepted hand-off's run (test_the_status_route_lists_the_accepted_hand_off_run_for_the_card,test_a_later_attempt_shows_the_run_its_producer_names).Mutation testing
37/37 killed.
When. These ran on the code commit,
e86d282dd, and weren't rerun after the merges, because the code lines they mutate are unchanged (see "This PR's code is the same as before the merges"). The only test change since then is the CodeQL fix,f7e4033a9. It empties the run-links list thatRunHarnessbuilds for each instance instead of replacing it, so the run-status test checks the same thing.Method:
Each mutation was applied to the source, the V2 bundle was rebuilt where a UI test needed it, the named tests ran, and the source was restored.
A mutation counts as killed only when a test fails on an assertion. A setup error doesn't count.
The first runs of M10a, M10b, M14a, M14b, M15 and M18 stopped at the UI tests' stale-bundle check. Each was rebuilt and re-run, and every final record is an assertion failure.
Every baseline passed with no mutation:
.mjsrun_floor.mjsThe test files:
functional_tests/test_v2_orchestration_workflow_handoff_client.mjsfunctional_tests/test_v2_orchestration_workflow_run_floor.mjsfunctional_tests/test_v2_orchestration_workflow_handoff_xss_guardrail.pyui_tests/test_v2_orchestration_workflow_handoff_*.pyfilescreatedneedn't agree with 201modeactionsworkflow_handofftask_promptGuardrails
I ran the coordinator's runner against V2's tip, the base of this PR's diff:
<venv python> run_v2_guardrails.py <worktree> 49a52bb5c841d98781030b5da49418a3738edb7e HEAD 7bAt this head,
1b2598f52, it found 25 changed files, and all six checks reported no failures.Keyword findings by category:
The most findings are in the client
.mjs(236), the card UI test (184), the run-status UI test (84),workflowHandoffs.ts(67) and the plan UI test (65).I reviewed the findings. All process use is in tests:
subprocess.runruns esbuild and node on a temporary bundle in the editor round trip, and the bundle is deleted afterwards.subprocess.runin the restoredharness_build.pyruns the app's own esbuild under node, with an argument list and no shell, to bundle a UI test fixture.execFileSyncruns the inline Python status-route probe in the client.mjs.ast.literal_evalreads string constants from the server source in the card and plan UI tests.The only URL host is
http://simplechat.test.In CI,
malicious-pr-security-reviewpassed. It posts 10 of its keyword findings as warning annotations, and I read each line:WorkflowHandoffCard.tsx(L6 and L11)role="note"and its icon'saria-hiddeninOrchestrationWorkflowHandoffNotice.tsx(L21 and L26)OrchestrationRunView.tsx(L114)MessageList.tsx(L1181–1185)CodeQL
No new alerts. CodeQL has analyzed
refs/pull/1683/mergefor three languages on each of five merge refs, 15 analyses in all.1896aaf36b3c0b7b0d92dc0e1d9(.279)99e5fcf28f1c3ddab0ba4706b50(.281)f9d03f7edd4cfde733b8b49f121(.290)729b69c9349a52bb5c4417d28a8(.294)aa8180bed49a52bb5c1b2598f52(.295)The first four rows come from the code-scanning analyses list, where every analysis ran the same number of rules: 172 for Python, 201 for JavaScript/TypeScript and 27 for Actions. For the fifth, the code-scanning API refused this session's token (HTTP 403), so its result is the
CodeQLcheck run's, below. Each of that ref's three Analyze jobs checked outaa8180band finished uploading its results. The two commits since4417d28a8change only a.gitignoreand 20 version lines.The one alert, fixed. Code-scanning alert 4087,
py/overwritten-inherited-attribute, flaggedui_tests/test_v2_orchestration_workflow_handoff_run_status.pyL231. There,HandoffRunHarness.__init__replaced thelink_itemslist thatRunHarness.__init__sets with a new empty one.f7e4033a9empties that list withself.link_items.clear()instead.RunHarness.__init__builds a fresh list for each instance, so the test checks the same thing.fixedas of729b69c93, the last merge ref the API let me read. I didn't dismiss any alert.At this head,
1b2598f52, on merge refaa8180bed:CodeQLcheck run (113048454937) succeeded with "No new alerts in code changed by this pull request" and no annotations.Analyze (python)(113048080207),Analyze (javascript-typescript)(113048080266) andAnalyze (actions)(113048080066) succeeded. Each has one annotation, a notice that theubuntu-latestlabel moves to Ubuntu 26 from October 19, 2026.Follow-ups
paused, because the over-limit pause has no reason code. Any other pause of a hand-off run without a reason reads the same. A server reason code would make it exact.availableflag. That flag comes fromworkflow_run_settings_gate(functions_orchestration_workflow_context.pyL283–296), which also needsworkflow_runon the capability allowlist.workflow_handoffbut notworkflow_runhides Retry, and 6b-2 shows "Starting workflows from chat is turned off, so Retry isn't available here." The resume route (route_backend_workflows.pyL1571) would accept the retry.node_modules. It fails, rather than skips, whenapplication/v2_ui/node_modulesis missing. CI would neednpm cifirst.allow_user_workflows. The run page itself refuses.workflowDeliveryRunlooks a row up by key (snapshot.runs[delivery.run_id]) without comparing itsrun_id.handoffdeclaration.workflowForSaverewrites a v3task_prompton regular saves.ui_tests/test_v2_orchestration_plan_card.pyworks only through__main__.M365Cards.tsxandFactMemoryBench.tsx(see Testing). The fix is stubs in those V2 tests; this PR doesn't change them.CHAT_ORCHESTRATION_WORKFLOW_HANDOFF.md, in "The one-time lifecycle" and Known limitations, and the pause row ofdocs/admin/orchestration.md's troubleshooting table. Only anall_matchesquery pauses; abest_nquery reviews only its best N.f41c26cefcorrects all three, along with the same wording in this PR's own card section and the guide's pause row.docs/reference/chat-controls.md, and the guide section has none.assert_app_version_at_least("0.261.253"), directly or throughIMPLEMENTED_IN. The headers, as renumbered after the merges:.tsprobe sayVersion: 0.261.253andImplemented in: 0.261.295..mjssays .295 for both.test_v2_orchestration_workflow_run_floor.mjs, which this PR extends, saysVersion: 0.261.295and keepsImplemented in: 0.261.212.0.261.295.harness_build.pykeeps its own .093 header.49a52bb5c(Fix orchestrated action charts that were drawn but never shown #1703), renumbering whenever V2 reached this PR's number, and once more, at the coordinator's call, to clear Azure Files Search action with live file permission checks, and File Sync Azure Files fix #1704's .294. It's now .295 (see "Version bump"). V2 hasn't moved since.VERSION = "0.261.294", so this PR moved to .295 and the two no longer collide. If V2 reaches .295 or higher before this PR merges, this PR moves to the next number above every open PR's claim.ui_tests/fixtures/agent_delegation/harness_build.py, which five UI tests import. This PR restores it (see Testing). If V2 Admin Settings: manage global agents and actions, fix category scroll #1672 meant to remove it, those tests needensure_bundlefrom somewhere else.