12 KiB
Tasks: Workspace Model, Memberships & Managed Tenants (v2)
Input: Design documents from specs/068-workspaces-v2/
Prerequisites: plan.md (required), spec.md (user stories), research.md, data-model.md, contracts/, quickstart.md
Tests: Required (Pest) because this feature changes runtime behavior and authorization semantics.
Phase 1: Setup (Shared Infrastructure)
- T001 Ensure Sail services are running for development (docker-compose.yml)
- T002 Verify admin panel provider registration (bootstrap/providers.php)
- T003 [P] Create new feature test folder structure (tests/Feature/Workspaces/)
Phase 2: Foundational (Blocking Prerequisites)
- T004 Create Workspace migrations (database/migrations/*_create_workspaces_table.php)
- T005 Create WorkspaceMembership migrations (includes unique
(workspace_id, user_id)constraint) (database/migrations/*_create_workspace_memberships_table.php) - T006 Add
users.last_workspace_idmigration (database/migrations/*_add_last_workspace_id_to_users_table.php) - T007 Add
tenants.workspace_idmigration + indexes + global unique entra tenant id constraint (workspace_id migration + existingtenant_idunique constraint) - T008 [P] Create
Workspacemodel + relationships (app/Models/Workspace.php) - T009 [P] Create
WorkspaceMembershipmodel + relationships (app/Models/WorkspaceMembership.php) - T010 [P] Add
workspace()relationship on Tenant model (app/Models/Tenant.php) - T011 Create Workspace role enum (app/Support/Auth/WorkspaceRole.php)
- T012 Extend capability registry with workspace-plane capabilities (app/Support/Auth/Capabilities.php)
- T013 Create workspace role→capability mapping (app/Services/Auth/WorkspaceRoleCapabilityMap.php)
- T014 Implement workspace capability resolver (app/Services/Auth/WorkspaceCapabilityResolver.php)
- T015 [P] Add Workspace + Membership policies for server-side enforcement (app/Policies/WorkspacePolicy.php)
- T016 [P] Add WorkspaceMembership policy + last-owner guard hooks (app/Policies/WorkspaceMembershipPolicy.php)
- T017 Implement workspace-scoped context helper (session + user last_workspace_id) (app/Support/Workspaces/WorkspaceContext.php)
- T018 Implement workspace selection middleware (app/Http/Middleware/EnsureWorkspaceSelected.php)
- T019 Implement workspace membership middleware (deny-as-not-found) (app/Http/Middleware/EnsureWorkspaceMember.php)
- T020 Register middleware in Laravel 12 middleware pipeline (bootstrap/app.php)
- T021 Add workspace resolver helper (slug preferred, id fallback) (app/Support/Workspaces/WorkspaceResolver.php)
Checkpoint: Foundation ready (DB + capability system + middleware hooks exist)
Phase 3: User Story 1 — Choose workspace and get correct scope (Priority: P1) 🎯 MVP
Goal: Users must always operate within an active Workspace context; all workspace-scoped routes are deny-as-not-found for non-members; global search never leaks records.
Independent Test: A user with memberships can select/switch workspaces and only see/search within the current workspace; a non-member gets 404 semantics.
Tests (US1)
- T022 [P] [US1] Add workspace selection routing tests (covers session vs
last_workspace_idprecedence + invalidation) (tests/Feature/Workspaces/WorkspaceSelectionTest.php) - T023 [P] [US1] Add non-member isolation tests for workspace-scoped routes (tests/Feature/Workspaces/WorkspaceIsolationTest.php)
- T024 [P] [US1] Add global search scoping tests (tests/Feature/Workspaces/WorkspaceGlobalSearchTest.php)
Implementation (US1)
- T025 [US1] Create ChooseWorkspace page (admin panel) (app/Filament/Pages/ChooseWorkspace.php)
- T026 [US1] Create NoAccess page (admin panel) (app/Filament/Pages/NoAccess.php)
- T027 [US1] Add routes/entry behavior for
/admin/choose-workspace+/admin/no-access(AdminPanelProvider authenticatedRoutes + EnsureWorkspaceSelected allowlist) - T028 [US1] Implement workspace switcher (user menu) wiring to WorkspaceContext (app/Providers/Filament/AdminPanelProvider.php)
- T029 [US1] Introduce workspace route group
/admin/w/{workspace}with required middleware (routes/web.php) - T030 [US1] Introduce workspace-scoped global search query trait (app/Filament/Concerns/ScopesGlobalSearchToWorkspace.php)
- T031 [US1] Apply workspace global search scoping to workspace-owned resources (app/Filament/Resources/**)
Checkpoint: User Story 1 works and is testable standalone.
Phase 4: User Story 2 — Manage workspace members and roles (Priority: P2)
Goal: Workspace Owner/Manager can add members, change roles, and remove members; last-owner cannot be removed/demoted; all membership mutations are audited.
Independent Test: From Workspace view, manage memberships; verify last-owner guard; verify audit entries.
Tests (US2)
- T032 [P] [US2] Add last-owner guard tests (tests/Feature/Workspaces/LastOwnerGuardTest.php)
- T033 [P] [US2] Add membership mutation authorization tests (403 vs 404 semantics) (tests/Feature/Workspaces/WorkspaceMembershipAuthorizationTest.php)
- T034 [P] [US2] Add audit logging tests for membership mutations (tests/Feature/Workspaces/WorkspaceMembershipAuditTest.php)
Implementation (US2)
- T035 [US2] Add
workspace_idsupport to AuditLog storage (nullabletenant_idwhere required) + indexes (database/migrations/*_add_workspace_id_to_audit_logs_table.php) - T036 [US2] Extend AuditLog model with optional
workspace()relationship (app/Models/AuditLog.php) - T037 [US2] Implement workspace audit logger service writing AuditLog entries + stable action ids (app/Services/Audit/WorkspaceAuditLogger.php)
- T038 [US2] Create WorkspaceResource CRUD with View page (required for global search) (app/Filament/Resources/Workspaces/WorkspaceResource.php)
- T039 [US2] Add WorkspaceResource pages (List/Create/View/Edit) (app/Filament/Resources/Workspaces/Pages/)
- T040 [US2] Implement Members relation manager on WorkspaceResource (app/Filament/Resources/Workspaces/RelationManagers/MembershipsRelationManager.php)
- T041 [US2] Apply UI enforcement (disabled vs hidden + confirmation) for membership actions (app/Support/Rbac/UiEnforcement.php)
- T042 [US2] Enforce last-owner guard server-side in membership mutations (app/Policies/WorkspaceMembershipPolicy.php)
Checkpoint: User Story 2 works and is testable standalone.
Phase 5: User Story 3 — Onboard a managed tenant inside a workspace (Priority: P3)
Goal: Managed Tenant CRUD/listing/onboarding is always workspace-scoped and uses the canonical onboarding entry under workspace.
Independent Test: Add a managed tenant in Workspace A; it never appears in Workspace B; old entry points redirect.
Tests (US3)
- T043 [P] [US3] Add managed tenant scoping tests (tests/Feature/Workspaces/ManagedTenantScopingTest.php)
- T044 [P] [US3] Add legacy entry-point redirect tests (tests/Feature/Workspaces/LegacyOnboardingRedirectTest.php)
Implementation (US3)
- T045 [US3] Update tenant-plane naming/terminology in UI to “Managed Tenant” within workspace context (app/Filament/Resources/TenantResource.php)
- T046 [US3] Scope managed tenant list query to current workspace (app/Filament/Resources/TenantResource/Pages/ListTenants.php)
- T047 [US3] Ensure managed tenant create uses current workspace_id and blocks cross-workspace edits (app/Filament/Resources/TenantResource/Pages/CreateTenant.php)
- T048 [US3] Implement canonical onboarding route under workspace scope (app/Filament/Resources/TenantResource/Pages/OnboardingManagedTenant.php)
- T049 [US3] Redirect legacy
/admin/newentry to choose-workspace or last_workspace onboarding (routes/web.php)
Checkpoint: User Story 3 works and is testable standalone.
Final Phase: Migration, Polish & Cross-Cutting Concerns
- T050 Create migration to backfill Default Workspace + assign existing tenants + bootstrap owner membership (database/migrations/*_backfill_default_workspace_and_memberships.php)
- T051 Add workspace selection invalidation when workspace archived or membership removed (app/Support/Workspaces/WorkspaceContext.php)
- T052 Ensure workspace-scoped navigation labels/IA are consistent (resources/views/** and app/Filament/**)
- T053 Run formatter on touched files using Sail (
bin pint --dirty) (vendor/bin/sail) - T054 Run targeted test suite for this feature using Sail (
artisan test --compact --filter=Workspaces) (vendor/bin/sail) - T055 Validate manual quickstart checklist remains accurate (specs/068-workspaces-v2/quickstart.md)
Additions (Consistency + Constitution Requirements)
- T056 [P] Add deterministic workspace capability mapping golden tests (tests/Feature/Workspaces/WorkspaceCapabilitiesTest.php)
- T057 [P] Add unique workspace membership constraint test (tests/Feature/Workspaces/WorkspaceMembershipUniquenessTest.php)
- T058 [P] Add unique Entra tenant id constraint test (tests/Feature/Workspaces/ManagedTenantUniquenessTest.php)
- T059 [P] Add migration safety test for backfill (SC-005) (tests/Feature/Workspaces/WorkspaceBackfillMigrationTest.php)
- T060 [P] Add workspace lifecycle tests (archive/unarchive + selection invalidation) (tests/Feature/Workspaces/WorkspaceLifecycleTest.php)
- T061 Add
archived_at(or equivalent) to workspaces table + index (database/migrations/*_add_archived_at_to_workspaces_table.php) - T062 Add archive/unarchive actions with confirmation + authorization in WorkspaceResource (app/Filament/Resources/WorkspaceResource.php)
- T063 [US1] Add “Create Workspace” action on ChooseWorkspace/NoAccess pages (supports first workspace) (app/Filament/Pages/ChooseWorkspace.php and app/Filament/Pages/NoAccess.php)
- T064 [P] Add test for creating the first workspace from no-access/choose-workspace flows (tests/Feature/Workspaces/WorkspaceCreationEntryFlowTest.php)
Dependencies & Execution Order
Phase Dependencies
- Phase 1 (Setup) → Phase 2 (Foundational) → Phase 3 (US1) → Phase 4 (US2) → Phase 5 (US3) → Final Phase
User Story Dependencies
- US1 depends on Foundational (workspace context, middleware, capability registry).
- US2 depends on Foundational (workspace models + policies) and benefits from US1 routing/context.
- US3 depends on Foundational (workspace_id on managed tenants) and US1 routing/context.
Parallel Execution Examples
US1
- [P] Write tests in tests/Feature/Workspaces/WorkspaceSelectionTest.php, tests/Feature/Workspaces/WorkspaceIsolationTest.php, tests/Feature/Workspaces/WorkspaceGlobalSearchTest.php
- Implement pages in app/Filament/Pages/ChooseWorkspace.php and app/Filament/Pages/NoAccess.php
US2
- [P] Write tests in tests/Feature/Workspaces/LastOwnerGuardTest.php, tests/Feature/Workspaces/WorkspaceMembershipAuthorizationTest.php, tests/Feature/Workspaces/WorkspaceMembershipAuditTest.php
- Implement audit storage/model updates in app/Models/AuditLog.php + app/Services/Audit/WorkspaceAuditLogger.php
US3
- [P] Write tests in tests/Feature/Workspaces/ManagedTenantScopingTest.php and tests/Feature/Workspaces/LegacyOnboardingRedirectTest.php
- Update tenant resource pages in app/Filament/Resources/TenantResource/Pages/
Implementation Strategy
- MVP scope is US1 only (workspace selection + scoping + search safety + deny-as-not-found isolation).
- Then implement US2 (memberships + auditing + last-owner guard).
- Then implement US3 (managed tenant scoping + canonical onboarding + redirects).