TenantAtlas/specs/068-workspaces-v2/tasks.md
2026-02-01 12:19:57 +01:00

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_id migration (database/migrations/*_add_last_workspace_id_to_users_table.php)
  • T007 Add tenants.workspace_id migration + indexes + global unique entra tenant id constraint (workspace_id migration + existing tenant_id unique constraint)
  • T008 [P] Create Workspace model + relationships (app/Models/Workspace.php)
  • T009 [P] Create WorkspaceMembership model + 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_id precedence + 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_id support to AuditLog storage (nullable tenant_id where 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/new entry 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).