# Scratchpad — Microsoft Teams Integration V1 - Plan slug: `microsoft-teams-integration-v1` - Created: `2026-03-07` ## What This Is Keep a lightweight, continuously-updated log of discoveries and decisions made while implementing this plan. Prefer short bullets. Append new entries as you learn things, and also *update earlier notes* when a decision changes or an open question is resolved. ## Decisions - (2026-03-07) V1 scope is MSP users only; client-portal Teams experiences are out of scope. - (2026-03-07) Teams will use the tenant-owned Microsoft provider configuration model rather than introducing a Teams-only credential store. - (2026-03-07) Microsoft integration settings should expand from a singleton config into named Microsoft profiles. - (2026-03-07) Teams will bind to one selected Microsoft profile at tenant admin setup time; end users do not choose a profile. - (2026-03-07) Notification scope is simplified for v1: personal Teams activity-feed notifications only, no channel/chat routing. - (2026-03-07) Bot scope is personal-first and command-first, not a general chatbot. - (2026-03-07) Teams should be treated as one tenant integration with four surfaces, not four separate products. - (2026-03-07) Shared command/action execution and shared notification payload generation should be reused across Teams surfaces to avoid duplicate implementations. - (2026-03-07) Microsoft profile migration will be lazy and app-driven instead of a secret-provider-aware SQL migration: the first profile-aware read/write backfills a default profile from legacy tenant secrets and leaves legacy keys in place for compatibility. - (2026-03-07) Default-profile compatibility will be maintained by mirroring the selected default profile back to legacy tenant secrets (`microsoft_client_id`, `microsoft_client_secret`, `microsoft_tenant_id`) until consumer-binding work lands. - (2026-03-07) Profile secrets stay out of SQL. The database stores profile metadata plus a deterministic tenant secret reference (`microsoft_profile__client_secret`). - (2026-03-07) The Microsoft settings screen should become a profile manager in `Integrations -> Providers`, with one card per profile and shared app-registration guidance rendered inline instead of a separate Teams-only credential editor. - (2026-03-07) Until explicit consumer-binding records ship, the UI will surface compatibility bindings by treating the active default profile as the current source for Email, Calendar, and MSP SSO. - (2026-03-07) Teams registration guidance will be generated from deployment base URL plus profile client ID, including tab/bot/message-extension callback URLs and a derived Teams application ID URI (`api:///teams/`). - (2026-03-07) The first Teams setup slice should live in `Integrations -> Providers` next to Microsoft profile management so admins can move between shared Microsoft credential setup and Teams setup without a separate navigation model. - (2026-03-07) Until the tenant-scoped Teams integration record exists, the Teams settings UI should be a guided readiness card: list Microsoft profiles already ready for Teams and route tenants with no eligible profile back to Microsoft profile management. - (2026-03-07) Consumer bindings should be lazy and tenant-scoped like Microsoft profile backfill: legacy Microsoft consumers (`email`, `calendar`, `msp_sso`) get compatibility binding rows on first binding-aware access, while `teams` remains explicit-only and never falls back silently. - (2026-03-07) Teams activity-feed handoffs should reuse existing PSA internal record URLs as the notification source of truth, then derive the Teams personal-tab destination from those URLs instead of introducing a second notification-only entity mapping format. ## Discoveries / Constraints - (2026-03-07) `packages/integrations/src/components/settings/integrations/MicrosoftIntegrationSettings.tsx` already anchors tenant-owned Microsoft config for Outlook inbound email, Outlook calendar, and MSP SSO; this is the right place to evolve toward named profiles. - (2026-03-07) `packages/auth/src/lib/nextAuthOptions.ts` already uses Azure AD / Microsoft OAuth and can resolve tenant-specific Microsoft credentials through MSP SSO resolution. - (2026-03-07) `packages/auth/src/lib/sso/mspSsoResolution.ts` already supports tenant-aware Microsoft provider discovery/resolution for MSP SSO. - (2026-03-07) Existing in-app notifications already store `link` and metadata, and UI components open specific PSA tickets/tasks/documents from those links. - (2026-03-07) Existing notification/deep-link infrastructure can be reused for Teams notification payloads instead of building a second record-linking system. - (2026-03-07) Existing event/workflow infrastructure is a better trigger source for Teams notifications than adding a Teams-specific event bus. - (2026-03-07) Existing extension iframe surfaces are useful precedent for embedded UI and auth forwarding, but they are not a native substitute for Teams bot/message-extension surfaces. - (2026-03-07) Existing Microsoft integration flows for email, calendar, and Entra already establish tenant-secret and Graph API patterns that Teams can reuse. - (2026-03-07) Teams package generation now persists `app_id`, `bot_id`, and a summarized `package_metadata` blob on `teams_integrations`, which keeps setup/status views tenant-local and avoids recomputing storage-less manifest state. - (2026-03-07) Rebinding Teams to a different Microsoft profile must clear persisted package metadata and reset non-`not_configured` install state back to `install_pending` so stale package/install assumptions do not survive profile swaps. - (2026-03-07) The Teams setup UI now has a package handoff card that only enables generation for saved/non-`not_configured` setups, which avoids generating install artifacts from unsaved or invalid profile selections. - (2026-03-07) Teams manifest generation and future action/notification deep links now share the same `TEAMS_PERSONAL_TAB_ENTITY_ID` and Teams tab deep-link builder, which reduces drift between package declarations and runtime navigation targets. - (2026-03-07) `packages/auth/src/lib/nextAuthOptions.ts` now exposes a Teams-specific `buildTeamsAuthOptions(tenantId)` entry point that resolves Microsoft credentials from the tenant-selected Teams profile and intentionally bypasses generic app/global Microsoft fallback for that surface. - (2026-03-07) There was no runtime `/teams/tab` page yet even though the generated Teams manifest already pointed the personal tab there. - (2026-03-07) `server/src/middleware.ts` still does not protect `/teams/*`, so the Teams tab route has to resolve auth state itself and redirect unauthenticated users into the existing MSP sign-in flow. - (2026-03-07) The auth package needed to export `resolveTeamsMicrosoftProviderConfig` through `packages/auth/src/lib/sso/index.ts` so new Teams server surfaces can reuse the tenant-selected Microsoft profile resolver without unsupported deep imports. - (2026-03-07) The Teams auth callback URIs surfaced in Microsoft profile guidance (`/api/teams/auth/callback/tab|bot|message-extension`) were still missing, and `/api/teams/auth/*` would have been blocked by middleware API-key enforcement until explicitly exempted. - (2026-03-07) The existing SSO account-link registry already stores tenant-scoped Microsoft provider account IDs, so Teams user mapping can reuse `findOAuthAccountLink('microsoft', providerAccountId)` plus the `users` table instead of introducing a separate Teams linkage table in the first identity slice. - (2026-03-07) Teams runtime entry points can receive a Microsoft/Teams tenant claim hint (`microsoftTenantId`, `teamsTenantId`, or `tid`) even before richer Teams SDK bootstrap lands, so the shared auth resolver can reject requests whose Teams tenant context does not match the tenant-selected Microsoft profile. - (2026-03-07) Generated Teams personal-tab deep links already encode destination context as a `context` JSON payload, so the runtime `/teams/tab` entry point needs to understand that payload rather than only flat query params. - (2026-03-07) Teams tab and callback entry points were independently constructing MSP sign-in redirects; a shared Teams reauth URL helper is safer because it guarantees consistent callback preservation and gives Teams flows an explicit `teamsReauth=1` marker instead of surfacing generic auth failures. - (2026-03-07) Teams entry points can arrive with `tenant=` rather than a raw tenant UUID. The shared Teams auth resolver needs to translate tenant slugs to tenant IDs before enforcing same-tenant checks, otherwise valid vanity-host or slug-based deep links are rejected. - (2026-03-07) Teams auth state already re-resolves the selected Teams Microsoft profile on every request instead of caching profile identity in the session, so a Teams profile rebind can invalidate stale Microsoft-tenant assumptions immediately as long as the request still carries the old tenant hint. - (2026-03-07) Existing Microsoft consumers still read the legacy tenant-secret keys directly. Introducing profiles without default-secret mirroring would break Outlook email, Outlook calendar, and MSP SSO compatibility. - (2026-03-07) `packages/integrations/src/components/settings/integrations/IntegrationsSettingsPage.tsx` already gives the right long-term home for Microsoft profile management: `Integrations -> Providers`, not a new top-level settings tab. - (2026-03-07) The backend compatibility guard is sufficient for `F015`/`T029-T030`: archiving the default profile is blocked until another profile is made default, which preserves the active compatibility binding. - (2026-03-07) The second slice is complete: `F019-F035` and `T037-T070` pass with the profile manager UI, per-profile readiness/rendering, inline registration guidance, refresh, default switching, and archive confirmation. - (2026-03-07) There was no existing Teams admin settings surface in the repo; `packages/integrations/src/components/settings/integrations/IntegrationsSettingsPage.tsx` `Providers` is the right mount point for the first Teams setup card. - (2026-03-07) The shared `Button` component does not expose custom ids reliably in the jsdom contract harness, so the Teams settings contract test uses role/name queries for refresh actions. - (2026-03-07) The third slice is complete: `F036`, `F048`, `F056`, `F057`, `F058` and `T071`, `T072`, `T095`, `T096`, `T111`, `T112`, `T113`, `T114`, `T115`, `T116` pass with a Providers-mounted Teams setup card, hash-based navigation between Microsoft profiles and Teams setup, and guided remediation when no eligible profile exists. - (2026-03-07) `server/migrations/20260307143000_create_microsoft_profile_consumer_bindings.cjs` adds a tenant-scoped one-row-per-consumer binding table keyed by `(tenant, consumer_type)` with composite FK back to `microsoft_profiles`. - (2026-03-07) `packages/integrations/src/actions/integrations/microsoftActions.ts` now exposes binding-aware helpers/actions: `listMicrosoftConsumerBindings`, `setMicrosoftConsumerBinding`, and `resolveMicrosoftProfileForConsumer`. - (2026-03-07) The fourth slice is complete: `F037`, `F038`, `F039`, `F040`, `F041` and `T073`, `T074`, `T075`, `T076`, `T077`, `T078`, `T079`, `T080`, `T081`, `T082` pass with a real Microsoft consumer-binding model, lazy compatibility binding backfill for legacy Microsoft consumers, and Teams explicit-binding resolution. - (2026-03-07) `server/migrations/20260307153000_create_teams_integrations.cjs` adds the tenant-scoped Teams integration table keyed by `tenant`, linked to a selected Microsoft profile, and storing install status, enabled capabilities, notification categories, and allowed quick actions. - (2026-03-07) `packages/integrations/src/actions/integrations/teamsActions.ts` now exposes `getTeamsIntegrationStatus` and `saveTeamsIntegrationSettings` with tenant-admin gating, client-user rejection, explicit selected-profile validation, and readiness checks before activation. - (2026-03-07) The fifth slice is complete: `F042`, `F043`, `F044`, `F045`, `F046`, `F047` and `T083`, `T084`, `T085`, `T086`, `T087`, `T088`, `T089`, `T090`, `T091`, `T092`, `T093`, `T094` pass with a real Teams integration record plus guarded admin save/load actions. - (2026-03-07) `packages/integrations/src/components/settings/integrations/TeamsIntegrationSettings.tsx` now renders a real Teams setup form with explicit profile selection, readiness checklisting, selected-profile app-registration guidance, capability toggles, notification preferences, and activate/deactivate actions. - (2026-03-07) The sixth slice is complete: `F049`, `F050`, `F051`, `F052`, `F053`, `F054`, `F055` and `T097`, `T098`, `T099`, `T100`, `T101`, `T102`, `T103`, `T104`, `T105`, `T106`, `T107`, `T108`, `T109`, `T110` pass with a saveable Teams setup workflow in the settings UI. - (2026-03-07) `packages/integrations/src/actions/integrations/teamsPackageActions.ts` now builds tenant-specific Teams app package metadata from the selected Teams profile, including manifest structure, personal tab/bot declarations, compose-extension commands, activity-feed activity types, webApplicationInfo, valid domains, and environment-aware base URLs. - (2026-03-07) The seventh slice is complete: `F059`, `F060`, `F061`, `F062`, `F063`, `F064`, `F065`, `F066`, `F067`, `F068`, `F069`, `F071` and `T117`, `T118`, `T119`, `T120`, `T121`, `T122`, `T123`, `T124`, `T125`, `T126`, `T127`, `T128`, `T129`, `T130`, `T131`, `T132`, `T133`, `T134`, `T135`, `T136`, `T137`, `T138`, `T141`, `T142` pass with a generated Teams manifest/package model and guarded package-status retrieval. ## Commands / Runbooks - (2026-03-07) Scaffolded plan folder with: `python3 /Users/roberisaacs/.codex/skills/alga-plan/scripts/scaffold_plan.py "Microsoft Teams Integration V1" --slug microsoft-teams-integration-v1` - (2026-03-07) Backend verification commands: - `cd server && npx vitest run --config vitest.config.ts ../packages/integrations/src/actions/integrations/microsoftActions.test.ts ../packages/integrations/src/actions/integrations/providerReadiness.test.ts` - `pnpm --dir packages/integrations typecheck` - (2026-03-07) UI verification commands: - `cd server && npx vitest run --config vitest.config.ts ../packages/integrations/src/components/settings/integrations/MicrosoftIntegrationSettings.contract.test.tsx` - `cd server && npx vitest run --config vitest.config.ts ../packages/integrations/src/actions/integrations/microsoftActions.test.ts ../packages/integrations/src/actions/integrations/providerReadiness.test.ts ../packages/integrations/src/components/settings/integrations/MicrosoftIntegrationSettings.contract.test.tsx` - `cd server && npx vitest run --config vitest.config.ts ../packages/integrations/src/components/settings/integrations/TeamsIntegrationSettings.contract.test.tsx ../packages/integrations/src/components/settings/integrations/IntegrationsSettingsPage.providers.test.ts` - `cd server && npx vitest run --config vitest.config.ts ../packages/integrations/src/components/settings/integrations/MicrosoftIntegrationSettings.contract.test.tsx ../packages/integrations/src/components/settings/integrations/TeamsIntegrationSettings.contract.test.tsx ../packages/integrations/src/components/settings/integrations/IntegrationsSettingsPage.providers.test.ts` - `cd server && npx vitest run --config vitest.config.ts ../packages/integrations/src/actions/integrations/microsoftActions.test.ts ../packages/integrations/src/actions/integrations/microsoftConsumerBindings.test.ts ../server/src/test/unit/migrations/microsoftConsumerBindingsMigration.test.ts` - `cd server && npx vitest run --config vitest.config.ts ../packages/integrations/src/actions/integrations/teamsActions.test.ts ../server/src/test/unit/migrations/teamsIntegrationsMigration.test.ts` - `cd server && npx vitest run --config vitest.config.ts ../packages/integrations/src/components/settings/integrations/TeamsIntegrationSettings.contract.test.tsx ../packages/integrations/src/components/settings/integrations/IntegrationsSettingsPage.providers.test.ts` - `cd server && npx vitest run --config vitest.config.ts ../packages/integrations/src/actions/integrations/teamsPackageActions.test.ts` - (2026-03-07) Relevant local references: - `packages/integrations/src/components/settings/integrations/MicrosoftIntegrationSettings.tsx` - `packages/integrations/src/components/settings/integrations/MicrosoftIntegrationSettings.contract.test.tsx` - `packages/integrations/src/components/settings/integrations/TeamsIntegrationSettings.tsx` - `packages/integrations/src/components/settings/integrations/TeamsIntegrationSettings.contract.test.tsx` - `packages/integrations/src/components/settings/integrations/IntegrationsSettingsPage.tsx` - `packages/integrations/src/components/settings/integrations/IntegrationsSettingsPage.providers.test.ts` - `packages/integrations/src/actions/integrations/microsoftActions.ts` - `packages/integrations/src/actions/integrations/microsoftConsumerBindings.test.ts` - `packages/integrations/src/actions/integrations/providerReadiness.ts` - `packages/integrations/src/actions/integrations/teamsActions.ts` - `packages/integrations/src/actions/integrations/teamsActions.test.ts` - `packages/integrations/src/actions/integrations/teamsPackageActions.ts` - `packages/integrations/src/actions/integrations/teamsPackageActions.test.ts` - `packages/auth/src/lib/nextAuthOptions.ts` - `packages/auth/src/lib/sso/mspSsoResolution.ts` - `packages/notifications/src/actions/internal-notification-actions/internalNotificationActions.ts` - `server/src/lib/utils/notificationLinkResolver.ts` - `server/src/lib/eventBus` - (2026-03-07) Verified Teams package metadata persistence and invalidation with: - `pnpm --dir packages/integrations typecheck` - `cd server && npx vitest run --config vitest.config.ts ../packages/integrations/src/actions/integrations/teamsActions.test.ts ../packages/integrations/src/actions/integrations/teamsPackageActions.test.ts ../server/src/test/unit/migrations/teamsPackageMetadataMigration.test.ts` - (2026-03-07) Verified Teams tab auth bootstrap with: - `cd server && npx vitest run --config vitest.config.ts src/test/unit/lib/teams/resolveTeamsTabAuthState.test.ts src/test/unit/app/teams/tab/page.test.tsx` - `pnpm --dir packages/auth typecheck` - (2026-03-07) Verified Teams auth callback routes with: - `cd server && npx vitest run --config vitest.config.ts src/test/unit/lib/teams/resolveTeamsTabAuthState.test.ts src/test/unit/app/teams/tab/page.test.tsx src/app/api/teams/auth/callback/bot/route.test.ts src/app/api/teams/auth/callback/message-extension/route.test.ts` - (2026-03-07) Verified Teams linked-user resolution with: - `cd server && npx vitest run --config vitest.config.ts src/test/unit/lib/teams/resolveTeamsLinkedUser.test.ts src/test/unit/lib/teams/resolveTeamsTabAuthState.test.ts src/test/unit/app/teams/tab/page.test.tsx src/app/api/teams/auth/callback/bot/route.test.ts src/app/api/teams/auth/callback/message-extension/route.test.ts` - (2026-03-07) Verified Teams Microsoft-tenant mismatch rejection with: - `cd server && npx vitest run --config vitest.config.ts src/test/unit/lib/teams/resolveTeamsTabAuthState.test.ts src/test/unit/app/teams/tab/page.test.tsx src/app/api/teams/auth/callback/bot/route.test.ts src/app/api/teams/auth/callback/message-extension/route.test.ts src/test/unit/lib/teams/resolveTeamsLinkedUser.test.ts` - (2026-03-07) Verified Teams deep-link destination bootstrap with: - `cd server && npx vitest run --config vitest.config.ts src/test/unit/lib/teams/resolveTeamsTabDestination.test.ts src/test/unit/app/teams/tab/page.test.tsx src/test/unit/lib/teams/resolveTeamsTabAuthState.test.ts src/app/api/teams/auth/callback/bot/route.test.ts src/app/api/teams/auth/callback/message-extension/route.test.ts src/test/unit/lib/teams/resolveTeamsLinkedUser.test.ts` - (2026-03-07) Verified Teams-safe reauthentication redirects with: - `cd server && npx vitest run --config vitest.config.ts src/test/unit/lib/teams/buildTeamsReauthUrl.test.ts src/test/unit/lib/teams/resolveTeamsTabDestination.test.ts src/test/unit/app/teams/tab/page.test.tsx src/test/unit/lib/teams/resolveTeamsTabAuthState.test.ts src/app/api/teams/auth/callback/bot/route.test.ts src/app/api/teams/auth/callback/message-extension/route.test.ts src/test/unit/lib/teams/resolveTeamsLinkedUser.test.ts` - (2026-03-07) Verified Teams tenant-slug resolution with: - `cd server && npx vitest run --config vitest.config.ts src/test/unit/lib/teams/resolveTeamsTabAuthState.test.ts src/test/unit/app/teams/tab/page.test.tsx src/app/api/teams/auth/callback/bot/route.test.ts src/app/api/teams/auth/callback/message-extension/route.test.ts src/test/unit/lib/teams/buildTeamsReauthUrl.test.ts src/test/unit/lib/teams/resolveTeamsTabDestination.test.ts src/test/unit/lib/teams/resolveTeamsLinkedUser.test.ts` - (2026-03-07) Verified Teams profile-rebind invalidation with: - `cd server && npx vitest run --config vitest.config.ts src/test/unit/lib/teams/resolveTeamsTabAuthState.test.ts` - (2026-03-07) Verified Teams tab destination access gating and state distinction with: - `cd server && npx vitest run --config vitest.config.ts src/test/unit/app/teams/tab/page.test.tsx src/app/api/teams/auth/callback/bot/route.test.ts src/test/unit/lib/teams/resolveTeamsTabAccessState.test.ts src/test/unit/lib/teams/resolveTeamsTabAuthState.test.ts` - (2026-03-07) Verified Teams selected-profile precedence over broad Microsoft env credentials with: - `cd packages/auth && npx vitest run --config vitest.config.ts src/lib/sso/teamsMicrosoftProviderResolution.test.ts` - (2026-03-07) Verified Teams-safe auth remediation copy with: - `cd server && npx vitest run --config vitest.config.ts src/test/unit/lib/teams/resolveTeamsTabAuthState.test.ts` - (2026-03-07) Verified Teams personal-tab default landing and fallback routing with: - `cd server && npx vitest run --config vitest.config.ts src/test/unit/app/teams/tab/page.test.tsx src/test/unit/lib/teams/resolveTeamsTabDestination.test.ts` - (2026-03-07) Verified Teams activity-feed notification deep-link routing with: - `cd server && npx vitest run --config vitest.config.ts src/test/unit/lib/teams/resolveTeamsTabDestination.test.ts src/test/unit/app/teams/tab/page.test.tsx` - `cd server && npx vitest run --config vitest.config.ts ../packages/integrations/src/actions/integrations/teamsPackageActions.test.ts` - `pnpm --dir packages/integrations typecheck` ## Progress Log - (2026-03-07) Completed `F070` by adding a migration for `teams_integrations.app_id`, `bot_id`, and `package_metadata`, and by persisting generated Teams package metadata in `packages/integrations/src/actions/integrations/teamsPackageActions.ts`. - (2026-03-07) Completed `F072` by clearing stored Teams package metadata and resetting install readiness to `install_pending` when the selected Teams Microsoft profile changes in `packages/integrations/src/actions/integrations/teamsActions.ts`. - (2026-03-07) Completed `T139`, `T140`, `T143`, and `T144` with focused migration and action-layer tests covering package metadata storage and stale-profile invalidation behavior. - (2026-03-07) Completed `F073` by adding a Teams package handoff panel in `packages/integrations/src/components/settings/integrations/TeamsIntegrationSettings.tsx` that prepares tenant package metadata and downloads a manifest JSON snapshot for admin install handoff. - (2026-03-07) Completed `T145` and `T146` with UI contract coverage for successful package handoff generation/download and recoverable package-generation failures. - (2026-03-07) Completed `F074` by adding shared Teams personal-tab deep-link builders and template targets in `packages/integrations/src/actions/integrations/teamsPackageActions.ts`, keeping package declarations aligned with future notification/action destinations. - (2026-03-07) Completed `T147` and `T148` with package-action tests covering deep-link template generation and prerequisite guard behavior. - (2026-03-07) Completed `F075` by adding `packages/auth/src/lib/sso/teamsMicrosoftProviderResolution.ts` and wiring `buildTeamsAuthOptions(tenantId)` in `packages/auth/src/lib/nextAuthOptions.ts` so Teams-specific auth can use the selected Teams Microsoft profile instead of broad fallback credentials. - (2026-03-07) Completed `T149` and `T150` with focused resolver tests plus a NextAuth contract test covering the Teams-specific auth-options entry point. - (2026-03-07) Completed `F076` by adding `server/src/lib/teams/resolveTeamsTabAuthState.ts` plus the first runtime `/teams/tab` page in `server/src/app/teams/tab/page.tsx`, which resolves MSP tenant/user context, redirects unauthenticated users to `/auth/msp/signin`, and renders Teams-safe remediation for non-ready tenants. - (2026-03-07) Completed `T151` and `T152` with focused Teams-tab auth-state tests in `server/src/test/unit/lib/teams/resolveTeamsTabAuthState.test.ts` and page-wiring coverage in `server/src/test/unit/app/teams/tab/page.test.tsx`. - (2026-03-07) Completed `F077` and `F078` by adding shared Teams auth callback handling in `server/src/lib/teams/handleTeamsAuthCallback.ts` plus concrete `/api/teams/auth/callback/bot` and `/api/teams/auth/callback/message-extension` routes that resolve tenant/MSP user context from the existing Teams auth-state resolver and return Teams-safe popup payloads. - (2026-03-07) Added `/api/teams/auth/` to the middleware API-key skip list so Teams browser-based auth callbacks can complete without being rejected as generic API traffic. - (2026-03-07) Completed `T153`, `T154`, `T155`, and `T156` with route-level tests covering ready callback payloads, unauthenticated redirect behavior, and safe rejected-access payloads for both bot and message-extension surfaces. - (2026-03-07) Completed `F079`, `F080`, `F081`, and `F082` via the shared Teams auth-state resolver: only internal MSP users can proceed, client users are rejected explicitly, the resolver prefers `getSessionWithRevocationCheck()` with the existing auth fallback path, and missing/not-ready Teams profiles return remediation-safe `not_configured` or `invalid_profile` states. - (2026-03-07) Completed `T157`, `T158`, `T159`, `T160`, `T161`, `T162`, `T163`, and `T164` by extending the Teams auth-state unit coverage to assert MSP-only acceptance, client-user rejection, revocation-checked session reuse, and admin-readable not-configured/invalid-profile failures. - (2026-03-07) Completed `F083` by adding `server/src/lib/teams/resolveTeamsLinkedUser.ts`, which resolves a tenant-scoped Microsoft provider account link back to the matching internal PSA user and rejects missing, cross-tenant, or client-user mappings safely. - (2026-03-07) Completed `T165` and `T166` with focused coverage in `server/src/test/unit/lib/teams/resolveTeamsLinkedUser.test.ts` for linked-user resolution plus missing-identity, cross-tenant, and client-user rejection paths. - (2026-03-07) Completed `F084` by extending `server/src/lib/teams/resolveTeamsTabAuthState.ts` to compare incoming Teams Microsoft-tenant hints against the tenant-selected Teams Microsoft profile and reject mismatches centrally. - (2026-03-07) Completed `T167` and `T168` by extending Teams auth-state, tab-page, and auth-callback tests to cover matching tenant claims and wrong-Microsoft-tenant rejection behavior. - (2026-03-07) Completed `F085` by adding `server/src/lib/teams/resolveTeamsTabDestination.ts` and wiring `/teams/tab` to bootstrap destinations from Teams `context` payloads while preserving the exact deep-link payload through MSP sign-in redirects. - (2026-03-07) Completed `T169` and `T170` with focused destination-parser coverage and Teams-tab page tests covering deep-link bootstrap plus safe rejection/redirect behavior for protected deep-link entries. - (2026-03-07) Completed `F086` by adding `server/src/lib/teams/buildTeamsReauthUrl.ts` and routing both Teams tab and Teams auth callbacks through the same explicit Teams-safe MSP reauthentication path. - (2026-03-07) Completed `T171` and `T172` with focused reauth URL helper coverage plus tab/bot callback tests asserting expired or invalid Teams sessions redirect to Teams-safe reauth URLs instead of leaking raw auth failures. - (2026-03-07) Completed `F087` by extending `server/src/lib/teams/resolveTeamsTabAuthState.ts` to resolve tenant slugs via `getTenantIdBySlug` before comparing Teams entry-point tenant hints to the authenticated MSP tenant. - (2026-03-07) Completed `T173` and `T174` with Teams auth-state, Teams tab page, and bot callback tests covering slug-based tenant resolution on vanity-host-style entry points plus safe rejection when the slug resolves to the wrong tenant. - (2026-03-07) Completed `F088` by verifying that `server/src/lib/teams/resolveTeamsTabAuthState.ts` re-reads Teams profile binding state per request, so stale Microsoft-tenant assumptions are rejected immediately after a Teams profile rebind. - (2026-03-07) Completed `T175` and `T176` with a focused Teams auth-state regression test that simulates a tenant profile rebind between requests and asserts the old Teams Microsoft tenant hint is rejected on the next request. - (2026-03-07) Completed `F089` by adding `server/src/lib/teams/resolveTeamsTabAccessState.ts` and wiring `server/src/app/teams/tab/page.tsx` through it so a ready Teams SSO session still has to pass existing PSA permission checks plus tenant-scoped entity lookup before ticket, project-task, contact, time-entry, or approval destinations are treated as accessible. - (2026-03-07) Completed `T177` and `T178` with focused access-resolver and Teams-tab page tests covering allowed destination access, permission-denied short-circuiting, tenant-scoped not-found handling, and Teams-safe fallback rendering after authentication succeeds. - (2026-03-07) Completed `F090` by verifying the existing `packages/auth/src/lib/sso/teamsMicrosoftProviderResolution.ts` implementation already resolves Teams auth exclusively from the tenant-selected Teams Microsoft profile instead of broad app/global Microsoft environment credentials. - (2026-03-07) Completed `T179` and `T180` with explicit resolver regression tests asserting Teams ignores broad Microsoft env credentials both when a valid selected profile exists and when Teams setup/profile state is missing or invalid. - (2026-03-07) Completed `F091` by verifying the Teams tab page and Teams bot auth callback keep `unauthenticated`, `forbidden`, and `not_configured` outcomes distinct at the surface boundary instead of collapsing them into one generic failure path. - (2026-03-07) Completed `T181` and `T182` with surface-level tests covering not-configured Teams tab rendering and not-configured bot auth callback payloads alongside the existing unauthenticated redirect and forbidden-access cases. - (2026-03-07) Completed `F092` by verifying the shared Teams auth-state resolver only returns human-readable remediation copy that remains safe to surface inside Teams tab/callback UI constraints. - (2026-03-07) Completed `T183` and `T184` with a focused auth-state test asserting unauthenticated, client-user, and invalid-profile failures use safe remediation text and avoid raw OAuth/provider jargon. - (2026-03-07) Completed `F093` by verifying the existing `/teams/tab` route acts as the Teams personal-tab entry point for PSA and can render a ready default destination without additional Teams context. - (2026-03-07) Completed `T185` and `T187` with a Teams-tab page test asserting the default ready state renders the `my_work` destination for the personal-tab entry path. - (2026-03-07) Completed `F094` by verifying the default Teams tab destination model already resolves to `my_work`, which matches the intended PSA technician landing surface for the personal tab. - (2026-03-07) Completed `T186` and `T188` with destination-parser coverage asserting unsupported or malformed Teams context falls back safely to `my_work` instead of failing open. - (2026-03-07) Completed `F095`, `F096`, `F097`, `F098`, and `F099` by expanding the Teams tab destination model and page shell so ticket, project-task, approval, time-entry, and contact deep links all render explicit entity context, with contact links carrying optional client context from Teams message-driven entry points. - (2026-03-07) Completed `T189` through `T200` with parser and page coverage for valid ticket/project-task/approval/time-entry/contact deep links plus guard coverage for incomplete context payloads falling back safely to `my_work`. - (2026-03-07) Completed `F100` by changing destination copy from generic bootstrap text to entity-specific titles and summaries so the initial Teams tab load visibly confirms the intended PSA entity. - (2026-03-07) Completed `F101` by changing post-auth record-access failures from a dead-end error card into a safe `my_work` fallback shell with explanatory messaging about the unavailable requested destination. - (2026-03-07) Completed `T201` and `T202` with page tests asserting unavailable ticket/project-task/approval/time-entry/contact links land on the `my_work` fallback shell and preserve the requested-destination context in user-facing remediation copy. - (2026-03-07) Completed `F102` by adding a shared Teams full-PSA URL builder and surfacing an `Open in full PSA` handoff from the tab shell for ticket, project-task, approval, time-entry, and contact destinations, including fallback states that still preserve the originally requested record URL. - (2026-03-07) Completed `T203` and `T204` with a focused URL-builder test plus page assertions covering visible full-PSA handoff links for entity destinations and omission of that link for the default `my_work` landing. - (2026-03-07) Completed `F103` by reconciling the plan with the existing access-state gate: the Teams tab only renders a destination after PSA permission checks and tenant-scoped entity resolution succeed, and otherwise falls back safely instead of rendering unauthorized content. - (2026-03-07) Completed `T205` and `T206` against the existing `resolveTeamsTabAccessState` happy/guard coverage and the tab-page fallback tests that already exercise authorization-preserving behavior for ready versus unavailable destinations. - (2026-03-07) Completed `F104` by replacing the ready-state Teams record shell with a same-origin iframe of the existing PSA record route when an authorized entity destination is available, so the tab now reuses PSA screens/components instead of keeping a parallel Teams-only detail view. - (2026-03-07) Completed `T207` and `T208` with page assertions proving authorized entity destinations render an embedded PSA route while fallback states intentionally suppress that embed and stay on the safe Teams shell. - (2026-03-07) Completed `F105` by keeping the embedded PSA composition on same-origin internal MSP routes that inherit the authenticated MSP session and tenant-scoped host context, avoiding any second auth callback or token-bearing bootstrap channel inside the iframe URL. - (2026-03-07) Completed `T209` and `T210` with URL-builder tests asserting embedded PSA routes remain relative `/msp/...` paths without callback/token parameters plus fallback-shell tests that suppress the embed when the requested destination is unavailable. - (2026-03-07) Completed `F106` by teaching Teams personal-tab routing and Teams deep-link helpers to derive supported tab destinations from existing PSA notification URLs (`/msp/tickets/...`, `/msp/projects/...?...`, `/msp/time-sheet-approvals?...`, `/msp/time-entry?...`, `/msp/contacts/...`) so activity-feed handoffs can target the exact tab destination without a second notification-specific mapping layer. - (2026-03-07) Completed `T211` and `T212` with unit and page coverage for notification-link destination parsing, Teams deep-link generation from PSA URLs, and safe my-work fallback when a notification targets an unsupported or unavailable record. - `server/migrations/20260307120000_create_microsoft_profiles.cjs` - `server/migrations/20260307143000_create_microsoft_profile_consumer_bindings.cjs` - `server/migrations/20260307153000_create_teams_integrations.cjs` - `server/src/test/unit/migrations/microsoftConsumerBindingsMigration.test.ts` - `server/src/test/unit/migrations/teamsIntegrationsMigration.test.ts` - (2026-03-07) The focused vitest slice still emits pre-existing React `act(...)` warnings from `MicrosoftIntegrationSettings.contract.test.tsx`; the tests pass, but the harness remains noisy. - (2026-03-07) The Teams setup contract tests currently emit similar non-blocking React `act(...)` warnings while asserting async save flows; the tests pass, but the harness remains noisy. - (2026-03-07) `npx vitest run --config vitest.config.ts src/test/unit/app/teams/tab/page.test.tsx src/test/unit/lib/teams/resolveTeamsTabDestination.test.ts` - (2026-03-07) `npx vitest run --config vitest.config.ts src/test/unit/app/teams/tab/page.test.tsx src/test/unit/lib/teams/resolveTeamsTabDestination.test.ts src/test/unit/lib/teams/buildTeamsFullPsaUrl.test.ts` - (2026-03-07) `npx vitest run --config vitest.config.ts src/test/unit/app/teams/tab/page.test.tsx` - (2026-03-07) `npx vitest run --config vitest.config.ts src/test/unit/app/teams/tab/page.test.tsx src/test/unit/lib/teams/buildTeamsFullPsaUrl.test.ts` - (2026-03-07) Verified bot-result, message-extension-result, cold-start tab, and escalation-path behavior with: - `cd server && npx vitest run --config vitest.config.ts src/test/unit/lib/teams/resolveTeamsTabAccessState.test.ts src/test/unit/lib/teams/resolveTeamsTabDestination.test.ts src/test/unit/app/teams/tab/page.test.tsx ../packages/integrations/src/actions/integrations/teamsPackageActions.test.ts` - `pnpm --dir packages/integrations typecheck` - `pnpm --dir server exec tsc -p tsconfig.json --noEmit --pretty false` - (2026-03-07) Running multiple coverage-enabled Vitest commands in parallel can race on `server/coverage/.tmp`; rerun sequentially for a clean green verification slice. - (2026-03-07) Next unchecked feature after this slice is `F111` (Shared Teams action registry exists for bot commands, message-extension actions, and card/dialog submits). ## Links / References - Microsoft Teams docs used during investigation: - Tabs overview: `https://learn.microsoft.com/en-us/microsoftteams/platform/tabs/what-are-tabs` - Tab SSO overview: `https://learn.microsoft.com/en-us/microsoftteams/platform/tabs/how-to/authentication/tab-sso-overview` - Tab SSO manifest: `https://learn.microsoft.com/en-us/microsoftteams/platform/tabs/how-to/authentication/tab-sso-manifest` - Bot SSO overview: `https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/authentication/bot-sso-overview` - Bot SSO manifest: `https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/authentication/bot-sso-manifest` - Activity feed notifications: `https://learn.microsoft.com/en-us/microsoftteams/platform/tabs/send-activity-feed-notification` - Deep links: `https://learn.microsoft.com/en-us/microsoftteams/platform/concepts/build-and-test/deep-links` - Search commands: `https://learn.microsoft.com/en-us/microsoftteams/platform/messaging-extensions/how-to/search-commands/define-search-command` - Action commands: `https://learn.microsoft.com/en-us/microsoftteams/platform/messaging-extensions/how-to/action-commands/define-action-command` - Dialogs / task modules: `https://learn.microsoft.com/en-us/microsoftteams/platform/task-modules-and-cards/task-modules/invoking-task-modules` - Existing related plans: - `ee/docs/plans/2026-02-23-msp-tenant-first-sso-provider-resolution` - `ee/docs/plans/2026-02-20-entra-integration-phase-1` ## Open Questions - Do existing Microsoft consumers beyond Teams need explicit per-consumer profile selection UX in v1, or is a default-binding compatibility path sufficient initially? - How much approval behavior belongs in Teams quick actions versus deep-linking into the tab? - What exact tenant-by-tenant Teams app packaging and distribution flow should be used across local/dev/staging/prod? ## Additional Discoveries / Constraints - (2026-03-07) The existing Teams tab runtime already understood direct `context` and notification-style PSA URLs; bot and message-extension result handoff only needed explicit surface-specific link aliases plus stable deep-link builders, not a new tab route. - (2026-03-07) The personal tab shell is a good place to surface invoking-surface context (`bot`, `message_extension`, `notification`) because it confirms why the user landed on a record without changing destination authorization semantics. - (2026-03-07) The project-task Teams access check should stay local to Teams and use `ProjectService.getTasks(projectId, context)` for project/task consistency instead of widening the shared `IProjectTask` interface with a `project_id` field that ripples through unrelated project-editing call sites. - (2026-03-07) The smallest reusable Teams action-layer pattern in this repo is a typed registry plus normalized DTO/result unions in `server/src/lib/teams/actions`, not the heavier workflow runtime registry; that keeps bot, message-extension, and quick-action semantics aligned with the existing Teams auth/setup code. - (2026-03-07) Teams action results can derive Teams-tab links from existing PSA record URLs by flowing through `buildTeamsFullPsaUrl` first and only then calling the Teams package deep-link builders, which keeps the PSA route as the source of truth. - (2026-03-07) The first idempotency slice for Teams actions is process-local and keyed by `tenant + user + action + idempotencyKey`; that is enough to prevent duplicate mutation execution within the current app process while keeping the action layer free of new persistence until a later concurrency hardening pass is justified. - (2026-03-07) The first Teams bot transport can stay SDK-free: a Next.js route can accept Bot Framework-style activity JSON, resolve tenant/user context locally, and map shared Teams action results into simple hero-card responses without introducing `botbuilder`. - (2026-03-07) Bot requests need a tenant-resolution helper separate from browser SSO because webhook activities arrive without a PSA session; the tenant can be resolved from an explicit query hint first and then from the selected Teams profile's Microsoft tenant ID. - (2026-03-07) Bot follow-up shortcuts should be filtered through `listAvailableTeamsActions(...)` so ticket result cards only advertise tenant-enabled actions and stay aligned with central Teams capability gating. - (2026-03-07) The smallest useful bot mutation syntax is `assign ticket to ` with `to me` as the default fallback and `add note : ` for note entry; both shapes keep parsing deterministic without introducing multi-step bot state. - (2026-03-07) Resolving a non-self assignee from the bot should require `user:read` permission and only match active internal PSA users by exact user ID, email, username, or full name so Teams assignment stays tenant-scoped and predictable. - (2026-03-07) Pending-approval lookup needs to mirror the existing Manager Approval dashboard scope logic instead of `TimeSheetService.list(...)`, because the service layer does not currently apply manager / reports-to visibility for time-sheet approvals. - (2026-03-07) The current time-sheet approval model supports `approve` and `request_changes`, not a distinct reject mutation, so the Teams bot treats `reject approval ...` as a user-friendly alias for the shared `request_changes` action. - (2026-03-07) The first Teams message-extension slice can stay SDK-free like the bot transport: a Next.js invoke route can accept `composeExtension/query` payloads directly and reuse tenant/user resolution plus the shared Teams action layer for result cards. - (2026-03-07) Approval lookup needed a reusable helper outside the action registry so both bot approvals and message-extension search can share the same manager/reports-to visibility rules and optional query filtering. - (2026-03-07) The smallest safe v1 message-extension search surface is one `searchRecords` command for `compose` and `commandBox` contexts that aggregates tickets, tasks, contacts, and approvals into shared `open_record` hero cards. - (2026-03-07) The first message-context action slice can reuse the same message-extension route by handling `composeExtension/fetchTask` and `composeExtension/submitAction`, returning Teams-safe task responses without introducing a second webhook surface. - (2026-03-07) The current message-action transport is intentionally a handoff foundation: it preserves Teams-authenticated message context and shows a task-module preview while later slices add actual message-to-ticket and message-to-update mutations. - (2026-03-07) Teams message-extension pagination can use native `queryOptions.skip/count` windowing instead of a custom continuation token; for the merged multi-entity result set, fetching up to `skip + count` per entity and slicing centrally is sufficient for the first paged slice. - (2026-03-07) The cleanest way to surface message-extension quick actions is to reuse `listAvailableTeamsActions(...)` per search hit and append only currently available mutation titles to the shared `open_record` card summary, which keeps result cards aligned with tenant allowed-action settings without introducing premature action buttons. - (2026-03-07) The first inline Teams message-to-ticket slice can stay inside `teamsMessageExtensionHandler.ts` as long as it still reuses the shared ticket model for creation, tenant default board/status/priority configuration, and shared `open_record` link mapping for confirmation. - (2026-03-07) Storing Teams message provenance plus the submit idempotency key in `tickets.attributes` is the smallest migration-free way to satisfy message-source auditability and duplicate-submit replay for Teams message actions. - (2026-03-07) The first message-to-update slice can stay in `server/src/lib/teams/messageExtension/teamsMessageExtensionHandler.ts` as long as ticket mutations still flow through the shared Teams action registry and project-task updates fail safely into a Teams-tab handoff instead of inventing a second task-specific mutation path. - (2026-03-07) Ticket message-to-update provenance is simplest when stored on the created `comments.metadata` payload, so the shared `TicketService.addComment(...)` contract now needs to accept optional metadata for both internal notes and customer-visible replies. - (2026-03-07) A generic `Continue in Teams tab` action can be attached to message-action task modules whenever `teams_integrations` already has `appId` plus `packageMetadata.baseUrl`; if those install artifacts are missing, the task flow should remain functional without the handoff button. - (2026-03-07) `createTicketFromMessage` and `updateFromMessage` task-module UIs can stay in `teamsMessageExtensionHandler.ts`, but their submit paths should route through dedicated registry actions (`create_ticket_from_message`, `update_from_message`) so message-driven workflows share the same idempotency, permission, and result-link plumbing as the rest of Teams. - (2026-03-07) The cleanest Teams notification slice is a second delivery channel hanging off `packages/notifications/src/realtime/internalNotificationBroadcaster.ts`, so existing internal notification producers and workflow-triggered notification rows do not need Teams-specific fanout logic. - (2026-03-07) Teams activity-feed delivery can reuse the existing `internal_notifications.link` PSA URL by converting it with `buildTeamsPersonalTabDeepLinkFromPsaUrl(...)`; no notification-specific destination mapping table is needed. - (2026-03-07) Category routing for Teams notifications is easiest to derive from the notification row itself: template names cover assignment/customer-reply/SLA, while approval requests can be recognized from approval-style PSA links. - (2026-03-07) Delivering personal Teams activity-feed notifications requires the Teams app manifest to request the `TeamsActivity.Send.User` application RSC permission in addition to the existing activity declaration. ## Progress Log - (2026-03-07) Completed `F107` and `F108` by teaching `server/src/lib/teams/resolveTeamsTabDestination.ts` to resolve `botResultLink` and `messageExtensionResultLink` entries, adding explicit entry-source handling in `server/src/app/teams/tab/page.tsx`, and exposing surface-specific Teams deep-link helpers in `packages/integrations/src/actions/integrations/teamsPackageActions.ts`. - (2026-03-07) Completed `F109` by refining the not-configured Teams tab copy so cold-start tenants get a setup-specific heading and remediation message instead of a generic unavailable state. - (2026-03-07) Completed `F110` by making the tab shell explicitly describe the `Open in full PSA` handoff as the escalation path when a workflow needs more context than Teams cards or quick actions provide. - (2026-03-07) Completed `T213`, `T214`, `T215`, and `T216` with destination-parser, page-shell, and deep-link-builder coverage for bot-result and message-extension-result tab handoff plus safe fallback behavior. - (2026-03-07) Completed `T217`, `T218`, `T219`, and `T220` with page-shell coverage for graceful cold-start setup messaging and explicit full-PSA escalation copy. - (2026-03-07) Fixed two pre-existing server TypeScript blockers encountered during verification by narrowing `normalizeTenantClaim` to accept `undefined` in `server/src/lib/teams/resolveTeamsTabAuthState.ts` and by keeping the project-task Teams access check local in `server/src/lib/teams/resolveTeamsTabAccessState.ts` instead of widening shared project-task interfaces. - (2026-03-07) Completed `F111` through `F126` by adding `server/src/lib/teams/actions/teamsActionRegistry.ts`, which centralizes Teams action definitions, input normalization, target resolution, permission/capability gating, result mapping, allowed-action metadata, and idempotent mutation replay for shared bot/message-extension/quick-action execution. - (2026-03-07) Completed `T221` through `T252` with `server/src/test/unit/lib/teams/actions/teamsActionRegistry.test.ts`, covering registry metadata, input normalization, ticket/task/contact/approval/time-entry resolution, permission guards, consistent validation errors, invoking-surface metadata, PSA-first deep-link generation, idempotent mutation replay, partial-failure remediation, capability gating, and service reuse for time entry and approval mutations. - (2026-03-07) Exported `getTeamsIntegrationExecutionState(tenant)` from `packages/integrations/src/actions/integrations/teamsActions.ts` so future Teams route handlers can read install status, enabled capabilities, allowed actions, app id, and package metadata without duplicating Teams integration row mapping. - (2026-03-07) Completed `F127` through `F134` by adding an SDK-free Teams bot transport in `server/src/app/api/teams/bot/messages/route.ts` and `server/src/lib/teams/bot/teamsBotHandler.ts`, plus `server/src/lib/teams/resolveTeamsTenantContext.ts` for tenant selection from Teams webhook context. The handler enforces personal-scope-only behavior, resolves linked MSP users, renders help/unsupported guidance, parses supported bot commands, and filters follow-up shortcuts through tenant allowed-action state. - (2026-03-07) Completed `F135`, `F136`, and `F137` by wiring the bot `my tickets` command through the shared Teams action layer and rendering summary cards with ticket number/title/status-style summaries plus Teams-tab and full-PSA links. - (2026-03-07) Completed `F138` and `F139` by wiring `ticket ` through the shared Teams action layer, enriching open-record summaries with entity-specific detail, and returning clear recoverable not-found/permission failures inside bot-safe cards. - (2026-03-07) Completed `T253` through `T278` with `server/src/test/unit/lib/teams/bot/teamsBotHandler.test.ts` and `server/src/app/api/teams/bot/messages/route.test.ts`, covering route presence, invalid JSON handling, welcome/help responses, unsupported-command recovery, personal-scope-only enforcement, allowed-action filtering, shared entity-reference parsing, `my tickets` cards/deep links, and `ticket ` success/error behavior. - (2026-03-07) Verified shared Teams action-layer execution with: - `cd server && npx vitest run --config vitest.config.ts src/test/unit/lib/teams/actions/teamsActionRegistry.test.ts` - `pnpm --dir server exec tsc -p tsconfig.json --noEmit --pretty false` - `pnpm --dir packages/integrations typecheck` - (2026-03-07) Verified Teams bot foundation and lookup commands with: - `cd server && npx vitest run --config vitest.config.ts src/test/unit/lib/teams/bot/teamsBotHandler.test.ts src/app/api/teams/bot/messages/route.test.ts src/test/unit/lib/teams/actions/teamsActionRegistry.test.ts` - `pnpm --dir server exec tsc -p tsconfig.json --noEmit --pretty false` - (2026-03-07) Verified Teams bot mutation commands with: - `cd server && npx vitest run --config vitest.config.ts src/test/unit/lib/teams/bot/teamsBotHandler.test.ts src/app/api/teams/bot/messages/route.test.ts src/test/unit/lib/teams/actions/teamsActionRegistry.test.ts` - `pnpm --dir server exec tsc -p tsconfig.json --noEmit --pretty false` - (2026-03-07) Verified Teams bot approval commands with: - `cd server && npx vitest run --config vitest.config.ts src/test/unit/lib/teams/actions/teamsActionRegistry.test.ts src/test/unit/lib/teams/bot/teamsBotHandler.test.ts` - `pnpm --dir server exec tsc -p tsconfig.json --noEmit --pretty false` - (2026-03-07) Verified Teams message-extension search with: - `cd server && npx vitest run --config vitest.config.ts src/test/unit/lib/teams/actions/teamsActionRegistry.test.ts src/test/unit/lib/teams/messageExtension/teamsMessageExtensionHandler.test.ts src/app/api/teams/message-extension/query/route.test.ts` - `pnpm --dir server exec tsc -p tsconfig.json --noEmit --pretty false` - (2026-03-07) Verified Teams message-extension action transport and focused command definitions with: - `cd server && npx vitest run --config vitest.config.ts src/test/unit/lib/teams/messageExtension/teamsMessageExtensionHandler.test.ts src/app/api/teams/message-extension/query/route.test.ts ../packages/integrations/src/actions/integrations/teamsPackageActions.test.ts` - `pnpm --dir server exec tsc -p tsconfig.json --noEmit --pretty false` - `pnpm --dir packages/integrations typecheck` - (2026-03-07) Verified Teams message-extension pagination with: - `cd server && npx vitest run --config vitest.config.ts src/test/unit/lib/teams/messageExtension/teamsMessageExtensionHandler.test.ts` - `pnpm --dir server exec tsc -p tsconfig.json --noEmit --pretty false` - (2026-03-07) Verified Teams message-extension quick-action surfacing with: - `cd server && npx vitest run --config vitest.config.ts src/test/unit/lib/teams/messageExtension/teamsMessageExtensionHandler.test.ts` - `pnpm --dir server exec tsc -p tsconfig.json --noEmit --pretty false` - (2026-03-07) Verified Teams message-extension shared surface metadata and PSA-first search access patterns with: - `cd server && npx vitest run --config vitest.config.ts src/test/unit/lib/teams/messageExtension/teamsMessageExtensionHandler.test.ts` - `pnpm --dir server exec tsc -p tsconfig.json --noEmit --pretty false` - (2026-03-07) Verified Teams message-to-ticket task-card creation, inline submit, message metadata persistence, and duplicate-submit replay with: - `cd server && npx vitest run --config vitest.config.ts src/test/unit/lib/teams/messageExtension/teamsMessageExtensionHandler.test.ts` - `pnpm --dir server exec tsc -p tsconfig.json --noEmit --pretty false` - (2026-03-07) Completed `F140`, `F141`, `F142`, and `F143` by teaching `server/src/lib/teams/bot/teamsBotHandler.ts` to parse `assign ticket` commands, default `assign ticket ` to self-assignment, resolve other assignees from active internal users with explicit `user:read` gating, and return the shared action result with an assignee-specific success summary plus Teams-tab/full-PSA links. - (2026-03-07) Completed `F144`, `F145`, and `F146` by teaching the same bot handler to parse `add note : `, preserve ticket context when the note body is missing, execute the shared `add_note` Teams action, and return the saved-note confirmation with follow-up deep links. - (2026-03-07) Completed `F147`, `F148`, and `F149` by extending the bot handler with `reply to contact : `, preserving ticket context when only the ticket reference is present, executing the shared `reply_to_contact` action, and returning the customer-reply confirmation with Teams-tab/full-PSA links. - (2026-03-07) Completed `F150`, `F151`, and `F152` by extending the bot handler with `log time ticket|task : `, parsing minute/hour durations into the shared `log_time` action input, defaulting the time entry to the trailing duration ending now, and supporting both ticket and project-task targets with the same shared mutation path. - (2026-03-07) Completed `T279` through `T286` with focused bot-handler tests covering assignment success, assignee lookup, missing-ticket validation, user-read permission failures during assignee resolution, and safe not-found mutation results for bot action handoff. - (2026-03-07) Completed `T287` through `T292` with bot-handler tests covering note-command parsing, missing-note remediation that preserves ticket context, shared `add_note` execution, and successful follow-up link rendering after note creation. - (2026-03-07) Completed `T293` through `T298` with bot-handler tests covering reply-command parsing, missing-reply remediation, shared `reply_to_contact` execution, and successful follow-up link rendering after a customer-visible reply is created. - (2026-03-07) Completed `T299` through `T304` with bot-handler tests covering missing work-item/duration remediation, shared ticket/task `log_time` execution, duration-and-note normalization, and follow-up link rendering after Teams time entry creation. - (2026-03-07) Completed `F153` by adding a shared `my_approvals` Teams action plus bot support for `my approvals`, reusing the Manager Approval dashboard scope rules so the bot lists only approval items the signed-in approver can review. - (2026-03-07) Completed `F154` by extending the bot parser/UX with `approve approval ` and `request changes` / `reject approval : ` commands that execute the shared `approval_response` action and return Teams-tab/full-PSA follow-up links. - (2026-03-07) Completed `T305` and `T306` with registry + bot coverage for pending-approval lookup, permission failures, approval summaries, and approval-result shortcut rendering. - (2026-03-07) Completed `T307` and `T308` with bot-handler coverage for approval approval/request-changes execution plus missing-reference and missing-comment remediation. - (2026-03-07) Completed `F155`, `F156`, `F157`, `F158`, `F159`, and `F160` by adding the SDK-free Teams message-extension route in `server/src/app/api/teams/message-extension/query/route.ts` plus `server/src/lib/teams/messageExtension/teamsMessageExtensionHandler.ts`, which resolves tenant and linked-user context, accepts `searchRecords` queries from compose/command-box contexts, and returns ticket/task/contact/approval hero-card results through the shared `open_record` Teams action mapping. - (2026-03-07) Completed `T309` through `T320` with `server/src/app/api/teams/message-extension/query/route.test.ts` and `server/src/test/unit/lib/teams/messageExtension/teamsMessageExtensionHandler.test.ts`, covering route delegation, invalid JSON handling, tenant/user-gated search aggregation, permission-based suppression, empty-state recovery, and compose/command-box context enforcement. - (2026-03-07) Completed `F161` by extending `server/src/lib/teams/messageExtension/teamsMessageExtensionHandler.ts` to accept `composeExtension/fetchTask` and `composeExtension/submitAction` requests for `createTicketFromMessage` and `updateFromMessage`, returning Teams-safe task-module and task-message responses from message context. - (2026-03-07) Completed `F162`, `F163`, `F165`, `F166`, `F168`, `F169`, `F171`, `F172`, `F173`, `F174`, `F175`, and `F176` by tightening the message-extension transport around tenant-scoped/user-scoped search execution, compact shared-result card rendering, Teams-tab/full-PSA deep links, inactive-tenant remediation, focused command definitions, and shared `open_record` result mapping. - (2026-03-07) Completed `T321` through `T352` with focused handler/package coverage for message-context action requests, tenant/user resolution, permission-scoped search behavior, compact result cards, deep-link buttons, inactive-tenant remediation, focused command definitions, and shared result mapping. - (2026-03-07) Completed `F164` by teaching `server/src/lib/teams/messageExtension/teamsMessageExtensionHandler.ts` to honor Teams `queryOptions.skip/count` search windows and validate pagination input before returning a paged attachment list. - (2026-03-07) Completed `T327` and `T328` with handler coverage for paged ticket result windows plus recoverable invalid-pagination messaging. - (2026-03-07) Completed `F167` and `F170` by extending message-extension search cards to append tenant-allowed mutation titles from `listAvailableTeamsActions(...)`, so search results surface only currently supported quick actions for the current entity and tenant. - (2026-03-07) Completed `T333`, `T334`, `T339`, and `T340` with handler coverage for available-action summaries plus suppression of unavailable tenant-disabled actions. - (2026-03-07) Completed `F177` and `F178` by asserting message-extension search continues to invoke the shared Teams action layer with `surface: 'message_extension'` while reusing existing PSA ticket, contact, task, and approval access paths instead of introducing a Teams-only index. - (2026-03-07) Completed `T353`, `T354`, `T355`, and `T356` with handler coverage for shared invoking-surface metadata plus explicit assertions against `TicketService.search`, `ContactService.search`, tenant task queries, and `listPendingApprovalsForTeams(...)`. - (2026-03-07) Completed `F179`, `F180`, and `F181` by extending `server/src/lib/teams/messageExtension/teamsMessageExtensionHandler.ts` so `createTicketFromMessage` now returns a real adaptive-card task form with captured message subject/body context, tenant-backed board/status/client/contact choices, and default-backed ticket field handling for inline ticket creation. - (2026-03-07) Completed `F182`, `F183`, and `F184` by executing inline ticket creation through the shared ticket model, storing Teams source-message/idempotency metadata in `tickets.attributes`, replaying duplicate submits safely by that idempotency key, and reusing shared `open_record` result links for post-create Teams-tab/full-PSA confirmation. - (2026-03-07) Completed `T357` through `T368` with handler coverage for create-ticket task-card rendering, permission/setup guards, inline ticket creation, source-metadata persistence, confirmation deep links, invalid input recovery, and duplicate-submit replay. - (2026-03-07) Completed `F185`, `F186`, `F187`, `F188`, and `F189` by extending `server/src/lib/teams/messageExtension/teamsMessageExtensionHandler.ts` so `updateFromMessage` now opens a real adaptive-card task form, accepts ticket/project-task targets, routes ticket note/reply mutations through the shared Teams action registry, and persists Teams message provenance into `comments.metadata`. - (2026-03-07) Completed `F190`, `F191`, `F192`, `F193`, `F194`, and `F195` by returning shared Teams-tab/full-PSA confirmation links after successful update actions, preserving permission/setup failures as recoverable task responses, prefilling update content from the selected Teams message, and offering explicit Teams-tab handoff paths for richer create/update workflows. - (2026-03-07) Completed `T369` through `T390` with message-extension handler coverage for update task-card rendering, ticket/task targeting, internal-note and customer-reply execution, provenance metadata flow, recoverable validation/permission failures, and richer Teams-tab handoff behavior. - (2026-03-07) Verified Teams message-to-update task-card rendering, shared ticket mutations, provenance metadata flow, and Teams-tab handoff behavior with: - `cd server && npx vitest run --config vitest.config.ts src/test/unit/lib/teams/messageExtension/teamsMessageExtensionHandler.test.ts` - `cd server && npx vitest run --config vitest.config.ts src/test/unit/lib/teams/actions/teamsActionRegistry.test.ts` - `pnpm --dir server exec tsc -p tsconfig.json --noEmit --pretty false` - (2026-03-07) Completed `F196` by moving both message-driven submit paths onto dedicated shared registry actions in `server/src/lib/teams/actions/teamsActionRegistry.ts`, leaving the message-extension handler responsible only for task-module UI, field prefilling, and final task-response rendering. - (2026-03-07) Completed `T391` and `T392` with handler coverage proving `createTicketFromMessage` and `updateFromMessage` submits delegate to the shared action layer and still surface recoverable Teams-safe failures when the shared action rejects the request. - (2026-03-07) Verified shared message-driven action delegation with: - `cd server && npx vitest run --config vitest.config.ts src/test/unit/lib/teams/messageExtension/teamsMessageExtensionHandler.test.ts src/test/unit/lib/teams/actions/teamsActionRegistry.test.ts` - `pnpm --dir server exec tsc -p tsconfig.json --noEmit --pretty false` - (2026-03-07) The shared quick-action slice can stay transport-thin: `server/src/lib/teams/quickActions/teamsQuickActionHandler.ts` owns adaptive-card/task-module rendering and submit handling, while the route only parses JSON and delegates into the shared Teams action registry plus `open_record` handoff links. - (2026-03-07) Completed `F197` through `F210` by adding `server/src/lib/teams/quickActions/teamsQuickActionHandler.ts` and `server/src/app/api/teams/quick-actions/route.ts`, which render minimal adaptive-card/task-module forms for assign-ticket, add-note, reply-to-contact, log-time, and approval-response flows; prefill from Teams message/entity context; validate required inputs before submit; execute the shared Teams action layer on submit; return Teams-safe success/error/cancel responses; suppress unavailable actions centrally; and hand users off to the Teams tab when the richer record workflow is the safer path. - (2026-03-07) Completed `T393` through `T420` with `server/src/app/api/teams/quick-actions/route.test.ts` and `server/src/test/unit/lib/teams/quickActions/teamsQuickActionHandler.test.ts`, covering route delegation, invalid JSON handling, per-action form rendering, contextual prefills, tenant/user/action gating, shared-action submission, success and recoverable failure responses, cancel/dismiss behavior, allowed-action visibility, and Teams-tab handoff for unavailable or too-complex quick actions. - (2026-03-07) Verified Teams quick-action adaptive-card flows with: - `cd server && npx vitest run --config vitest.config.ts src/test/unit/lib/teams/quickActions/teamsQuickActionHandler.test.ts src/app/api/teams/quick-actions/route.test.ts` - `pnpm --dir server exec tsc -p tsconfig.json --noEmit --pretty false` - (2026-03-07) Verified Teams notification delivery channel, Graph payload mapping, and manifest permission handoff with: - `cd server && npx vitest run --config vitest.config.ts src/test/unit/internal-notifications/teamsNotificationDelivery.test.ts src/test/unit/internal-notifications/internalNotificationBroadcaster.test.ts ../packages/integrations/src/actions/integrations/teamsPackageActions.test.ts` - `pnpm --dir packages/notifications typecheck` - `pnpm --dir server exec tsc -p tsconfig.json --noEmit --pretty false` - (2026-03-07) Next unchecked feature after this slice is `F231` (Permissions: only authorized tenant admins can manage Microsoft profiles and Teams setup). - (2026-03-07) Completed `F211` through `F230` by adding `packages/notifications/src/realtime/teamsNotificationDelivery.ts` and refactoring `packages/notifications/src/realtime/internalNotificationBroadcaster.ts` so every internal notification can attempt an independent Teams activity-feed delivery path alongside Redis in-app delivery, gated by active Teams setup, personal-notification capability/category settings, linked Microsoft accounts, and tenant-selected Teams profile credentials. - (2026-03-07) Completed `T421` through `T460` with `server/src/test/unit/internal-notifications/teamsNotificationDelivery.test.ts`, `server/src/test/unit/internal-notifications/internalNotificationBroadcaster.test.ts`, and updated `packages/integrations/src/actions/integrations/teamsPackageActions.test.ts`, covering category classification, Teams deep-link reuse, recipient linkage, sent/delivered/failed workflow events, safe suppression on disabled/missing prerequisites, channel independence from Redis delivery, and the required Teams manifest permission for activity-feed sends. - (2026-03-07) Archiving a Microsoft profile only checked the default-profile compatibility guard. It did not yet block the case where Teams explicitly selected a non-default profile, and there was no backend delete action for applying the same guard to deletions. - (2026-03-07) Completed `F231` through `F248` by adding binding-aware Microsoft profile lifecycle guards in `packages/integrations/src/actions/integrations/microsoftActions.ts`, exporting a new `deleteMicrosoftProfile` action, and re-verifying the existing Teams surface authorization/fallback behavior that was already present across Teams tab, bot, message extension, quick-action, and profile-compatibility code. - (2026-03-07) Completed `T461` through `T496` with updated integrations action tests plus re-verification of the existing Teams surface suites, covering tenant-admin gating for Microsoft/Teams settings, cross-surface authorization enforcement, mutation denial for read-only users, tenant-scoped search/lookup behavior, legacy Microsoft compatibility APIs/bindings, Teams-profile rebind safety, Teams-selected profile archive/delete guards, client-user rejection, guided not-configured remediation, inactive-integration action suppression, and tenant allowed-action enforcement across bot and message extension. - (2026-03-07) Verified Teams permission/compatibility/fallback regression coverage with: - `cd server && npx vitest run --config vitest.config.ts ../packages/integrations/src/actions/integrations/microsoftActions.test.ts ../packages/integrations/src/actions/integrations/microsoftConsumerBindings.test.ts ../packages/integrations/src/actions/integrations/teamsActions.test.ts src/test/unit/lib/teams/actions/teamsActionRegistry.test.ts src/test/unit/lib/teams/resolveTeamsTabAccessState.test.ts src/test/unit/lib/teams/resolveTeamsTabAuthState.test.ts src/test/unit/lib/teams/bot/teamsBotHandler.test.ts src/test/unit/lib/teams/messageExtension/teamsMessageExtensionHandler.test.ts src/test/unit/lib/teams/quickActions/teamsQuickActionHandler.test.ts` - `pnpm --dir server exec tsc -p tsconfig.json --noEmit --pretty false` - `pnpm --dir packages/integrations typecheck` - (2026-03-07) Completed `T497` through `T516` by re-verifying the existing focused suites that already exercise the end-to-end acceptance paths for migration compatibility, Teams package identity, SSO tab entry, notification deep-link routing, bot lookup/mutation/approval commands, message-extension search and message-driven actions, quick-action submit flows, Teams notification delivery/suppression, and cross-surface unauthorized/client-user rejection. - (2026-03-07) Verified Teams acceptance-scenario coverage with: - `cd server && npx vitest run --config vitest.config.ts ../packages/integrations/src/actions/integrations/microsoftActions.test.ts ../packages/integrations/src/actions/integrations/microsoftConsumerBindings.test.ts ../packages/integrations/src/actions/integrations/teamsActions.test.ts ../packages/integrations/src/actions/integrations/teamsPackageActions.test.ts src/test/unit/lib/teams/resolveTeamsTabAuthState.test.ts src/test/unit/lib/teams/resolveTeamsTabAccessState.test.ts src/test/unit/lib/teams/resolveTeamsTabDestination.test.ts src/test/unit/app/teams/tab/page.test.tsx src/test/unit/lib/teams/actions/teamsActionRegistry.test.ts src/test/unit/lib/teams/bot/teamsBotHandler.test.ts src/app/api/teams/bot/messages/route.test.ts src/test/unit/lib/teams/messageExtension/teamsMessageExtensionHandler.test.ts src/app/api/teams/message-extension/query/route.test.ts src/test/unit/lib/teams/quickActions/teamsQuickActionHandler.test.ts src/app/api/teams/quick-actions/route.test.ts src/test/unit/internal-notifications/teamsNotificationDelivery.test.ts src/test/unit/internal-notifications/internalNotificationBroadcaster.test.ts` - `pnpm --dir server exec tsc -p tsconfig.json --noEmit --pretty false` - `pnpm --dir packages/integrations typecheck` - `pnpm --dir packages/notifications typecheck` - (2026-03-07) No unchecked feature or test items remain in the Microsoft Teams Integration V1 plan.