Skip to content

Render Microsoft 365 pending-action cards in V2 chat (#1722 gap 2) - #1731

Merged
Paul Lizer (paullizer) merged 1 commit into
paullizer-react-v2-uifrom
paullizer-v2-m365-pending-actions
Oct 9, 2026
Merged

Paul Lizer (paullizer) merged 1 commit into
paullizer-react-v2-uifrom
paullizer-v2-m365-pending-actions

Conversation

@paullizer

Copy link
Copy Markdown
Contributor

Summary

  • V2 chat now renders Microsoft 365 pending-action cards (saved outgoing email or calendar actions) with Send, Send now and Cancel, matching classic chat. Until now V2 chat could not draw the card, so notices carrying m365_pending_action opened classic chat. That becomes a dead end for non-admins under the coming "New UI only" admin mode.
  • Placement follows classic chat: the card sits inline under the reply that created it (live while that reply is still streaming), and a conversation-level section lists any saved action with no matching reply, with Load more and Refresh. One shared PendingActionCard now serves both Approvals and chat; the Approvals test ids are unchanged.
  • m365_pending_action notices now open V2 /chat with a one-shot focus parameter: the page scrolls to the card, highlights it, then strips the parameter from the URL.
  • No backend, schema or settings changes; it uses the existing /api/msgraph/pending-actions endpoints. Out of scope: workflow-activity Microsoft 365 notices, the m365_sign_in_required and m365_approval_required stream-pause UIs, and data-access approvals.

Notes for reviewers

  • Stop and detach: detachActiveStream() drops the live card, and the conversation's list is re-read after a reply ends, resumes or is stopped. A card the server saves after Stop appears on the next read (window focus, Refresh, or a collaboration event). This is an accepted race and is described in the feature doc.
  • Test harnesses: opening a conversation now fires GET /api/msgraph/pending-actions?conversation_id=...&limit=30, so every strict UI harness (one that 404s unknown routes) needs a stub. ui_tests/fixtures/v2_pending_action_stubs.py is the shared helper and 25 harness files now use it, which is most of the ui_tests churn in this diff. Future harnesses that open a conversation need it too.
  • XSS allowlist: chatHrefForPendingAction is added to TS_SAME_ORIGIN_URL_BUILDERS in scripts/check_xss_sinks.py, with a justification comment. It returns /chat?conversationId=<encoded> plus &m365_pending_action=<encoded id>, and only when normalizePendingActionId accepts the id; otherwise it returns the plain conversation link. Worth a reviewer's look.

Linked issue

Refs #1722

Covers gap 2 (Chat, "M365 pending-action card") of that tracking issue. The issue stays open for the remaining gaps.

Release Notes & Latest Features

  • New Feature
  • Bug Fix
  • UI Enhancement
  • Breaking Change
  • Internal only

Is this visible to end users?

  • Yes
  • No

Is this admin-facing (Admin Settings, governance, deployment, config)?

  • Yes
  • No

Should this become a Latest Feature card?

  • Yes
  • No
  • Already added

This brings an existing classic capability to V2 for parity, and the catalog already has a generic Microsoft 365 Actions card.

Screenshot needed for the card?

  • Yes
  • No
  • Attached

Version bump

  • application/single_app/config.py VERSION third segment bumped, or not needed because this is docs-only
  • deployers/version.txt bumped, or not needed because deployers/ was not changed

VERSION goes from 0.261.306 to 0.261.307. deployers/ is untouched.

Testing / validation

  • node --test --test-reporter=tap functional_tests/test_v2_m365_pending_actions_logic.mjs functional_tests/test_v2_m365_pending_actions_store.mjs functional_tests/test_v2_m365_pending_actions_stream.mjs functional_tests/test_v2_workflow_run_link_routing.mjs: passed, 119/119 (30 + 44 + 34 + 11).
  • python -m pytest functional_tests/test_v2_m365_pending_actions.py -q -p no:cacheprovider: passed, 14 tests.
  • npm run typecheck and npm run build in application/v2_ui: both passed (the build shows only the existing chunk-size warning).
  • python -m pytest ui_tests/test_v2_m365_pending_action_cards.py -q -p no:cacheprovider: passed, 10 tests (Playwright against stubbed endpoints).
  • python -m pytest ui_tests/test_v2_notifications_bell.py -q -p no:cacheprovider: 52 passed, 10 errors. The same 10 teardown errors occur on the unmodified base (47 passed, 10 errors there); they come from Settings endpoints that the harness does not stub. This branch adds 5 passing bell tests.
  • CI-equivalent checks on the committed tree: python scripts/check_xss_sinks.py --base-sha <base> --head-sha <head> <the 16 changed application files> passed; python functional_tests/test_xss_guardrails_checker.py passed 12/12; python scripts/check_broken_access_control.py --base-sha <base> --head-sha <head> application/single_app/config.py passed; python -m py_compile application/single_app/config.py passed; python scripts/check_malicious_pr_security_review.py --base-sha <base> --head-sha <head> --report-file <tmp> --verify-release-age reported no blocker findings.
  • python functional_tests/test_docs_app_surface_coverage.py: passed 7/7. python functional_tests/test_docs_site_quality.py: passed 6/6.
  • Full V2 browser regression (about 55 ui_tests/test_v2_*.py files) after the final rebase: green apart from the items below, each of which fails or errors the same way on the unmodified base.

Known failures not caused by this PR (all reproduced on base 11d84783e):

  • ui_tests/test_v2_document_provenance.py: 3 group tests fail because upstream's group switch now calls GET /api/v2/scope, which ui_tests/fixtures/workspace_authoring.py does not stub.
  • ui_tests/test_v2_workflow_run_card.py: 1 failure; it expects /profile?tab=settings#m365-connection-status while the code links /settings?tab=preferences&section=m365-workflow-connection (lib/m365Links.ts).
  • ui_tests/test_v2_workflow_alert_notices.py: 1 error from unstubbed Settings endpoints (same cause as the bell teardown errors above).
  • functional_tests/test_v2_notifications_bell.py::test_the_administrator_switch_and_the_preference_reach_both_sides pins the literal "features": _build_feature_flags(public_settings, per_user_overrides), which upstream's route_backend_v2.py no longer contains. This PR does not touch that route.

Not run or not validated:

  • Nothing here was exercised against a live Microsoft 365 tenant. The browser tests use stubbed /api/msgraph/pending-actions responses, and the backend endpoints are unchanged.
  • Backend tests that import semantic_kernel do not run in my local environment (broken OpenSSL/cryptography install). This PR has no backend changes.

Documentation

  • Release notes updated, or not needed
  • Feature documentation updated, or not needed
  • Fix documentation updated, or not needed

Release notes are not included in this PR: no entry was added to docs/explanation/release_notes.md for 0.261.307. One can be added before merge if wanted.

Feature documentation: new docs/explanation/features/V2_M365_PENDING_ACTION_CARDS.md, plus updates to V2_NOTIFICATIONS_BELL.md, V2_TERMS_AND_APPROVALS.md, MSGRAPH_PENDING_ACTIONS.md, CHAT_ORCHESTRATION_WORKFLOWS_ROADMAP.md and docs/reference/chat-controls.md. Fix documentation is not needed because no shipped bug is fixed.

Security checklist

  • New Flask routes include @swagger_route(security=get_auth_security())
  • Settings sent to non-admin frontends use sanitize_settings_for_user()
  • Browser JavaScript is served from local SimpleChat static assets only; no CDN-hosted JS
  • No secrets, keys, connection strings, or local-only artifacts are included

The first two items are not applicable: this PR adds no Flask routes and no settings. The added lines of the diff were scanned and contain no credential patterns and no CDN, script-src or dynamic-import references; the external URLs that appear are test fixtures and one issue link in the docs.

Saved outgoing email and calendar actions (Send, Send now, Cancel) rendered
only in classic chat, so notices carrying m365_pending_action opened the
classic page. Under New UI only mode those links are dead ends for
non-admins.

- Extract one shared PendingActionCard, used by the Approvals page and chat.
- Draw the cards in V2 chat the way classic does: inline under the reply that
  saved them, live while that reply streams, and in a conversation-level
  section (with Load more and Refresh) for actions whose reply is not on
  screen.
- Route m365_pending_action notices to V2 /chat with a one-shot focus
  parameter that scrolls to the card, highlights it, then strips the
  parameter.
- Re-read the conversation's list after a reply ends, resumes or is stopped,
  and drop the live card when a stream is detached, so a Stop does not leave
  a stale card behind.
- Stub the new list request in the strict UI harnesses, and add Node,
  functional and UI tests plus feature documentation.

No backend, schema or settings changes. Version 0.261.307.

Refs #1722

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@paullizer
Paul Lizer (paullizer) merged commit 08def99 into paullizer-react-v2-ui Oct 9, 2026
11 checks passed
Paul Lizer (paullizer) added a commit that referenced this pull request Oct 9, 2026
V2's tip landed at .307 (PR #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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant