Repository navigation
Phase 6c: in-plan wait for a quick saved workflow - #1732
Merged
Paul Lizer (paullizer) merged 26 commits intoOct 9, 2026
Conversation
Adds enable_chat_orchestration_workflow_run_wait (default off) and chat_orchestration_workflow_run_wait_max_seconds (default 300, 60 to 1,800) with their V2 admin fields, the Classic Chat Orchestration pane inputs, the Classic form clamp, and validation on the settings writer. Nothing reads either setting yet. Part of microsoft#1546. Refs microsoft#1543. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The help listed only some of the rules, so an admin could conclude that a Microsoft 365 run-as or file-sync workflow can be waited on. Both the V2 field and the Classic pane now name every rule, plus the plan-level limits that come from resuming without the user's sign-in. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
When the in-plan wait setting is on and the saved workflow passes the quick-run rule, the planner offers it as waitable, the schema compiles a server-side wait marker, and drop_workflow_runs keeps the consumed run step. With the setting off, behavior is unchanged. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…pare-and-set A plan waiting on a saved workflow run holds the run's 6b post-back only while no post of that outcome has begun. It uses the result through a compare-and-set on the run's delivery record, and lets go on timeout so the post-back posts it exactly once. The worker waits out a live hold. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Adds the saved_workflow_run wait kind to the executor's resume path, the run step's hold, observe, consume and timeout logic, the reply note's wait lines, and two failure codes for runs that fail or are cancelled while a plan waits. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Compose reads a run the plan waited for only as fenced, untrusted notes: a result the plan took is read again through the workflow result reader with its digest, and any other outcome becomes an application-owned note. The answer's lineage carries the taken result so later reads re-authorize it. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Covers holding the post-back while waiting, using a result that finishes within the bound, the timeout hand-back with exactly one post, failed and cancelled runs, re-authorization failures mid-wait, refused continuations, and the finish-versus-timeout race in both orders. A run started without the post-back now reports it is still running rather than that the plan stopped waiting. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
With the wait setting off (absent, false, string and numeric values, and on without the 6b post-back), planner context, prompt, payload, repair turn, drop_workflow_runs, normalize_plan and execution are byte-identical to the fixture captured from e1657ef (0.261.304). Random plan, run and turn ids and timestamps are redacted; two base captures were identical. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…swer - The compact progress line shows the waiting workflow_run step's server summary (for example, Waiting for "Sales digest" to finish (up to 5 min).). Other waits keep the generic line. - The run card reads a delivery marked used_in_answer as "Its results were used in a chat answer." It never says the results were posted, and it shows no jump button. The tracker and the bell never announce it. - .mjs unit tests plus a Playwright test with hostile workflow names. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Plan replay (microsoft#1724) took 0.261.306 after V2 moved to 0.261.305. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…it rules in the research harness The V2 admin renderer evaluates each field's own conditions, so the cap field now repeats its gate's enable_chat_orchestration_workflow_runs condition. The offline research harness executes only explicitly supplied definitions; plan validation now asks the import-light wait rules about every plan, so the harness seeds the real compute/stored wait functions into the schema namespace. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Adds the feature doc, admin rows for Wait For Quick Workflows In Chat and its wait cap, the guide section on using a quick workflow's result in the same answer, the features.yml claim, the regenerated docs inventory and the v0.261.307 release notes. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
waited_run_consumers_valid previously required only that some enabled compose step bind the run's output through a required input. That let a plan mark a step as waiting even when the final response never read that compose step, directly or transitively, so the answer could finish without the result while the 6b post-back stayed held for the plan. Add a reader-reachability pass: starting from the run step, walk enabled compose steps whose required inputs bind the run or another reader, and require the final response's own step_id to land in that set. A final response with no step_id, a non-string step_id, or one equal to the run step itself is rejected up front. Covers: a reader whose text never reaches the answer, an answer that reaches the reader only through an optional input, a final response selecting a disabled step, a missing final response, and a final response without a step_id. Also confirms a second, unused reader is harmless as long as the answer itself needs the result, and that the stored-marker fast path accepts the same plan. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
_finalize passed composed=bool(prepared) to workflow_run_note, so the note could say a consumed run's result was used in the answer even when the reply itself was not written from the plan's steps, for example when only unplaced generated images populated prepared. workflow_results_note next to it already required error is None as well. Compute workflow_result_reads once, before building either note, and pass that same flag as workflow_run_note's composed argument. Both notes now agree on what "used in this answer" means. _wait_line's branching was already correct; only the flag _finalize handed it was wrong. Adds a test that drives a consumed wait through to completion and checks workflow_run_note's text both ways: with composed=True, "Its results were used in this answer."; with composed=False, "This answer couldn't use its results; they're in its run in Workflows." (coordinator note 3: "compose fails after a consume"). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Picks up 14 V2 commits since the prior merge base (817f9dd), including M365 pending-action cards, the V2 agents catalog, the scope-response contract fix and the group-switch latency fix. No Phase 6c file conflicted except release_notes.md, resolved by keeping this branch's section on top of V2's .306 and .305 sections, all above the shared .304 section. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
V2's tip landed at .307 (PR microsoft#1731) and the sibling Phase 8 replay branch has claimed .308, so this PR's version moves to .309 per the coordinator. Updates every self-authored test header, docstring, assert_app_version_at_least floor, feature/admin/guide doc mention and the release-notes section header. Leaves config.py VERSION and every other branch's own .307 references (V2 M365 pending actions, the roadmap doc) untouched; the VERSION bump itself follows as the final, separate commit. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Paul Lizer (paullizer)
marked this pull request as ready for review
October 9, 2026 01:52
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Paul Lizer (paullizer)
merged commit Oct 9, 2026
271b301
into
microsoft:paullizer-react-v2-ui
11 checks passed
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
enable_chat_orchestration_workflow_run_wait, default off), a chat plan can start one of the user's saved quick personal workflows (workflow_run), wait for that run to finish, and use its result in latercomposesteps of the same plan. Example: "Run my sales-digest workflow, then compare its totals with the Q3 report in my workspace." Before this change, the planner dropped any run step whose output a later step consumed, and the result only came back as a separate 6b post.quick_run_eligibility()derives a deterministic quick-run rule from the stored workflow and the settings. It's checked when the plan is normalized (the catalog'swaitableflag), again when the run starts, and on every continuation. A model-writtenworkflow_run_waitsmarker is always discarded.chat_orchestration_workflow_run_wait_max_seconds, default 300 s, range 60–1800.composeonly through the existingworkflow_resultsreader, which re-authorizes and fences the content as untrusted.chat_deliveryrecord decides whether the plan's answer or the 6b post-back delivers the result. That's the same record the 6b worker claims, and the decision lives in itsplan_waitfield. A consumed result is markeddelivered/used_in_answerwith no notice, so nothing posts and no bell fires, and the V2 run card says "Its results were used in a chat answer."one_time) workflows are never waitable. No new routes.For Phase 8 replay (#1724): the wait kind is exported as
SAVED_WORKFLOW_RUN_WAIT_KIND = 'saved_workflow_run'inapplication/single_app/functions_orchestration_workflow_run_wait.py. The plan marker shape isplan['workflow_run_waits'] = {step_id: {'version': 1, 'workflow': <catalog handle>}}.Linked issue
Part of #1546
Refs #1543
Release Notes & Latest Features
Is this visible to end users?
Is this admin-facing (Admin Settings, governance, deployment, config)?
Should this become a Latest Feature card?
Screenshot needed for the card?
Version bump
application/single_app/config.pyVERSIONthird segment bumped, or not needed because this is docs-only0.261.301(base) →0.261.309(final), in its own final commit. Originally assigned.307; after V2's tip landed at.307(PR Render Microsoft 365 pending-action cards in V2 chat (#1722 gap 2) #1731) and the sibling Phase 8 replay branch claimed.308, the coordinator reassigned this PR to.309. Every self-authored.307reference (test headers/assertions, docs, the release-notes header) was renumbered to.309in its own preceding commit, withconfig.pyleft untouched there; the VERSION bump is the strictly final commit.deployers/version.txtbumped, or not needed becausedeployers/was not changeddeployers/changed.Testing / validation
All commands use the pinned test venv (
...\aa1bf1e0-b10d-4312-8dbb-143e9e9f9966\files\venv\Scripts\python.exe) withPYTHONPATH=application/single_app;functional_tests,PYTHONIOENCODING=utf-8,PYTHONUTF8=1, and-p no:langsmith_plugin -p no:cacheprovider. Every new Phase 6c test file was also run aspytest -O, as a plain script, and as an-Oscript; all four ways pass (only the plain-pytestcounts are itemized below).New Phase 6c tests — all 8 files, one process:
test_orchestration_workflow_run_wait_{off_golden,eligibility,plan_rules,planner,execution,delivery,claims,results}.py→ 170 passed, 0 failedNeighbours (one file/group at a time, per the shared-machine rule):
test_orchestration_workflow_run*files → 458 passedtest_orchestration_workflow_results*files → 190 passedtest_workflow_chat_delivery*files → 383 passedtest_orchestration_native_waiting_continuation,test_orchestration_native_results,test_orchestration_native_infrastructure_runtime,test_orchestration_native_infrastructure_failures,test_orchestration_native_checkpoint_receipts,test_orchestration_native_adapter,test_orchestration_continuation) → 277 passedtest_orchestration_actions_admin.py+test_admin_settings_field_contract.py→ 10 passed, 14 subtests passed817f9dd07and confirmed identical — see Known base failures.Docs:
python scripts/build_docs_inventory.py→ regenerateddocs/_data/app_surface.ymltest_docs_app_surface_coverage.py(__main__) → 7/7 passedtest_docs_site_quality.py(__main__) → 6/6 passedtest_docs_link_integrity.py(__main__) → 2/5 passed — identical on base, see Known base failurestest_docs_release_notes_integrity.py(__main__) → 0/1 passed — identical on base, see Known base failuresV2 typecheck/build:
npm --prefix application/v2_ui run typecheck→ cleannpm --prefix application/v2_ui run build→ cleannpm --prefix application/v2_ui run build -- --outDir ../../ui_tests/artifacts/orchestration-plan-editor→ clean (the second bundletest_v2_workflow_run_wait.pyneeds)V2 unit tests (Node's test runner,
.mjs):test_v2_workflow_run_status.mjs+test_v2_workflow_run_tracker.mjs→ 61 passed (both modified, to add theused_in_answerdelivery reason)test_v2_orchestration_run_progress_status.mjs→ 12/12 passed (modified, to add the waiting-workflow line)V2 Playwright (
ui_tests/,pytestmark = pytest.mark.ui), one file at a time, free memory checked ≥3 GB first:test_v2_workflow_run_wait.py(new) → 7/7 passedtest_v2_notifications_bell.py→ 10/10 passedtest_v2_workflow_run_card.py(pre-existing, unmodified) → 40/41 passed — the 1 failure is pre-existing on base, see Known base failurestest_v2_workflow_run_tracker_spa.py(pre-existing, unmodified) → 5/6 passed — the 1 failure is pre-existing on base, see Known base failurestest_v2_orchestration_plan_card.py(pre-existing, unmodified; run aspython test_v2_orchestration_plan_card.py, since it predates the pytest-fixture convention and drives its own__main__) → 6/7 passed — the 1 failure is pre-existing on base, see Known base failuresGuardrails: 6/6 PASS, 0 blockers — see the Guardrails section below.
Post-merge catch-up (V2's tip merged in at push time,
08def99f7, PR #1731):All validation above ran pre-merge. After merging
origin/paullizer-react-v2-ui(which had advanced 14 commits past this branch's prior base — M365 pending-action cards (#1731), the V2 agents catalog (#1729), a scope-response-contract fix, and a group-switch-latency fix) and renumbering.307→.309, the coordinator asked for a "light catch-up," not a full re-run. This is what ran, all green:py_compileon every file the merge auto-resolved or touched (functions_orchestration_composition.py,functions_orchestration_continuation.py,config.py) — clean.test_orchestration_workflow_run_wait_off_golden.py→ 9/9 passed (the single most load-bearing guard: setting off ⇒ unchanged behavior).npm --prefix application/v2_ui run typecheck→ clean;run build→ clean (free memory checked first: 4.99–5.30 GB, all above the 3 GB floor).python scripts/build_docs_inventory.py→ no diff (the inventory was already in sync post-merge; no hand-merge ofapp_surface.ymlwas needed).test_docs_app_surface_coverage.py(__main__) → 7/7 passed.test_docs_site_quality.py(__main__) → 6/6 passed.test_docs_link_integrity.py(__main__) → 2/5, andtest_docs_release_notes_integrity.py(__main__) → 0/1 — both confirmed unchanged from the pre-merge known-base-failure baseline above.08def99f7(see the Guardrails section) — 6/6 PASS, 0 blockers, identical scope.Not re-run, by design: the full 170-test Phase 6c suite, the 111-file neighbour sweep, and the Playwright suite. The merge touched no Phase 6c runtime file directly (
config.pyandfunctions_orchestration_composition.pyauto-merged on disjoint hunks;release_notes.mdwas the only real conflict, resolved by keeping every section from both sides), so the pre-merge full results above still describe the code that shipped.Documentation
### **(v0.261.309)**section at the top ofdocs/explanation/release_notes.md(renumbered in place from.307once the coordinator reassigned the version — content unchanged). Merging V2's tip (08def99f7) brought in V2's own### **(v0.261.306)**and### **(v0.261.305)**sections directly beneath it; both are preserved byte-identical, as is every older section.docs/explanation/features/CHAT_ORCHESTRATION_WORKFLOW_RUN_WAIT.md.docs/admin/orchestration.md:docs/_data/features.ymlclaimsenable_chat_orchestration_workflow_run_wait, anddocs/_data/app_surface.ymlwas regenerated withscripts/build_docs_inventory.py.docs/guides/trigger-a-workflow.mdhas a new subsection, "Use a quick workflow's result in the same answer".docs/reference/chat-controls.mdis unchanged because no V2 controls changed.CHAT_ORCHESTRATION_WORKFLOWS_ROADMAP.mdis untouched.Security checklist
@swagger_route(security=get_auth_security())route_frontend_admin_settings.pyis the one route file touched, only to save/coerce the two new keys inside its existing handler; its route signatures and@swagger_route(security=get_auth_security())decorators are unchanged, confirmed by the swagger-routes guardrail.sanitize_settings_for_user()static/v2bundle.Authorization and untrusted content
Re-authorization on every continuation (
_reauthorize_wait). Each check confirms:refresh_workflow_planning_privacy)Any failure releases the hold and ends the wait. The step then says "stopped waiting", and 6b's own rules decide whether the result is posted.
Content. Workflow output reaches the plan only through
functions_workflow_result_reader.py, through the existingworkflow_resultscompose path. Each read re-authorizes and checks the sha256 of what was consumed, and the text stays fenced as untrusted. The step output itself holds only ids, status, timestamps and a result pointer, never content.XSS. Workflow names render as React text nodes only. The server summary is built with
clean_catalog_text(), and hostile names are covered byui_tests/test_v2_workflow_run_wait.py.Design decisions (D1–D7)
D1. Quick-run rule and cap.
quick_run_eligibility(workflow, settings), which reads nothing else and refuses anything it can't read. A workflow is quick only if all of these hold:group_id. A group workflow isn't the user's alone to wait for.durable_executionis true. Only a durable run records its progress where a later continuation can read it.M365_ACTIVE_STATES). Those runs wait for a person or for sign-in.one_time, top-level or inorigin). A hand-off is large work sent out of the chat on purpose, and its result is always posted back.workflow_resultsreader.approval.required. Approval waits for a person.m365_run_as_user_id. That can pause for the user's sign-in.QUICK_RUN_MAX_TASKS = 5). A small task count keeps a run inside a few minutes, given the per-task timeouts.compute_workflow_run_waits:workflow_runstepweb_search,url_fetch,deep_research,agent_invoke,action_invoke,workflow_handofforworkflow_proposecomposesteps may read the run's resultcomposebinds it through a required inputrequire_member_of_workflow_usermust be offchat_orchestration_workflow_run_wait_max_seconds, default 300 s, range 60–1800 s.WAIT_MIN_SECONDS. Anything shorter is dominated by scheduler latency; see D4.deadline = min(now + cap, plan_deadline − (90 s + step_timeout × steps that use the result, transitively)). There's no wait at all when less than 60 s would be left.D2. Where the "consumed in plan" mark lives.
chat_deliveryrecord, which is the record the 6b worker claims, in a newchat_delivery.plan_waitfield. That field holds{version, state: holding|consumed|released, orchestration_run_id, step_id, until, held_at, consumed_at|released_at, updated_at}._etag(MatchConditions.IfNotModified), retried on 412.deadline + 120 s.untilwhile a hold is live. It never posts a consumed record, because consuming writesstatus: delivered,notice_kind: none,outcome_reason: used_in_answerandmessage_id: null.delivery.reason === 'used_in_answer'from the existing status projection, which gained one key, and shows "Its results were used in a chat answer."D3. What later steps and the final answer see.
composeinputwait.outcome = consumed: run id, status, timestamps, result pointerX. Its results were used in this answer."workflow_run_failed/workflow_run_cancelledX. It was still running when this answer was written."X. Its results are posted to this chat separately."X. The plan stopped waiting for it."D4. Scheduler polling and lease.
posted. Exactly one delivery still happens; only the route changes.D5. Planner prompt.
waitableentries, and one hint line explains that a waitable run's result may feedcompose. Both are computed server-side fromquick_run_eligibilityand the plan rules.workflow_run_waits, and the schema recomputes the marker from the server-only planning context. A stored plan's marker is revalidated withstored_workflow_run_waits.D6. V2 changes. These are the minimum needed.
Waiting for "Sales digest" to finish (up to 5 min)., instead of the generic "Waiting for results". The line comes from the server's step summary and is shown as text.used_in_answerdelivery reason, because there's no posted message to jump to.The timeout text needs no new UI, since the step summary and answer note already carry it. No controls, tracker or bell changes.
D7. The admin turns the setting off mid-wait.
_reauthorize_waitreturnswait_disabled. It releases the hold, and the step ends "stopped waiting" without promising a post. 6b then posts when the run finishes, if 6b is still on.result_input_changed), the hold lapses on its own atdeadline + 120 s, and 6b posts once.Decisions made autonomously
waitrecord. When a marked step's start is refused, it reports the refusal exactly as today.workflow_run_failed/workflow_run_cancelled, so dependent steps follow the existing dependency-failure rules and 6b doesn't post a second failure notice.composebehavior:composedis true only whencomposeactually prepared the run's content.composeignores a run value that carries nowaitrecord.composepolicy text is unchanged.require_member_of_workflow_usermust be off for any wait. Under it, a headless continuation can't prove membership.817f9dd07) with ids redacted, then compared byte-for-byte with the setting off.orchestrationPlan.tsmatches the literal capability id'workflow_run'.used_in_answeragainstdelivered.clean_catalog_text(), and React renders them as text.depends_onlists only the in-tab prerequisites. Use Workflow Results In Chat is on another tab, so the runtime enforces it throughwait_configured()and the help text names it..307, reassigned once V2's own tip reached.307(PR Render Microsoft 365 pending-action cards in V2 chat (#1722 gap 2) #1731) and the sibling Phase 8 replay branch claimed.308.State mapping
chat_deliveryrecordWaiting for "X" to finish (up to N min).; dependents show "Waiting for required results."status: pending,plan_wait.state: holding,until = deadline + 120 sX. Its results were used in this answer."status: delivered,outcome_reason: used_in_answer,notice_kind: none,message_id: null,plan_wait.state: consumedworkflow_run_failed/workflow_run_cancelled);composeblocked by its required inputstatus: delivered,used_in_answer,plan_wait.state: consumedX. It was still running when this answer was written."plan_wait.state: released; 6b proceedsX. Its results are posted to this chat separately."X. The plan stopped waiting for it."plan_wait.state: released; 6b's own rulesX. The plan stopped waiting for it."plan_wait.state: releasedMutation kill table
Each mutation was applied by hand, the listed tests were run and confirmed to fail, the mutation was reverted, and
git diff --quiet -- applicationconfirmed a clean revert (pre-version-bump commits checkedgit diff --quiet -- application ':!application/single_app/config.py'instead)...._X.pyabbreviatestest_orchestration_workflow_run_wait_X.py. Full log is a session artifact (mutations_kill.log,mutations_check.log).wait_configuredignores the switch)functions_orchestration_workflow_run_wait.py..._off_golden.py,..._planner.pyfunctions_orchestration_schema.py..._off_golden.py,..._planner.pyfunctions_orchestration_workflow_run_wait.py..._eligibility.py,..._planner.pyfunctions_orchestration_workflow_runs.py..._execution.pyfunctions_orchestration_workflow_run_wait.py..._delivery.pyfunctions_orchestration_workflow_runs.py..._delivery.pyfunctions_workflow_chat_delivery.py..._delivery.py,..._execution.pyfunctions_orchestration_workflow_runs.py..._execution.pyfunctions_orchestration_workflow_run_wait.py..._eligibility.py,..._planner.py,..._execution.pyfunctions_orchestration_continuation.py..._claims.py,test_orchestration_continuation.pyfunctions_orchestration_continuation.py..._claims.pyfunctions_orchestration_workflow_run_wait.py..._plan_rules.pyfunctions_orchestration_workflow_run_wait.py..._plan_rules.pyused_in_answerfrom the client delivery reasonsworkflowRunStatus.tstest_v2_workflow_run_status.mjs,test_v2_workflow_run_tracker.mjsused_in_answerworkflowRunStatus.tstest_v2_workflow_run_status.mjsdescribeRunProgressorchestrationPlan.tstest_v2_orchestration_run_progress_status.mjs16/16 KILLED.
Known base failures (not caused by this PR)
All confirmed identical against a temporary detached worktree of base
817f9dd07(removed after each comparison), so none of these are caused by this PR. That triage predates the push-time merge of V2's tip to08def99f7; per the coordinator's "light catch-up, not a full re-run" instruction, the 111-file sweep and the V2 Playwright pre-existing failures below were not independently re-triaged against08def99f7. The two docs-specific known failures were re-run post-merge (see "Post-merge catch-up" under Testing/validation) and are confirmed unchanged at the new head.From the full 111-file neighbour sweep (10 files, 11 failing tests, 1 timeout):
test_admin_settings_group_shared_regions.py::test_shared_controls_are_not_duplicatedtest_admin_settings_nav_map.py::test_every_nav_destination_existstest_admin_settings_safe_int_fallback_fix.py::test_version_alignment_for_safe_int_fixtest_admin_settings_sidebar_card_parity.py::test_every_top_level_card_has_sidebar_destinationtest_admin_settings_tab_preservation.py::test_admin_settings_html_structuretest_admin_settings_template_composition.py::test_no_functional_test_reads_the_template_uncomposedtest_orchestration_planner_failure_diagnostics.py::test_three_pinned_documents_allow_analyze_but_explicit_search_stays_requiredtest_settings_secrets_exposure_hardening.py::test_admin_secret_redaction_and_preservationtest_v2_admin_settings_rail_collapse.py::test_the_rail_collapses_to_labelled_iconstest_v2_admin_settings_schema.py::test_defaults_match_their_field_typeand::test_gated_fields_inherit_their_gate_s_own_conditionstest_orchestration_workflow_handoff_imports.py— TIMEOUT at 300s (slow, not failing; an import-cycle probe unrelated to this PR)None of these touch workflow runs, workflow results, 6b delivery, native waits, or V2 workflow-run/plan-card surfaces — they're admin-settings-shell, nav-map and secrets-hardening checks unrelated to Phase 6c.
Docs (named as known failures in the task brief):
test_docs_link_integrity.py(__main__) — 2/5 passed. The 3 failures: a dead link toV2_MEDIA_GALLERIES_AND_VIEWER/DOCUMENTS_DRAWER_GENERATED_FILES_FIX, and 2 unresolvedfeatures.ymllinks (CONTENT_SCREENING_FRAMEWORK,CHAT_CONTENT_CHECKS).test_docs_release_notes_integrity.py(__main__) — 0/1 passed.V2 Playwright, discovered during this PR's own validation (pre-existing files, unmodified by this branch):
test_v2_workflow_run_card.py::test_the_card_follows_each_live_state_the_status_route_reports— expects the M365 reconnect link at/profile?tab=settings#m365-connection-status; the app currently serves/settings?tab=preferences§ion=m365-workflow-connection.test_v2_workflow_run_tracker_spa.py::test_the_tracker_stays_off_after_the_app_fails_to_start— "The app never refreshed its bootstrap."; unrelated to this branch's 3 touched V2 files (WorkflowRunCard.tsx,orchestrationPlan.ts,workflowRunStatus.ts).test_v2_orchestration_plan_card.py::test_timed_plan_shows_a_countdown— "a timed plan must render a role=timer element"; the countdown timer is a pre-run display path this branch doesn't touch.Guardrails
Run twice, both clean. The second run is the one that counts, since it reflects the final head after the push-time V2 merge.
run_v2_guardrails.py <worktree> 817f9dd07 HEAD 6c, base = the V2 commit this branch was based on at the time (itself picked up by an earlier mid-session V2 catch-up merge). 6/6 PASS, 0 blockers, 542 app.pyfiles compiling.08def99f7, the V2 tip merged in at push time — PR Render Microsoft 365 pending-action cards in V2 chat (#1722 gap 2) #1731): same 45 files relative to that base (20 app.py, 24 XSS-surface, 1 route.py, 3 V2.ts/.tsx), unchanged scope and verdict;python-syntaxnow compiles 543 app.pyfiles (+1, from V2's own tip advancing).08def99f7).ts/.tsxfiles)6/6 PASS, 0 blockers (both runs).
CodeQL
CodeQL has since completed a real PR diff — the earlier fork-PR limitation noted below resolved once GitHub finished the configuration the coordinator had already flagged as "still in progress."
gh api "repos/microsoft/simplechat/commits/2e174aa9704396d51170766f6bf4be7d91171b3c/check-runs?check_name=CodeQL"now returns conclusionsuccess, with:All three
Analyzejobs (actions, javascript-typescript, python) and the aggregateCodeQLcheck pass. This result supersedes the earlierneutral/"configurations not found" read in this section's history.The 6 new notes (
gh api repos/microsoft/simplechat/check-runs/113615996428/annotations), every onenotice/note severity (the lowest CodeQL severity, none security-severity-rated):py/cyclic-importfunctions_orchestration_schema.pyfunctions_orchestration_workflow_run_wait.pynever importsfunctions_orchestration_schemaat module level; CodeQL's whole-graph check still flags the edge regardless of which side is lazy.py/cyclic-importfunctions_orchestration_workflow_run_wait.pyfunctions_orchestration_registryimport is already function-local (inside atry:block, not at module top-level) for exactly this reason.py/cyclic-importfunctions_workflow_limits.pyfunctions_settings-import pattern already used by all 8 getters in this file, each with the same comment: "Settings initialize application clients; load them only at a request boundary." Same established technique, new call site, not a new pattern.py/import-and-import-fromfunctional_tests/test_orchestration_workflow_run_wait_execution.pyfrom ... import (PLAN_WAIT_CONSUMED, ...)for direct constant references, andimport ... as deliverybecausemonkeypatch.setattr(delivery, 'utc_now', ...)(line 262) must patch the module object itself, not a name copied out of it.py/import-and-import-fromui_tests/test_v2_workflow_run_wait.pyimport test_v2_orchestration_plan_editor as editor_testsforeditor_tests.make_plan(...)/editor_tests.RUNcall sites, andfrom ... import (connect_options, editor_assets, editor_browser)because pytest fixtures must be bound by their own name in the test module's namespace to be discovered —editor_assets/editor_browserare used as fixture parameters at line 161.py/unused-importui_tests/test_v2_workflow_run_wait.pyrun_assetsis a pytest fixture (fromtest_v2_workflow_run_card.py) used as a fixture parameter at line 177 (def card_ui(editor_browser, run_assets):); pytest resolves fixtures by name against the module's imports, which a plain usage scan can't see — hence the pre-existing# noqa: E402, F401. The same rule already appears 3 times in the base-branch baseline below for this identical pytest convention.None of the 6 are fixed: each fix would either reintroduce a real import cycle, break
monkeypatch.setattron the actual module object, or break pytest's fixture discovery. All 6 match an established, already-documented pattern used elsewhere in this codebase — see the baseline immediately below, which carries the same 3 rules for the same reasons.Base-branch baseline (
paullizer-react-v2-ui, queried viacode-scanning/alerts?ref=refs/heads/paullizer-react-v2-ui&state=open, 10 open alerts, all severitynote) — unchanged, none in a file this PR touches:py/cyclic-importfunctions_orchestration_recovery.pypy/cyclic-importfunctions_orchestration_rendering.pypy/cyclic-importfunctions_orchestration_checkpoints.pypy/unused-importtest_conversation_delete_hns_directory_cleanup.pypy/unused-importtest_orchestration_terminal_wait_retry.pypy/unused-importtest_orchestration_settings_runtime_state_binding.pypy/import-and-import-fromtest_orchestration_file_sign_in_publication.pyNo alerts are dismissed here — that stays a maintainer action.
Follow-ups
recovery_changed,result_input_changed) instead of waiting for it to lapse atdeadline + 120 s._reader_outcome, used by the wait.SAVED_WORKFLOW_RUN_WAIT_KIND.test_v2_workflow_run_card.py's staleM365_HREF, the tracker's focus-refresh throttle intest_v2_workflow_run_tracker_spa.py, and the missing M365 endpoints in thetest_v2_notifications_bell.pyharness.