diff --git a/docs/explanation/features/CHAT_ORCHESTRATION_WORKFLOWS_ROADMAP.md b/docs/explanation/features/CHAT_ORCHESTRATION_WORKFLOWS_ROADMAP.md index f3f9ce0a7..556008f10 100644 --- a/docs/explanation/features/CHAT_ORCHESTRATION_WORKFLOWS_ROADMAP.md +++ b/docs/explanation/features/CHAT_ORCHESTRATION_WORKFLOWS_ROADMAP.md @@ -7,7 +7,7 @@ doesn't change the version. Each phase records the version it ships in. Planning branch: `paullizer-orchestration-workflows-capability`. -Status updated: **2026-10-01**, with `paullizer-react-v2-ui` at 0.261.213. The PRs for this work target that branch +Status updated: **2026-10-01**, with `paullizer-react-v2-ui` at 0.261.216. The PRs for this work target that branch (§10). This is the master plan for letting chat orchestration propose, create, run and hand off saved workflows. It's the @@ -26,10 +26,10 @@ Dependencies: V2 chat orchestration (`functions_orchestration*.py`), durable wor | 0 | Tracking: roadmap doc + GitHub issues | [#1543](https://github.com/microsoft/simplechat/issues/1543) (umbrella) | — | **Done** ([#1557](https://github.com/microsoft/simplechat/pull/1557)) | | 1 | Calendar schedules (day, time, timezone) | [#1544](https://github.com/microsoft/simplechat/issues/1544) | — | **Done**: [#1561](https://github.com/microsoft/simplechat/pull/1561), v0.261.193 | | 2 | Workflow draft service (dry-run build, blueprint builder, provenance) | [#1545](https://github.com/microsoft/simplechat/issues/1545) | 1 | **Done**: [#1566](https://github.com/microsoft/simplechat/pull/1566), v0.261.202. It also took gotcha 13 and Phase 1's two V2 editor follow-ups | -| 3 | AI workflow assistant (Score-style assisted editing in the V2 editor) | [#1548](https://github.com/microsoft/simplechat/issues/1548) | 3a: — · 3b: 2, A2 · 3c: 3a, 3b, A1 | **3a done**: [#1569](https://github.com/microsoft/simplechat/pull/1569), v0.261.203. **3b done**: [#1577](https://github.com/microsoft/simplechat/pull/1577), v0.261.208. **3c done**: [#1593](https://github.com/microsoft/simplechat/pull/1593), v0.261.213. Phase 3 is complete | +| 3 | AI workflow assistant (Score-style assisted editing in the V2 editor) | [#1548](https://github.com/microsoft/simplechat/issues/1548) | 3a: — · 3b: 2, A2 · 3c: 3a, 3b, A1 | **3a done**: [#1569](https://github.com/microsoft/simplechat/pull/1569), v0.261.203. **3b done**: [#1577](https://github.com/microsoft/simplechat/pull/1577), v0.261.208. **3c done**: [#1593](https://github.com/microsoft/simplechat/pull/1593), v0.261.213. Phase 3 is complete. Follow-up [#1596](https://github.com/microsoft/simplechat/pull/1596), v0.261.215, makes personal **Draft Workflow Instructions** follow the assistant setting | | 4 | Orchestration proposes workflows (`workflow_propose` + Approve / Deny / Edit card) | [#1547](https://github.com/microsoft/simplechat/issues/1547) | 2 | **Done**: [#1580](https://github.com/microsoft/simplechat/pull/1580), v0.261.207, behind **Propose Workflows From Chat** (off by default). It includes personal File Sync authoring in the V2 editor (gotcha 58) | | 5 | Orchestration runs existing workflows (`workflow_run`, start-and-link) | [#1551](https://github.com/microsoft/simplechat/issues/1551) | 4 | **Done**: [#1594](https://github.com/microsoft/simplechat/pull/1594), v0.261.212, behind **Run Workflows From Chat** (off by default) | -| 6 | Results back in chat: 6a results reader + **Follow up**; 6b post-back delivery, run card and chat-list indicator; 6c in-plan wait (later) | [#1546](https://github.com/microsoft/simplechat/issues/1546) | 6a: 4 · 6b: 5, 6a, N1 | **6a in review**: [#1592](https://github.com/microsoft/simplechat/pull/1592). 6b and 6c not started | +| 6 | Results back in chat: 6a results reader + **Follow up**; 6b post-back delivery, run card and chat-list indicator; 6c in-plan wait (later) | [#1546](https://github.com/microsoft/simplechat/issues/1546) | 6a: 4 · 6b: 5, 6a, N1 | **6a done**: [#1592](https://github.com/microsoft/simplechat/pull/1592), v0.261.214, behind **Use Workflow Results In Chat** (off by default). **In progress**: the `workflow_results` planner capability, and 6b as two stacked PRs, 6b-1 (server delivery and status route) and 6b-2 (V2 run card, tracker and chat-list indicator). 6c not started | | 7 | Hand-off of big one-time jobs | [#1549](https://github.com/microsoft/simplechat/issues/1549) | 4, 6b | Not started | | 8 | Follow-ons: group workflows, #1347 parity, plan-replay task | [#1550](https://github.com/microsoft/simplechat/issues/1550) | 4+ | Not started | | A1 | Shared AI-assist thread: immediate send, Cancel/Retry, one component for every assist editor | [#1552](https://github.com/microsoft/simplechat/issues/1552) | — | **Done**: [#1564](https://github.com/microsoft/simplechat/pull/1564), v0.261.200 | @@ -42,14 +42,15 @@ Dependencies: V2 chat orchestration (`functions_orchestration*.py`), durable wor Track A (AI-assist UX) is independent of the workflow phases, and both of its parts have landed (#1564). Phase 3 shipped as three PRs: 3a (change tracking), 3b (the assist endpoint) and 3c (the **Ask AI** tab, which also works on a proposal opened with **Edit**). Phase 5 (#1594) lets a plan start a saved workflow and link to the run, and 6a -(#1592), the results reader and **Follow up**, is in review. Tracks N (V2 notifications) and P (document provenance) -are independent too, and all three of their items have landed, so 6b can rely on the V2 bell for undeliverable -results. +(#1592) lets chat answer from a finished run's stored result, with **Follow up**. The `workflow_results` planner +capability and 6b, split into a server PR and a stacked V2 PR, are in progress. Tracks N (V2 notifications) and P +(document provenance) are independent too, and all three of their items have landed, so 6b can rely on the V2 bell for +undeliverable results. Repository follow-ups found along the way, not tied to one phase: - The generated release-notes pages under `docs/explanation/release-notes/` are stale, so - `test_docs_release_notes_integrity.py` fails on the base, with 149 stale releases at 0.261.213. The PRs above leave + `test_docs_release_notes_integrity.py` fails on the base, with 152 stale releases at 0.261.216. The PRs above leave them alone; regenerate them once, in a docs-only change, after the in-flight PRs land. - Other docs pages have pre-existing broken relative links (`test_docs_link_integrity.py`). - **Fixed in [#1576](https://github.com/microsoft/simplechat/pull/1576)** (#1571): the PR guardrail workflows (broken @@ -72,9 +73,16 @@ Repository follow-ups found along the way, not tied to one phase: every node on each render. Its handlers are stable and an unchanged node keeps its object (`lib/workflowFlowNodeReuse.ts`), so React Flow keeps the measured handles and the edges stay attached (`V2_WORKFLOW_FLOW_CANVAS_STABLE_RERENDERS_FIX.md`). +- **Fixed in [#1597](https://github.com/microsoft/simplechat/pull/1597)** (v0.261.216): the 19 open CodeQL + `py/log-injection` alerts are cleared. The search cache logs and the workflow run cancellation error logs pass + identifiers and exception text through `sanitize_log_message`, and the admin plugin settings route logs only the + number of fields it received instead of the request body (`LOG_INJECTION_CODEQL_ALERTS_FIX.md`). The older + `py/clear-text-logging-sensitive-data` results need separate triage. The ones in `utils_cache.py` trace back to + `_get_trusted_group_upload_scope_ids`, which CodeQL treats as a secret because of its name, so they're likely false + positives. The ones in `functions_appinsights.py` haven't been analyzed. - `test_v2_admin_settings_schema.py` fails on `paullizer-react-v2-ui`, because `m365_trusted_download_hosts` has a `[]` default on a string field. It appears to pass under `python -O`, because that check is a bare `assert` in - `__main__`. + `__main__`. Whether the field type or the default should change is a product decision. - Other failures on `paullizer-react-v2-ui` that the phase PRs report and leave alone: - `test_v2_prompt_composer_card.py`: two tests fail with `NameError: prepared_chat_image_references`. #1585 made the shipping-persistence block in `route_backend_chats.py` read that name, and the test's extracted copy of the @@ -93,6 +101,33 @@ Repository follow-ups found along the way, not tied to one phase: `test_orchestration_plan_revision_routes.py`, `test_run_record_keeps_the_turn_and_question_ids` in `test_orchestration_turn_recovery_identifiers.py`, and `test_revoked_record_refresh_clears_the_previous_page` in `ui_tests/test_v2_workflow_loops.py`. +- **In review in [#1598](https://github.com/microsoft/simplechat/pull/1598)** (tests only): it fixes the composer-card + `NameError`, the auto-open fixture, the block-revision prompt name, the three single tests above, and the stub + modules that `test_workflow_document_picker_recent_targets.py` leaves in `sys.modules`. It also brings + `test_app_settings_auxiliary_writers.py`, `test_v2_admin_workflow_parity.py`, + `ui_tests/test_v2_group_participant_invites.py` and `test_group_collaboration_source_storage_fix.py` up to date with + the code they test. The context picker, runtime integration and telemetry order failures above aren't part of it. +- Four `non_group_invitee` subtests in `test_group_collaboration_source_storage_fix.py` still fail after #1598, and + the test is right. `ensure_personal_collaboration_for_legacy_conversation` and + `ensure_group_collaboration_for_legacy_conversation` (`functions_collaboration.py`) read the source chat's history + through `prepare_m365_history_publication` before the invitees are validated, so a rejected invitee still causes a + history read. The fix is to validate invitees first. +- Stale assertions that #1596 found on the base and left alone: + - `test_workflow_instruction_drafting.py` expects the prompt wording "workflow or task instructions" and the + **Workflow or Task Instructions** label. The prompt now says "workflow instructions", and the label is + **Task Instructions**. + - `test_workflow_access_controls.py` expects 14 personal workflow route gates. There are now 32, and each still has + both decorators. + - `test_governance_route_and_wiring_coverage.py` expects `workspace_governance = {` in + `route_frontend_workspace.py`, which now reads it from `workspace_availability`. + - `test_model_endpoint_capacity_save_validation.py`: both cases fail because the test's offline subprocess attempts + network access while importing and trips the test's own guard. The cause isn't diagnosed yet. +- Importing `route_backend_workflows` first in a fresh process fails with `NameError: enabled_required`, raised in + `route_backend_agents.py`. `functions_settings` loads first, and its imports reach `functions_public_workspaces`, + which imports `functions_authentication`. Its `from functions_settings import *` then copies a half-loaded module + that doesn't have `enabled_required` yet. The app isn't affected, because `app.py` loads `functions_authentication` + (through `semantic_kernel_loader`) before `functions_settings`. A test that imports a route module directly has to + import `functions_authentication` first, as #1597's import test does. ## 1. Goal @@ -947,9 +982,10 @@ Phase 3 ships as three PRs. 3a needs nothing else, 3b needs Phase 2 and A2, and pending block focus request, so the Flow canvas can't take focus back. - **Follow-ups** from #1593: - A Latest Feature card, once `docs/images/guides/create-a-workflow-ask-ai.png` exists. - - Settled for personal scope in v0.261.215: `/api/workflows/draft-instructions` refuses personal requests with - 403 `workflow_assistant_disabled` when `enable_workflow_ai_assistant` is off for the person, and the classic - personal workspace hides its draft controls. Group-scope AI drafting is to be revisited with Phase 7 (#1550). + - Settled for personal scope in [#1596](https://github.com/microsoft/simplechat/pull/1596), v0.261.215: + `/api/workflows/draft-instructions` refuses personal requests with 403 `workflow_assistant_disabled` when + `enable_workflow_ai_assistant` is off for the person, and the classic personal workspace hides its draft + controls. Group-scope AI drafting is to be revisited with Phase 8 (#1550). - Structural flow edits (For each and If blocks, adding or moving tasks), File Sync operations, and the rest of #1577's list. - Disable **Reload workflow** during a save, check focus after a reloaded editor closes, and add browser tests @@ -1144,20 +1180,34 @@ chat later (decision #11). Nothing stays connected, and closing the browser does - **Done when** Follow up answers only from the stored run result, a changed result or lost access shows the unavailable message, "what did my digest find last Monday?" reads the right run, and shared chats can't read workflow results. -- **In review** in [#1592](https://github.com/microsoft/simplechat/pull/1592), behind the new admin setting - **Use Workflow Results In Chat** (`enable_chat_workflow_results`, off by default). The `workflow_results` planner - capability will be a separate PR after #1592 merges, under the same setting. +- **Done** in [#1592](https://github.com/microsoft/simplechat/pull/1592), v0.261.214, behind the admin setting + **Use Workflow Results In Chat** (`enable_chat_workflow_results`, off by default). Where it differs from the plan + above: + - The reader (`functions_workflow_result_reader.py`) excerpts each succeeded task's output, last task first, up to + 8 outputs and 48 KB by default. Structured (v3) runs answer 409 `workflow_result_unsupported` for now, because + their outputs need an exact node, execution and attempt. + - The entry points are **Ask in chat** on a run in the V2 Workflows page and **Ask about this** on a workflow alert. + The delivered message, the run card and the recurring-workflow card get theirs with 6b. + - The chip reads the run through `GET /api/user/workflows//runs//result-context`, which only + finds the signed-in person's own runs. + - Retry and Edit of a Follow up turn are refused (gotcha 63). + - The `workflow_results` planner capability is in progress as a separate PR, under the same setting. #### 6b — Post-back delivery, run card and chat-list indicator +**In progress** as two stacked PRs: 6b-1 is the server part, and 6b-2, branched from it, is the V2 part. + **Server** - When `workflow_run` starts a run from a private chat, the run gets a `chat_delivery` record: origin conversation, request message, orchestration run and step, requester, request time, model selection, `status: pending`, and `expires_at` (the durable deadline plus grace). It lives on the run (`personal_workflow_runs`, partition - `/user_id`), so there's no new container and no deployer change. -- The plan's answer message carries `workflow_run_link` metadata, which renders the card. The orchestration run then - completes normally. A run waiting for delivery never counts as an active plan. + `/user_id`), so there's no new container and no deployer change. It also carries a delivery generation, because a + retried run keeps its run ID (see the V2 actions below) and needs its own delivery. +- The card mounts the way Phase 5's **Started workflows** links do: from the answer's `metadata.orchestration`, + through `GET /api/v2/orchestration/runs//workflow-runs`. There's no `workflow_run_link` message key; Phase 5 + chose the route instead, because message metadata is readable by anyone who later reads the chat. The orchestration + run then completes normally. A run waiting for delivery never counts as an active plan. - Workflow finalization triggers delivery right away. A background sweep with a distributed lock picks up anything missed. - **Delivery steps**: @@ -1169,12 +1219,13 @@ chat later (decision #11). Nothing stays connected, and closing the browser does 5. Compose the answer with the request's model (re-authorized). Fall back to the default model, then to the trimmed result with a note. Run output is fenced as untrusted content. 6. Apply the same content-safety handling as other replies. - 7. Create the message with a deterministic ID, `uuid5(run_id, conversation_id)`. A conflict means it was already - delivered. + 7. Create the message with a deterministic ID built from the run, the chat and the delivery generation. A conflict + means it was already delivered. 8. Call `mark_conversation_unread` and `create_chat_response_notification` with an idempotency key (a new pass-through parameter). 9. Record `delivered` and the message ID. -- A failed or cancelled run posts a short, uncomposed note ("Weekly digest failed: …") with **Open run** and **Retry**. +- A failed or cancelled run posts a short, uncomposed note ("Weekly digest failed: …") with **Open run**. A failed run + also gets **Retry** when it can be resumed; a cancelled run can't. - **Can't deliver** (chat deleted, made shared, access lost, or expired): don't post. Create one bell notice ("Results from Weekly digest are ready") that links to the run. The result is still in the workflow's own conversation. - Waiting states (approval, Microsoft 365 reconnect) show on the card only. The existing Microsoft 365 approval notices @@ -1185,13 +1236,18 @@ chat later (decision #11). Nothing stays connected, and closing the browser does - the waiting reason and its action - delivery state and the delivered message ID - a sanitized error + - the workflow's scope, for the run link **V2** - **`WorkflowRunCard`** under the plan's answer, with the states above. Actions reuse existing routes: - - Cancel: `/runs//cancel` (`route_backend_workflows.py:1815`) - - Retry: `/resume-failed` (1902) - - Approve: `/runtime/decision` (1518) + - Cancel: `/runs//cancel` (`route_backend_workflows.py:1912`) + - Retry: `/runs//runtime/resume` (1570), with the runtime's `expected_version` and a new `request_id`. It + resumes the same run from its checkpoints, and only a `failed`, `invalid` or `incomplete` run can resume + (`RESUMABLE_STATES`, `functions_workflow_runtime_store.py:43`). It can answer 409 `workflow_definition_changed` + or `workflow_already_running`. `/resume-failed` (2027) isn't used: it answers 409 for durable workflows, and every + run that `workflow_run` starts is durable. + - Approve: `/runtime/decision` (1561) - Reconnect Microsoft 365, and Open run - **Checking** (decision #13): one tracker in the app shell, modeled on `startImageApprovalTracking` (`lib/imageProposalResume.ts:258`), so the chat list stays current from any page. @@ -1203,10 +1259,12 @@ chat later (decision #11). Nothing stays connected, and closing the browser does - **Recurring-workflow card**: once a Phase 4 proposal is approved, its card shows the schedule and timezone, the next run and the last run's status, with **Open latest results**, **Follow up** and **Open workflow**. It's checked on open only. Scheduled results stay in the workflow's own conversation (decision #4). -- **Run deep link**: a new V2 route, for example `/workspace/workflows/:workflowId/runs/:runId`, that opens the run - inspector. None exists today (`App.tsx:183-187`). Pointing `v2WorkflowRunPath` at it also moves Microsoft 365 - pending-action links and N2's alert buttons; see gotcha 61. Phase 5's **Started workflows** links use - `workflowRunHref` (`lib/workflowRunLink.ts`), so point it there too, or route both through one helper. +- **Run deep link**: reuse the query form Track P already opens, `/workspace/workflows?workflow_id=…&run_id=…`, or the + group's workflows page with the same query. One scope-aware helper serves both `v2WorkflowRunPath` + (`lib/notificationLinks.ts`, after the checks in gotcha 61) and Phase 5's `workflowRunHref` + (`lib/workflowRunLink.ts`), so Microsoft 365 pending-action links, N2's alert buttons and the **Started workflows** + links all follow it. A `/workspace/workflows/:workflowId/runs/:runId` route would collide with + `/workspace/:section/:resourceId` (`App.tsx`), and the group page ignores a resource ID. - **Done when**: - a run started from a private chat posts exactly one answer, through restarts, retries and concurrent sweeps - closing the browser, reloading, or pinning the chat and coming back later all work: the unread dot appears, with @@ -1232,7 +1290,9 @@ chat later (decision #11). Nothing stays connected, and closing the browser does ### Phase 8 — Follow-ons (separately scoped) -- **Group workflows**: group roles, eligible Run-as members, `allow_group_workflows` and group assignment. +- **Group workflows**: group roles, eligible Run-as members, `allow_group_workflows` and group assignment. This includes + the AI workflow assistant. Ask AI is personal only, and group **Draft Workflow Instructions** checks only group + workflow access, not `enable_workflow_ai_assistant` (#1596). - **#1347**: the SimpleChat action creates task-based workflows through the draft service. - **Plan-replay workflow task (M5C O2)**: replays a frozen, approved orchestration plan. This closes the vocabulary gap (web search, deep research, render_file, images) but needs a delegated-identity design for scheduled orchestration. @@ -1400,7 +1460,7 @@ chat later (decision #11). Nothing stays connected, and closing the browser does bound inputs by construction. **Settled in #1577** for the workflow assistant: a `#` document is a bound input and never narrows a search. 61. **Run links for Microsoft 365 pending actions.** When 6b gives `v2WorkflowRunPath` (`lib/notificationLinks.ts`) - a V2 run page, every `/workflow-activity` link moves to V2. The only notices with those links are Microsoft 365 + a V2 run link, every `/workflow-activity` link moves to V2. The only notices with those links are Microsoft 365 pending actions (`functions_m365_runtime.py`), and V2 can't approve or cancel them yet. Keep a notice whose metadata has `m365_pending_action_id` on the classic page, as the chat link rule for `m365_pending_action` does, until V2 renders pending actions. `v2WorkflowRunPath` also needs the workflow's scope, from the link's query or @@ -1430,9 +1490,9 @@ chat later (decision #11). Nothing stays connected, and closing the browser does | 3 | Setting key and default for the assistant; persist the side-channel transcript? | **Settled in #1577**: `enable_workflow_ai_assistant`, on by default, effective only when personal workflows are on and the user passes the WorkflowUser role rule. The transcript isn't stored: the client sends up to 20 completed turns with each request. | | 3 | Inline word diff for long text: a local helper or a bundled npm package? | **Deferred in #1569**: long text shows its previous value under **Previously**. When the word diff is added, start with a small local helper. | | 4 | Proposal expiry; where Deny state persists | **Settled in #1580**: proposals expire 14 days after they're created, and every decision, Deny included, is stored on the producing run's document. | -| 6a | How long run results stay available for Follow up | Until the run is deleted; add an admin retention setting later | +| 6a | How long run results stay available for Follow up | **Settled in #1592**: until the run is deleted. There's no retention setting yet. | | 6b | How long to keep trying to deliver | The durable deadline plus 24 hours, then the undeliverable notice | -| 6b | Run deep-link route | P (#1562) already opens a run with `?workflow_id=…&run_id=…` on the workflows pages. Reuse it unless 6b needs a separate run page. Either way, point both builders at it: `v2WorkflowRunPath` (`lib/notificationLinks.ts`), after the checks in gotcha 61, and `workflowRunHref` (`lib/workflowRunLink.ts`), which Phase 5's links use. Or route both through one helper. | +| 6b | Run deep-link route | **Decided for 6b-2**: reuse P's query form (`?workflow_id=…&run_id=…` on the personal or group workflows page) through one scope-aware helper that both `v2WorkflowRunPath` (`lib/notificationLinks.ts`, after the checks in gotcha 61) and `workflowRunHref` (`lib/workflowRunLink.ts`) use. No separate run page. | | 6c | Add an in-plan wait? | Only if planners need a workflow's output later in the same plan | | N1 | Where the bell sits in the sidebar | **Settled in #1563**: beside the brand mark in the header row, with a dot when collapsed | | N2 | Which entrance style ships | **Settled in the lab review**: A, the sidebar callout. It was judged with the rail collapsed and on a 360 px phone, not only on an expanded desktop | @@ -1448,9 +1508,9 @@ chat later (decision #11). Nothing stays connected, and closing the browser does 2. Pick the first phase whose dependencies are done. Settle its open decisions (§9), then plan that phase alone. 3. Use one branch and PR per phase, or per part where a phase is split (Phase 3). At the end of each, update the roadmap status table, the phase issue, the release notes, and the `config.py` version. -4. Base every PR on `paullizer-react-v2-ui`. The repository's default branch is `Development`, so set the base - explicitly. Merging into `paullizer-react-v2-ui` doesn't close issues, because `Closes #N` only fires on the default - branch. +4. Base every PR on `paullizer-react-v2-ui`, and branch from it too. The repository's default branch is `Development`, + so set both explicitly: a worktree or session created from the default branch starts at `Development`. Merging into + `paullizer-react-v2-ui` doesn't close issues, because `Closes #N` only fires on the default branch. 5. When a PR builds on another that hasn't merged yet, branch from that PR's head, open it against that PR's branch, and register the pair as a native GitHub stack once both are open. A native stack needs both heads in this repository, so when a head is pushed to a fork, the PR's base branch alone keeps the order. Keep branches current by