Repository navigation
test: fix V2 test-harness drift (chat image refs, auto-open fixture, harness seeds) - #1598
Merged
Paul Lizer (paullizer) merged 15 commits intoOct 1, 2026
Conversation
The closed exec namespace in test_v2_prompt_attachment_persistence.py never received prepared_chat_image_references (added to chat_api by microsoft#1585), so every chat-path boundary and the two composer-card tests that reuse the harness died with a NameError. - Seed the route's initial value next to the other chat_api locals in a shared _chat_namespace, plus the content-check names and the orchestration/shared-route names the boundaries now read (real pure functions with chat checks off; no production statements copied). - Translate boundary NameErrors into a readable AssertionError naming the missing local. - Add an AST drift check that compares the seed to chat_api's dict literal and fails with the added/dropped keys. - Move the composer-card TypeScript dispatchPlan mock to planner contract 2 with a runnable step, which normalizePlan/isPlanRunnable now require. Refs microsoft#1543 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The orchestration controller now adopts a streamed plan only when it carries plan, run and turn ids and names the conversation the turn was started for. The auto-open fixture streamed a plan with neither, so the controller dropped it and four of the five checks failed even as a script. - _plan() now names the conversation the plan was started for, and the mocked plan stream echoes the turn id the client sent, as the server does. - Every page check first asserts the plan was adopted (one plan request for the started conversation, one held plan with the requested turn id), so a dropped plan can no longer pass a "drawer stays shut" check. - The file is converted to plain asserts with a module-scoped harness page fixture, keeping the __main__ runner and its exit code (microsoft#1572 guard). Refs microsoft#1543 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
73ff7d6 taught repair_plugin to resolve the action type with resolve_action_type and to pick the plugin class with the module helper _find_plugin_class. The isolated exec namespace provided neither, so the route's own except block swallowed a NameError, returned 500 without writing, and all 18 repair cases failed with "assert 0 == 1". - Import the dependency-free functions_action_manifest.resolve_action_type as shipped and AST-extract the real _find_plugin_class, the same way the route itself is loaded. - Capture the route's log_event so a missing name now fails with the logged error instead of an empty call count. Refs microsoft#1543 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
26e8cae added chart editing and gave each block kind its own system prompt, renaming ASSIST_SYSTEM_PROMPT to DIAGRAM_ASSIST_SYSTEM_PROMPT. The positioning check still read the old constant and failed with an AttributeError, although the shipped diagram prompt still carries every phrase it asserts. Read the system message build_assist_messages sends for a diagram edit instead, so the check follows the prompt that is actually sent rather than a constant name. Refs microsoft#1543 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
test_run_record_keeps_the_turn_and_question_ids execs the route's real _validate_turn_memory_context and _persist_planned_turn in a hand-built namespace. Since the image-reference work (microsoft#1585), _persist_planned_turn calls image_reference_provenance_from_seeds() for the saved user message, and the namespace never received it, so the test died with a NameError (script 5/6; pytest 1 failed under the microsoft#1572 guard). Inject the real helper from functions_orchestration_context through the existing import_app_module stub seam. It is a pure function, and a turn with no seeds yields [], so the asserted identifiers are unchanged. Refs microsoft#1543 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Planning now confirms every picked document against the strict current-authority manifest (0401fdb). The shared conversation-route harness stubs candidate discovery to ([], []), so both picked documents in test_document_removal_is_not_lost_when_editor_opens were reported as unavailable and planning refused with "A selected document could not be opened". Scope a helper to that one test: restore the production explicit-selection branch of resolve_candidate_documents (no service call), and answer the strict manifest seam from the test's existing authority stub, which now carries the source_kind real manifests do. Refs microsoft#1543 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
778c10f moved the workflow runner's inline hybrid_citations update into _attach_workflow_search_context, so the source contract now checks the helper's body (scoped with ast) and that the runner routes Search results through it. The picker test also left stub functions_search/functions_document_analysis modules, and a functions_document_actions bound to them, in sys.modules for the rest of the process; a later `from functions_search import build_tags_filter` failed with ImportError. The stubs are now installed by a context manager that restores those three entries afterwards, and a new test fails if any module installed over the stubs outlives its test. Refs microsoft#1543 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
group_workflow_allowed_group_ids declares a two-link depends_on chain (allow_group_workflows, then require_group_assignment_for_group_workflows), so test_sub_settings_are_gated_by_their_capability crashed with "'list' object has no attribute 'get'". Its docstring already recorded a chain-aware fix, but that fix never landed in the code. The check now reads every link through the application's own iter_field_dependencies. The expected gate must be one of the links, with equals True, and any other link must be one the gate itself requires. For a single condition that is the original check. The ungated-limit message path had the same single-dict assumption and is fixed too. EXPECTED_DEPENDENCIES is unchanged; Phase 6a (microsoft#1592) edits it. Refs microsoft#1543 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…ness Conversion now publishes Microsoft 365 history and the route catches M365ApprovalRequired/M365PolicyError, so the shared ConversionHarness failed with "History sharing authorization is not configured" and NameError before any conversion assertion ran. - Build a real M365HistoryService from production's nested history callbacks (AST-loaded from configure_m365_history_runtime) over the in-memory stores, scoped to each conversion and restored in finally. - Seed the route's M365 exceptions, strip_pending_action_references, a delivery-cancel recorder and the m365_execution_runs container; loud stubs guard the unreachable M365 publication paths. - Assert the exact delivery-cancel order and add drift tests: every global read by the loaded production code is bound, and the history service does not leak out of a conversion. - ui_tests/test_v2_group_participant_invites.py binds the globals its own served helpers read (real strip_private_chat_checks, loud image stubs) and gets a matching drift test. Refs microsoft#1543 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
test_revoked_record_refresh_clears_the_previous_page looked for the access alert inside the "Complete record inspection" region. Since ad8f421 a 403 on a paged workflow read calls onAccessLost, and WorkflowRunHistory replaces the whole expanded run with a run-level alert that removes every cached run detail, so that region unmounts and the panel-scoped alert can never appear. Assert the deliberate run-level contract instead, matching test_v2_workflow_publication_completion.py: the 403-specific alert is shown, the queued failure was consumed, and the inspection region, the records list and the previously loaded record text are all gone. Refs microsoft#1543 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
CodeQL (py/uninitialized-local-variable) flagged the module fixture's `yield page` on microsoft#1598. It does not model pytest.skip() as non-returning, so it saw a path from the HarnessUnavailable handler to the yield with `page` unbound. Moving the yield into the try statement's else clause makes the control flow explicit with no change in behaviour: the page is yielded only when the harness opened, and a missing v2_ui toolchain still skips the four browser checks. Refs microsoft#1543 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Brings in Phase 6a (workflow results in chat) so the harness fixes on this branch are rerun against the route, collaboration, orchestration and V2 UI changes it ships. Refs microsoft#1543 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
microsoft#1592 (6a) moved the summary copy in both legacy conversions into _carry_summary_into_collaboration, and made _copy_legacy_personal_messages_to_collaboration withhold workflow-result messages through functions_workflow_result_masking. The ConversionHarness closed namespace bound none of these names, so every conversion raised NameError (script: failures=8, errors=58; pytest: 66 failed). Load _carry_summary_into_collaboration from production with the other collaboration functions, and bind the real masking helpers, whose module has no application imports. No production statements are copied. The file is back to its pre-merge result: only the 4 known non_group_invitee subtests fail, in all four modes. The existing drift test names each unbound global, and removing any of the three bindings fails it. Refs microsoft#1543 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Brings in N9 (the AI workflow assistant gate on personal workflow instruction drafting) so the harness fixes on this branch are rerun against the updated workflows route and workspace template. Refs microsoft#1543 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Brings in the log-injection CodeQL alerts fix (sanitized log arguments in utils_cache and the workflow cancellation routes, and a field-count log line in the admin plugin settings route) so the harness fixes on this branch are rerun against it. Refs microsoft#1543 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Paul Lizer (paullizer)
added a commit
to paullizer/simplechat
that referenced
this pull request
Oct 1, 2026
Docs-only status update to the chat orchestration workflows roadmap at paullizer-react-v2-ui 0.261.216: - 6a done in microsoft#1592 (v0.261.214), with where it differs from the plan - workflow_results capability and 6b (6b-1 server, 6b-2 V2) in progress; Retry uses the durable runtime/resume, not /resume-failed; run deep link reuses P's query form through one scope-aware helper - N9 follow-up microsoft#1596 (v0.261.215) and microsoft#1597 log injection fix (v0.261.216) - new follow-ups: microsoft#1598 in review, non_group_invitee product bug, stale base assertions, and the verified cold-import cycle - section 10: branch from paullizer-react-v2-ui, not only base on it Refs microsoft#1543 microsoft#1546 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
14 of 24 tasks
Paul Lizer (paullizer)
marked this pull request as ready for review
October 1, 2026 20:22
Paul Lizer (paullizer)
merged commit Oct 1, 2026
b5cc18e
into
microsoft:paullizer-react-v2-ui
7 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
Test-only. On a clean V2 (
paullizer-react-v2-uiat70c255508, VERSION0.261.213), several functional and UI tests fail because the application moved on and the tests didn't. All 12 changed files are underfunctional_tests/andui_tests/. There are no application, template, static or docs changes, noVERSIONbump and no release notes, as in Fail pytest tests that return False (#1572) #1579. There is no end-user or admin impact.Kept current with V2. The branch has since merged V2 three times, with rerere off and no conflicts:
520132dc2(Phase 6a: workflow results reader and Follow up in chat #1592, Phase 6a) asbe05eb54f86f3f3629(Gate personal workflow instruction drafting on the AI workflow assistant setting (N9) #1596, N9) as60fefae19b61729627(Clear the 19 py/log-injection CodeQL alerts on V2 #1597, log-injection CodeQL fix) ase8e3561a0Every file was rerun after each merge; see Reruns on later V2 tips. 6a needed one test-side binding fix in the group conversion harness (
bf2940574). N9 and Clear the 19 py/log-injection CodeQL alerts on V2 #1597 needed none.Must 1:
prepared_chat_image_references.chat_api. The persistence harness's closed exec namespace now seeds it with the route's initial value, next to the other route locals, in one shared_chat_namespace.NameErrorin the 72 failing persistence cases and in the 2 composer-card tests that reuse that boundary.astcheck compares the seed's keys with the route's dict literal, so the next drift fails with a readable message.Must 2: the auto-open plan fixture.
_plan()now names the conversation the plan was started for, and the mocked plan stream echoes the client'sturn_id. The controller'sonPlanrequires both.__main__exit code.Should-fix. Eight more files are fixed at the root cause, all test-only:
sys.modulesstub is restored after its test.Items that need a product decision are under Not fixed.
Linked issue
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-only. Not applicable: this PR is test-only. No application code changed, soVERSIONis V2's own:0.261.216after theb61729627merge. In each edited test file only theVersion:header line changed, to the base's version at the time of the edit:0.261.213for the original fixes, and0.261.214for the group collaboration file's 6a binding fix. No exact-version assert was added.deployers/version.txtbumped, or not needed becausedeployers/was not changed.deployers/is not changed.Testing / validation
Setup
PYTHONPATH=application/single_app;functional_testsandPYTHONIOENCODING=utf-8. Only one test process ran at a time.PYTEST_ADDOPTS=-p no:langsmith_plugin. The venv's langsmith pytest plugin importsrichat startup, and itsplatform.system()WMI query can hang for minutes on this shared host. The variable was set only in my local runner and is not committed.70c255508, which I removed afterwards.npm ci, thennpx vite build, inapplication/v2_ui. The build output is gitignored. I rebuilt it after the 6a merge. Neither N9 nor Clear the 19 py/log-injection CodeQL alerts on V2 #1597 touchesapplication/v2_ui, so the same build served both later reruns. Every browser count below is passed tests, with 0 skipped.Commands
python -u <file>python -u -m pytest <file> -p no:cacheprovider -q -rfE -o faulthandler_timeout=150-O: the same two commands withpython -O -uBefore and after: files this PR touches
These counts were measured before the V2 merges: the base is
70c255508, and "This PR" isddb2d3362. The reruns after each merge are in the next section.70c255508ddb2d3362)functional_tests/test_v2_prompt_attachment_persistence.py(Must 1)pytest.main)-Oscript,-Opytestfunctional_tests/test_v2_prompt_composer_card.py(Must 1; not edited, it reuses the persistence boundary and the editedtest_v2_prompt_composer_card_logic.ts)NameError), 9 passed-Oscript,-Opytestui_tests/test_v2_orchestration_auto_open.py(Must 2)-Opytestfunctional_tests/test_app_settings_auxiliary_writers.pypytest.main)-Oscript,-Opytestfunctional_tests/test_block_revision_assist.py-Opytestfunctional_tests/test_orchestration_turn_recovery_identifiers.py-Opytestfunctional_tests/test_orchestration_plan_revision_routes.py(unittest)-Opytestfunctional_tests/test_workflow_document_picker_recent_targets.py-Opytestfunctional_tests/test_v2_admin_workflow_parity.py-Opytestfunctional_tests/test_group_collaboration_source_storage_fix.py(unittest)-Oscript,-Opytestui_tests/test_v2_group_participant_invites.py(base:NameError: M365ApprovalRequired)pytest.main)-Opytestui_tests/test_v2_workflow_loops.py(test_revoked_record_refresh_clears_the_previous_page; the file has no__main__)-Opytest, whole fileSome counts grow because tests were added:
Reruns on later V2 tips
Each merge used
git -c rerere.enabled=false merge origin/paullizer-react-v2-ui, and the #1597 merge also passed--no-ff, as the coordinator asked. None had a conflict or a rerere resolution.be05eb54fmerges520132dc2(#1592, Phase 6a; VERSION0.261.214)admin_settings_fields.py,functions_collaboration.py,functions_orchestration_context.py,functions_orchestration_memory.py,functions_orchestration_workflow_context.py,route_backend_chats.py,Composer.tsxandWorkflowRunHistory.tsx.test_v2_admin_workflow_parity.py'sEXPECTED_DEPENDENCIESauto-merged. This PR's edits to that file are elsewhere.bf2940574binds them; see Changes by file and the mutation table. Every other file matched its pre-merge result.60fefae19merges86f3f3629(#1596, N9; VERSION0.261.215)route_backend_workflows.py(draft_workflow_instructionsnow callsis_workflow_assistant_enabled_for_user),route_frontend_workspace.pyandtemplates/workspace.html. It addedfunctional_tests/test_workflow_draft_instructions_assistant_gate.py.draft_workflow_instructionsor loadsroute_backend_workflows.py. The doc picker readsworkspace.html, but only its picker markup andwindow.userGroupslines, which N9 didn't change.e8e3561a0mergesb61729627(#1597, log-injection CodeQL alerts fix; VERSION0.261.216)route_backend_plugins.py,utils_cache.py,route_backend_workflows.pyandfunctions_appinsights.py. The admin plugin settings route (update_core_plugin_settings) no longer logs the request body. It now logs[PLUGINS] Received plugin settings update request.throughlog_event, with afield_count. On the test side, Clear the 19 py/log-injection CodeQL alerts on V2 #1597 addedfunctional_tests/test_support/log_sanitizer.pyandtest_log_injection_codeql_alerts_fix.py, and editedtest_group_document_collaboration.pyandtest_group_workflow_fixture_parity.py. None of those files is in this PR.route_backend_plugins.py. Its plugin cases execrepair_pluginand_find_plugin_classfromplugin_validation_endpoint.py, withlog_eventcaptured, and Clear the 19 py/log-injection CodeQL alerts on V2 #1597 didn't change that file. Clear the 19 py/log-injection CodeQL alerts on V2 #1597's own test coversupdate_core_plugin_settings. No file in this PR referencesroute_backend_plugins,utils_cache,route_backend_workflows,functions_appinsightsorsanitize_log_message.The results after the 6a, N9 and #1597 merges are identical in every cell. After 6a, the group file and the Playwright files ran at
bf2940574. The other Python files ran atbe05eb54f, sincebf2940574changes only the group file. After N9, everything ran at60fefae19, and after #1597, ate8e3561a0.-Oscript-Opytest__main__)test_workflow_draft_instructions_assistant_gate.py(not edited; N9 and #1597 reruns)test_log_injection_codeql_alerts_fix.py(not edited; #1597 rerun only; its__main__runspytest.main)The group collaboration failures are the same 4
non_group_inviteesubtests as before; see Not fixed.The checks this PR leaves alone also match after all three merges:
-Opytest 1 failed and 10 passed. As an-Oscript it shows 11/11, but only because the failing check is a bareassert, which-Ostrips. pytest's rewritten assert still runs under-O.PytestReturnNotNoneWarning, with no other error.Investigated and left alone
functional_tests/test_v2_admin_settings_schema.py(not edited; see Not fixed)The earlier 11, 8 and 6 counts
The earlier counts for composer card (11), block revision (8) and turn recovery (6) reproduce on base only when pytest treats
PytestReturnNotNoneWarningas an error (-W error::pytest.PytestReturnNotNoneWarning).return Truefor its__main__runner.Truealone, and the repo'spytest.inionly declares theuimarker.test_v2_prompt_composer_card.pytest_block_revision_assist.pytest_orchestration_turn_recovery_identifiers.pyCross-test leak check (document picker)
python -u -m pytest functional_tests/test_workflow_document_picker_recent_targets.py functional_tests/test_tag_filter_sanitization.py, both files in one process:test_build_tags_filterfails withImportError: cannot import name 'build_tags_filter' from 'functions_search' (unknown location), because the picker left its stub insys.modules.TypeErrorbecause it needs Cosmos credentials. Nothing leaks any more.Mutation checks
Each mutation was applied in the working tree, run, then reverted, and the file hash was checked afterwards. None is committed. Each one fails with a readable message; none is skipped.
prepared_chat_image_referencesseed-Opytest run oftest_chat_harness_seeds_the_route_initial_image_referencesfails too: "The chat harness namespace must seed prepared_chat_image_references: chat_api's new-message branch reads it before the user message is written."input_fidelitykey tofidelitytest_chat_harness_seeds_the_route_initial_image_referencesfails: "chat_api's prepared_chat_image_references keys drifted from ROUTE_IMAGE_REFERENCES_INITIAL: the route added ['input_fidelity'] and dropped ['fidelity']. Update the seed to the route's literal."turn_id-Opytest 4 failed, 1 passed. Message: "the controller must adopt the streamed plan, but it holds 0 plan(s) and reported 'The planner returned an invalid plan. Please try again.'" Rerun atddb2d3362, after the CI fix: script 1/5 and pytest 4 failed, 1 passed, with the same message.resolve_action_typebindingblock_kind='chart') instead of a diagramimage_reference_provenance_from_seedssource_kindfrom the manifest stub; drop the strict-manifest patchassertFalsewith the route's refusal: "A selected document could not be opened…" for the first, "The selected document metadata could not be loaded…" for the other twosys.modulesrestore_attach_workflow_search_context(a temporary product edit, reverted)list(search_context.get('citations') or [])require_group_assignment_for_group_workflowslinkbf2940574; script)Group collaboration mutations:
M365PolicyError)finallyrestoreTypeError)Group collaboration 6a binding mutations, run after the 6a merge. With the fix in place, the file gives failures=4. Each drift failure ends "Seed these production globals in ConversionHarness, or the code paths that read them fail with NameError."
_carry_summary_into_collaborationfrom the functions loaded withastensure_personal_collaboration_for_legacy_conversationandensure_group_collaboration_for_legacy_conversation.message_uses_workflow_resultbinding_carry_summary_into_collaborationand_copy_legacy_personal_messages_to_collaboration.withhold_workflow_result_messagebinding_copy_legacy_personal_messages_to_collaboration. No fixture message uses a workflow result, so only the static drift check catches this one.UI invites mutations:
-OWorkflow loops mutations:
attempts/9Guardrails
After the merges, run at
e8e3561a0:<venv python> run_v2_guardrails.py <worktree> b6172962777fd7a9678b00818c43a33752c8fe87 HEAD harness. The base is the PR's V2 merge-base,b61729627. Measured from70c255508, the diff would also include the application changes from 6a, N9 and #1597. Exit code 0. Scope line: "12 changed files; app .py 0, xss surface 0, route .py 0, v2 ts/tsx 0". All 6 checks pass:functions_workflow_result_followup.py,functions_workflow_result_masking.pyandfunctions_workflow_result_reader.py. N9 and Clear the 19 py/log-injection CodeQL alerts on V2 #1597 added none.60fefae19, with base86f3f3629, gave the same results.Before the merges, with base
70c255508675bb0de7b7940c0ec90d5980f8b529, the results were the same. That ran atddb2d3362, and earlier at82cb3452a, where the auto-open warnings were one line higher. The table shows that run..ts/.tsxonly. The one changed.tsfile is the testfunctional_tests/test_v2_prompt_composer_card_logic.ts.The 8 advisory warnings, all on changed test lines:
ui_tests/test_v2_orchestration_auto_open.pyL273, L276 and L279. These are the docstring, the_drive_plan(..., visible_conversation=OTHER_CONVERSATION)call and the assertion message oftest_manual_plan_for_another_conversation_stays_shut, and they name a conversation._runscript runner'stry/except/tracebackfailure reporting.ui_tests/test_v2_workflow_loops.pyL909 and L916: the "Complete saved records" list locator and the access-loss alert filter.CI
82cb3452a: the CodeQL results check failed with "1 new alert including 1 error". The alert waspy/uninitialized-local-variableonui_tests/test_v2_orchestration_auto_open.pyL225: "Local variable 'page' may be used before it is initialized."pytest.skip()as non-returning, so it saw a path from the fixture'sHarnessUnavailablehandler toyield page.ddb2d3362: theyield pagemoved into thetrystatement'selseclause, with no change in behavior.-Opytest 5 passed (its 1 warning is pytest's own-OPytestConfigWarning, also seen before the fix). pyflakes is clean.harness_pageraiseHarnessUnavailable, and the run gave 1 passed and 4 skipped, each with the reason "skipped the browser-driven checks: probe: v2_ui toolchain missing".60fefae19, after the 6a and N9 merges: all 7 checks pass:e8e3561a0, after the Clear the 19 py/log-injection CodeQL alerts on V2 #1597 merge: the same 7 checks pass. The CodeQL results check reports "No new alerts in code changed by this pull request".application/**, so they don't run on this test-only diff. The guardrails above run their checks locally.Scope
git diff --stat b61729627..HEAD, against the PR's V2 merge-base: 12 files changed, 646 insertions, 153 deletions, all underfunctional_tests/orui_tests/. Before the merges,70c255508..ddb2d3362was the same 12 files, with 639 insertions and 153 deletions. The 7 extra insertions are all in the group collaboration file, frombf2940574's 6a bindings. That commit'sVersion:change only rewrites a line the PR had already changed.No
conftest.py,functional_tests/test_support/orui_tests/fixtures/file changed. The pytest reports functional tests that return False as passed #1572 guard blocks are untouched.Two test modules also serve as harnesses for another test:
test_v2_prompt_composer_card.pyreuses the persistence file's boundary.ui_tests/test_v2_group_participant_invites.pyimportsConversionHarnessfromfunctional_tests/test_group_collaboration_source_storage_fix.py.Neither is designated shared test infrastructure, so there is no fix doc.
Documentation
VERSIONbump.conftest.py,functional_tests/test_support/,ui_tests/fixtures/).Security checklist
@swagger_route(security=get_auth_security()). Not applicable: no routes were added or changed.sanitize_settings_for_user(). Not applicable: no settings code changed.Changes by file
functional_tests/test_v2_prompt_attachment_persistence.py(dc26690c5)chat_apireadprepared_chat_image_references, and the boundaries now also read content-check and orchestration names. The closed exec namespace had none of them, so every chat-path boundary raisedNameError._chat_namespaceseeds the route's initial value next to the other route locals. The other names are bound to the real pure helpers, with chat checks off. No production statements are copied, so the L13 rule holds. A boundaryNameErrornow becomes anAssertionErrorthat names the missing local. A new test compares the seed with the route's literal usingast.functional_tests/test_v2_prompt_composer_card_logic.ts(dc26690c5)NameErrorwas gone, thedispatchPlanmock still used the old planner contract, whichnormalizePlanandisPlanRunnablenow reject.ui_tests/test_v2_orchestration_auto_open.py(13cf075f3,ddb2d3362)onPlanadopts a plan only if it hasplan_id,run_idandturn_id, and only if itsconversation_idequals the current conversation._plan()set neither the conversation nor the turn._plan()namesconv, and the mocked stream echoes the client'sturn_id. Each check first asserts adoption: one plan request forconv, and one held plan with the requested turn. The file is converted to asserts with a module-scoped harness page, and the__main__exit code is kept. The fixture yields the page from thetrystatement'selseclause, so the yield is reached only when the harness opened (see CI).functional_tests/test_app_settings_auxiliary_writers.py(d5906cdf6)repair_plugincallsresolve_action_typeand_find_plugin_class. The route's ownexceptswallowed the resultingNameErrorand returned 500, so the test only sawassert 0 == 1.functions_action_manifest.resolve_action_typeand extracts the real_find_plugin_classwithast. It also captures the route'slog_event, so a missing name fails with the logged error.functional_tests/test_block_revision_assist.py(a598ef3fb)ASSIST_SYSTEM_PROMPTtoDIAGRAM_ASSIST_SYSTEM_PROMPT, so the test raisedAttributeError.build_assist_messagesactually sends for a diagram edit.functional_tests/test_orchestration_turn_recovery_identifiers.py(82e1cc751)_persist_planned_turncallsimage_reference_provenance_from_seeds, which the hand-built namespace didn't have.functions_orchestration_contextis injected through the existingimport_app_moduleseam.functional_tests/test_orchestration_plan_revision_routes.py(a55863564)([], []), so planning refused with "A selected document could not be opened".resolve_candidate_documentsis restored (it makes no service call). The strict-manifest seam is answered from the test's authority stub, which now carriessource_kind.functional_tests/test_workflow_document_picker_recent_targets.py(75c9cfe32)hybrid_citationsupdate into_attach_workflow_search_context. Separately, the test left stubfunctions_search,functions_document_analysisandfunctions_document_actionsmodules insys.modules.astto the helper's body and the runner's call to it. A context manager installs the stubs and restores those three entries afterwards. A new test fails if any module installed over the stubs outlives its test.functional_tests/test_v2_admin_workflow_parity.py(dffb8aadc)group_workflow_allowed_group_idsnow has a two-linkdepends_onchain, so the check crashed with "'list' object has no attribute 'get'".iter_field_dependencies. The expected gate must be a link withequals: True, and any other link must be one the gate itself requires. This PR doesn't touchEXPECTED_DEPENDENCIES, because 6a (Phase 6a: workflow results reader and Follow up in chat #1592) edits it. It was L56–61 on the base. 6a's newenable_chat_workflow_resultsentry auto-merged at L59, so it is now L56–62. The PR's hunks in this file are at L5 (theVersion:line) and from L251 on.functional_tests/test_group_collaboration_source_storage_fix.py(bb83f2ed1,bf2940574)M365ApprovalRequiredandM365PolicyError. The sharedConversionHarnesshad no history runtime and didn't define those exception names. It failed with "History sharing authorization is not configured" and withNameError.M365HistoryServicefrom production's nested history callbacks, loaded withastfromconfigure_m365_history_runtime, over the in-memory stores. The service is scoped to each conversion and restored infinally.strip_pending_action_references, a delivery-cancel recorder and the runs container.bf2940574):ensure_*_collaboration_for_legacy_conversationfunctions now carry the summary through_carry_summary_into_collaboration. That helper, and_copy_legacy_personal_messages_to_collaboration, readmessage_uses_workflow_result. The copy function also readswithhold_workflow_result_message. The drift test named all three as unbound._carry_summary_into_collaborationis loaded from production withast, like the other collaboration functions. The two masking functions are imported from 6a'sfunctions_workflow_result_masking, which imports nothing from the app, and bound in the harness namespace.Version:is0.261.214.ui_tests/test_v2_group_participant_invites.py(bb83f2ed1)ConversionHarness, and the helpers it serves read globals that were never bound, henceNameError: M365ApprovalRequired.strip_private_chat_checks, plus loud image stubs. It has its own drift test.bf2940574's bindings, and its 7 cases pass.ui_tests/test_v2_workflow_loops.py(82cb3452a)onAccessLost.WorkflowRunHistorythen replaces the expanded run with a run-level alert, so the "Complete record inspection" region unmounts.test_v2_workflow_publication_completion.pydoes: the 403 alert shows, the queued failure was consumed, and the panel, the records list and the old record text are gone.Decisions made autonomously
_chat_namespacethat every chat-path boundary uses. All six persistence paths are fixed, not only the two the composer card reaches.NameErroris re-raised as anAssertionErrorthat names the missing local and says where to seed it.NameError. It's in the same commit as Must 1.turn_id, as the server does, instead of a fixed literal. The adoption check is stricter than "planCount == 1": it requires one plan request for the started conversation and one held plan with the requested turn. A module-scoped fixture reuseshb.harness_page.ast:resolve_action_type_find_plugin_classimage_reference_provenance_from_seedsiter_field_dependenciesstrip_private_chat_checksConversionHarness, and its 83 errors have the same root cause.return Truestays; see the 11, 8 and 6 section above.PYTEST_ADDOPTS), because of the WMI startup hang on this shared host. Nothing about it is committed.520132dc2(Phase 6a: workflow results reader and Follow up in chat #1592, asbe05eb54f),86f3f3629(Gate personal workflow instruction drafting on the AI workflow assistant setting (N9) #1596, as60fefae19) andb61729627(Clear the 19 py/log-injection CodeQL alerts on V2 #1597, ase8e3561a0, with--no-ff). Each usedgit -c rerere.enabled=false merge, with no rebase, and was pushed as a fast-forward. The PR stays a draft._carry_summary_into_collaborationis loaded withast, like the other collaboration functions. The masking functions are the real ones fromfunctions_workflow_result_masking, which has no app imports. Binding them inConversionHarnessfixes the functional file and the UI invites file together, so the UI file needed no edit.Not fixed (with causes)
functional_tests/test_v2_admin_settings_schema.py::test_defaults_match_their_field_type(script 10/11; pytest 1 failed).application/single_app/admin_settings_fields.py, thecore-plugin-togglesfieldm365_trusted_download_hostsis declared"type": "textarea", which the test maps tostr, but its"default"is[]. The settings default infunctions_settings.pyis also[]. The failure reads "core-plugin-toggles.m365_trusted_download_hosts: default [] is not str".functional_tests/test_group_collaboration_source_storage_fix.py: the 4non_group_inviteesubtests. These aretest_rejections_do_not_read_history_or_mutate_storesandtest_v1_route_preserves_rejection_statuses_without_mutation, each for both storage types.functions_collaboration.py(line numbers as of theb61729627merge),ensure_personal_collaboration_for_legacy_conversation(L1060–1064) andensure_group_collaboration_for_legacy_conversation(L1278–1284) callprepare_m365_history_publicationfirst. It reads the source history withSELECT * FROM c WHERE c.conversation_id = @id ORDER BY c.timestamp ASC. Only afterwards doescreate_*_collaboration_conversation_recordvalidate the invitees, for example through_normalize_group_conversation_participants. That order was introduced in b8a7541, and 6a, N9 and Clear the 19 py/log-injection CodeQL alerts on V2 #1597 didn't change it.PytestReturnNotNoneWarningfailures (11, 8 and 6) in the composer-card, block-revision and turn-recovery files.return Truefor the__main__runner, not from drift.pytest.inidoesn't turn the warning into an error.Follow-ups
route_backend_chats.pyaround chat-turn persistence didn't affect the persistence or composer-card suites.e8e3561a0; every cell matches. This corrects an earlier note here: the aux-writers harness doesn't bindroute_backend_plugins.py's plugin route. It bindsrepair_pluginand_find_plugin_classfromplugin_validation_endpoint.py, which Clear the 19 py/log-injection CodeQL alerts on V2 #1597 didn't change. Clear the 19 py/log-injection CodeQL alerts on V2 #1597's own test covers the route's new logging and passes 44/44.withhold_workflow_result_messagebinding. A fixture message with a workflow result would add behavioral coverage.m365_trusted_download_hosts.prepare_m365_history_publicationreads history.resolve_candidate_documentsstub ignores explicit selections. A future test that picks documents needs the same scoped helper, or the base harness could adopt it.has_active_request.pytest.failunlessapplication/single_app/static/v2is a fresh local Vite build. A note inui_tests.instructions.mdwould help.hb.harness_pagemay clash with pytest-playwright'spagefixture.richimport can hang in a WMI query before collection;-p no:langsmith_pluginavoids it.test_tag_filter_sanitization.pyneeds Cosmos credentials, even for its pure checks.