Preconditions
User is authenticated and belongs to at least two teams with different tenant ids.
Happy path
Open the team switcher from the admin shell header.
An autocomplete or keyboard menu lists every team the user can access.
Select a different team.
Unsaved work prompts first; confirmation updates URL state, active tenant context, cached data scope, and visible team name.
Refresh and navigate.
The active team persists across refresh and all subsequent API requests use the selected tenant.
Failure modes
Permission denied at the right boundary
Trigger: user selects a team after membership was revoked.
Resolution: switch request returns 403 and current tenant remains unchanged.
Cross-tenant isolation
Trigger: user guesses a team id outside their memberships.
Resolution: 404 masks team existence.
Soft-delete leaves audit trail
Trigger: team membership is removed while switcher is open.
Resolution: membership is inactive with prior state in audit and cannot be selected.
Archive vs delete distinction
Trigger: a team is archived rather than deleted.
Resolution: archived team is labelled unavailable; deleted team is absent.
Edit lock during concurrent operations
Trigger: switch begins while unsaved admin form is dirty.
Resolution: modal requires discard/save before changing tenant context.
Audit log row on every state change
Trigger: user changes active team.
Resolution: team-switch audit row includes actor, from team, to team, timestamp, and session id.
Two organizers concurrent
Trigger: two sessions for the same user switch teams independently.
Resolution: per-tab URL state decides context; no tab steals another tab's active team.
Undo window for destructive actions
Trigger: switch discards dirty form state.
Resolution: 10 second undo returns to the previous team and restores preserved draft state.
Refresh loses active team
Trigger: user refreshes after switch.
Resolution: URL-state mandate restores the selected team, not the default membership.
Subsequent request uses previous tenant
Trigger: event list fetch after switch carries old tenant header.
Resolution: request is blocked or retried with new tenant context before data renders.
Accidental cross-tenant mutation after switch
Trigger: user switches teams while an old event page is open.
Resolution: stale event mutation returns 404 and UI redirects to selected team's dashboard.
Capability gap: active-team switcher missing
Trigger: matrix marks EF-002 partial.
Resolution: visible gap panel remains until switcher, URL persistence, and audit-per-switch ship.
Stable test attributes
Visibility teeth. Each attribute must be effectively visible when active.
team-selection-switcher | Header | Team picker |
team-selection-menu | Popover | Available teams |
team-selection-option | Menu | Team option |
team-selection-active-label | Header | Active team |
team-selection-unsaved-modal | Modal | Dirty work confirmation |
team-selection-confirm-toast | Toast | Switch confirmation |
team-selection-gap-panel | Page | Partial gap |
team-selection-audit-row | Audit view | Switch audit |
team-selection-undo-toast | Toast | Undo switch discard |
team-selection-error | Menu | Switch error |
Agent test plan
- team-switcher-renders
- team-switch-persists-url
- team-switch-audits
- permission-denied-boundary
- cross-tenant-404
- soft-delete-audit
- archive-delete-distinction
- dirty-edit-lock
- audit-row-every-change
- per-tab-team-context
- destructive-undo-window
- refresh-keeps-active-team
- subsequent-requests-use-new-tenant
- stale-cross-tenant-mutation-blocked
- team-selection-gap-probe
- evaluate-team-selection