TenantAtlas/specs/279-workspace-managed-environment-core/plan.md
ahmido e64bae9cfc feat: cut over tenant core to managed environments (#335)
## Summary
- replace the legacy Tenant and TenantMembership core models with ManagedEnvironment and ManagedEnvironmentMembership
- propagate the managed environment naming and key changes across Filament resources, pages, controllers, jobs, models, and supporting runtime paths
- add feature 279 spec artifacts and focused managed-environment test coverage for model behavior, route binding, panel context, authorization, and legacy guardrails

## Validation
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/ManagedEnvironment/LegacyTenantCoreGuardTest.php tests/Feature/ManagedEnvironment/ManagedEnvironmentAuthorizationTest.php tests/Feature/ManagedEnvironment/ManagedEnvironmentPanelContextTest.php tests/Feature/ManagedEnvironment/ManagedEnvironmentRouteBindingTest.php tests/Unit/ManagedEnvironment/ManagedEnvironmentContextResolverTest.php tests/Unit/ManagedEnvironment/ManagedEnvironmentModelTest.php`
- `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent`

## Notes
- branch pushed from commit `1123b122`
- browser smoke test file was added but not run in this pass

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #335
2026-05-07 06:38:14 +00:00

21 KiB
Raw Permalink Blame History

Implementation Plan: Workspace-first Managed Environment Core Cutover

Branch: 279-workspace-managed-environment-core | Date: 2026-05-06 | Spec: spec.md Input: Feature specification from specs/279-workspace-managed-environment-core/spec.md

Summary

Prepare the first reserved cutover slice that replaces Tenant as the active managed-target core with ManagedEnvironment. The narrow implementation path is a breaking in-place cutover: introduce the new root entity, retarget current core foreign keys and memberships to managed_environment_id, replace current context seams with ManagedEnvironment, and keep the product bootable by temporarily rebinding the existing /admin/t/{environment} shell to ManagedEnvironment until Spec 280 owns the final Workspace-tenancy and public workspace-first route rewrite.

This plan stays intentionally narrower than the full cutover pack. Filament remains v5 on Livewire v4, provider registration remains unchanged in apps/platform/bootstrap/providers.php, no compatibility layer is allowed, no new asset strategy is expected, and provider-profile extraction, artifact retargeting, RBAC redesign, copy neutralization, and cutover quality-gate automation remain explicit follow-up specs.

Inherited Baseline / Explicit Delta

Inherited baseline

  • Workspace already exists as the SaaS and organization context.
  • Tenant is currently the active managed-target core across models, route binding, memberships, Filament tenancy, current-target selection, and tenant-owned query helpers.
  • TenantPanelProvider currently binds Filament tenancy directly to Tenant::class on the /admin/t path family.
  • Current helper seams such as WorkspaceContext, ResolvesPanelTenantContext, InteractsWithTenantOwnedRecords, ScopesGlobalSearchToTenant, and TenantOwnedModelFamilies all assume Tenant as the active managed target.
  • Current provider and governance tables still use tenant_id in some follow-up lanes, but their semantic retargeting remains outside this package.

Explicit delta in this plan

  • Introduce ManagedEnvironment as the new active managed-target root inside a workspace.
  • Replace active core use of Tenant and tenant_id with ManagedEnvironment and managed_environment_id in core-owned paths.
  • Replace current environment-membership seams and current-target context helpers in place rather than adding adapters.
  • Rebind the current /admin/t/{environment} shell to ManagedEnvironment as an explicit temporary bridge while Spec 280 retains ownership of the final Workspace Filament-tenancy and public route-family rewrite.
  • Keep provider-specific identity off ManagedEnvironment; follow-up Spec 281 owns the remaining provider extraction.

Technical Context

Language/Version: PHP 8.4, Laravel 12
Primary Dependencies: Filament v5, Livewire v4, Pest v4, current workspace and authorization services, current panel providers, current tenant-owned query helpers
Storage: PostgreSQL with destructive pre-production cutover from tenants/tenant_id to managed_environments/managed_environment_id in core-owned paths
Testing: Pest unit, feature, and one browser smoke
Validation Lanes: fast-feedback, confidence, browser
Target Platform: Laravel monolith in apps/platform
Project Type: web application
Performance Goals: keep current environment-scoped flows functionally equivalent after the cutover; no new queue or remote-call path
Constraints: no compatibility shims, no dual columns, no legacy alias model, no new provider-specific fields on ManagedEnvironment, no second route family, and no broadened route/IA rewrite in this slice
Scale/Scope: one breaking core entity replacement, current context and schema retargeting, and the minimum shell/route binding needed to keep the product operable

Likely Affected Repo Surfaces

  • apps/platform/app/Models/Tenant.php, Workspace.php, TenantMembership.php, and any current core-owned model family using tenant_id as the managed-target key.
  • apps/platform/app/Providers/Filament/TenantPanelProvider.php and admin routes/pages that assume Tenant route binding or current target semantics.
  • apps/platform/app/Filament/Concerns/ResolvesPanelTenantContext.php, InteractsWithTenantOwnedRecords.php, and ScopesGlobalSearchToTenant.php.
  • Current chooser/context pages such as ChooseTenant, current tenant dashboard pages, and any context bar partials that name the active target.
  • apps/platform/app/Support/WorkspaceIsolation/TenantOwnedModelFamilies.php as the authoritative inventory anchor for environment-scoped model families.
  • apps/platform/app/Support/OperationRunLinks.php and environment-scoped route builders.
  • Commands, fixtures, factories, and tests that still instantiate Tenant or search on tenant_id as active core truth.

Filament v5 / Panel Notes

  • Livewire v4.0+ compliance: The cutover keeps Filament on Livewire v4 and retargets existing native panel/provider seams only.
  • Provider registration location: Provider registration remains unchanged in apps/platform/bootstrap/providers.php.
  • Global search: Existing global-search resources touched by the cutover must keep safe environment scoping and continue to satisfy the view/edit-page rule where applicable.
  • Destructive actions: No new destructive actions are added. Any touched existing destructive actions must preserve ->requiresConfirmation() and current authorization.
  • Asset strategy: No new asset registration is planned. Deployment remains unchanged.

Managed Environment Cutover Fit

  • Use one breaking in-place cutover rather than a staged alias or compatibility layer.
  • Prefer replacing existing tenant-context helpers and route binding seams in place over adding ManagedEnvironment adapters around Tenant behavior.
  • Treat the current /admin/t/{environment} public path family as a temporary shell-level exception only. The active bound model, route parameter meaning, and current-target naming all change to ManagedEnvironment, while Spec 280 owns the final workspace-first route family and the final Workspace Filament-tenancy end state.
  • Keep ManagedEnvironment provider-neutral. Fields such as Entra tenant IDs, Graph identifiers, domains, or consent details stay in provider-owned seams.

RBAC / Data Ownership / Auditability Fit

  • Workspace remains the primary SaaS context and access boundary.
  • ManagedEnvironment becomes the environment-scoped root entity under a workspace.
  • Existing tenant-membership semantics are retargeted to managed-environment memberships without expanding role or capability scope in this slice.
  • Existing audit and authorization rules remain in force; the cutover changes nouns and keys, not trust boundaries.
  • Any touched audit or log surface must keep environment scope truthful after the key rename.

UI / Surface Guardrail Plan

  • Guardrail scope: changed surfaces
  • Native vs custom classification summary: native Filament
  • Shared-family relevance: context selection, route links, context bars, global-search scoping, current-target summaries
  • State layers in scope: shell, page, detail, URL-query
  • Audience modes in scope: operator-MSP, support-platform
  • Decision/diagnostic/raw hierarchy plan: decision-first on the chooser, diagnostics-second elsewhere, no new raw/debug surface
  • Raw/support gating plan: provider-specific metadata remains off ManagedEnvironment and out of default chooser/shell disclosure
  • One-primary-action / duplicate-truth control: the chooser keeps one dominant Enter environment action; the shell shows current context once and avoids duplicating page-level summaries
  • Handling modes by drift class or surface: review-mandatory with one documented route exception
  • Repository-signal treatment: temporary /admin/t retention is exception-required and must be recorded in the active feature close-out
  • Special surface test profiles: standard-native-filament, global-context-shell, exception-coded-surface
  • Required tests or manual smoke: functional-core, state-contract, manual/browser smoke
  • Exception path and spread control: the /admin/t/{environment} path retention is the only allowed route exception and must not grow a second compatibility family
  • Active feature PR close-out entry: Guardrail

Shared Pattern & System Fit

  • Cross-cutting feature marker: yes
  • Systems touched: panel providers, chooser pages, context helpers, query helpers, route builders, current-target summaries, and environment-bound resources
  • Shared abstractions reused: existing WorkspaceContext, panel providers, capability resolver, current route builders, TenantOwnedModelFamilies
  • New abstraction introduced? why?: none planned; prefer replacing or renaming tenant-specific seams in place
  • Why the existing abstraction was sufficient or insufficient: the seams are correct cutover anchors but are currently encoded with the wrong core noun
  • Bounded deviation / spread control: one route-path exception plus one temporary shell-binding bridge only, both owned by this feature and closed by Spec 280

OperationRun UX Impact

  • Touches OperationRun start/completion/link UX?: yes, existing deep-link resolution only
  • Central contract reused: OperationRunLinks
  • Delegated UX behaviors: environment-aware URL resolution only
  • Surface-owned behavior kept local: none
  • Queued DB-notification policy: N/A
  • Terminal notification path: N/A
  • Exception path: same temporary /admin/t/{environment} shell retention described above

Provider Boundary & Portability Fit

  • Shared provider/platform boundary touched?: yes
  • Provider-owned seams: current provider identity, Graph consent, provider metadata, Microsoft-specific identifiers
  • Platform-core seams: managed-target root entity, workspace-to-environment relationship, route binding, current-target context, operator vocabulary
  • Neutral platform terms / contracts preserved: workspace, managed environment, provider connection, target scope
  • Retained provider-specific semantics and why: provider-specific identity remains where it already belongs until Spec 281 extracts it cleanly
  • Bounded extraction or follow-up path: Specs 281, 284, and 286

Constitution Check

GATE: Must pass before implementation begins and again after the design artifacts are complete.

  • Inventory-first / snapshot truth: PASS. The cutover changes the managed-target root, not inventory or snapshot semantics.
  • Read/write separation: PASS. No new operator write workflow or remote call path is introduced.
  • Graph contract path: PASS. No new Graph call or contract registry change is required in this slice.
  • Deterministic capabilities: PASS. Current capability semantics remain and are retargeted to managed-environment membership.
  • Workspace and tenant isolation: EXCEPTION-REQUIRED. Workspace remains the primary boundary and managed-environment membership remains the scoped target boundary, but SCOPE-001 still hardcodes tenant_id as the required managed-target key until the constitution is amended or an explicit approved exception is recorded for this cutover inventory.
  • RBAC-UX plane separation: PASS. /admin and /system remain separate.
  • Destructive action discipline: PASS by non-expansion. Existing destructive actions keep current confirmation requirements.
  • Global search safety: PASS if all touched resources keep environment-safe scoping.
  • OperationRun / Ops-UX: PASS. Only deep-link resolution changes.
  • Data minimization: PASS. Provider-specific identity stays out of ManagedEnvironment.
  • Test governance: PASS. Planned proof is bounded to unit, feature, browser, and guard checks.
  • Proportionality / no premature abstraction: PASS. The cutover replaces a wrong root concept instead of layering a framework.
  • Persisted truth: PASS. New persistence is justified because the managed-target root has independent product truth.
  • Behavioral state: PASS. kind and lifecycle posture exist only to support current target selection and filtering, not a new semantic framework.
  • UI semantics / shared pattern first / Filament-native UI: PASS. Native panel/provider seams remain the first path.
  • Provider boundary: PASS. Neutral core noun is introduced explicitly.

Gate evaluation: PASS with approved feature-local exception in place.

Post-design re-check: PASS while research.md, data-model.md, quickstart.md, contracts/managed-environment-core-cutover.logical.openapi.yaml, checklists/requirements.md, checklists/constitution-scope-001-exception.md, and tasks.md remain aligned and the approved exception record stays valid.

Implementation gate reference: specs/279-workspace-managed-environment-core/checklists/constitution-scope-001-exception.md is the current approved-exception artifact for this package. Phase 2 may not begin unless that path remains valid or is replaced by the actual constitution-amendment reference.

Test Governance Check

  • Test purpose / classification by changed surface: Unit, Feature, Browser
  • Affected validation lanes: fast-feedback, confidence, browser
  • Why this lane mix is the narrowest sufficient proof: the cutover changes core identity, panel binding, and shell context. Unit tests prove model/context rules, feature tests prove authorization and route binding, and one browser smoke proves the end-to-end chooser/shell path remains operable.
  • Narrowest proving command(s):
    • export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && REPO_ROOT="$(git rev-parse --show-toplevel)" && (cd "$REPO_ROOT/apps/platform" && ./vendor/bin/sail artisan test --compact tests/Unit/ManagedEnvironment/ManagedEnvironmentModelTest.php tests/Unit/ManagedEnvironment/ManagedEnvironmentContextResolverTest.php)
    • export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && REPO_ROOT="$(git rev-parse --show-toplevel)" && (cd "$REPO_ROOT/apps/platform" && ./vendor/bin/sail artisan test --compact tests/Feature/ManagedEnvironment/ManagedEnvironmentRouteBindingTest.php tests/Feature/ManagedEnvironment/ManagedEnvironmentAuthorizationTest.php tests/Feature/ManagedEnvironment/ManagedEnvironmentPanelContextTest.php tests/Feature/ManagedEnvironment/LegacyTenantCoreGuardTest.php)
    • export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && REPO_ROOT="$(git rev-parse --show-toplevel)" && (cd "$REPO_ROOT/apps/platform" && ./vendor/bin/sail artisan test --compact tests/Browser/Spec279ManagedEnvironmentCoreCutoverSmokeTest.php)
    • export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && REPO_ROOT="$(git rev-parse --show-toplevel)" && (cd "$REPO_ROOT/apps/platform" && ./vendor/bin/sail bin pint --dirty --format agent)
    • export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && REPO_ROOT="$(git rev-parse --show-toplevel)" && rg -n --fixed-strings 'App\Models\Tenant' "$REPO_ROOT/apps/platform/app" "$REPO_ROOT/apps/platform/tests" "$REPO_ROOT/apps/platform/database"
    • export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && REPO_ROOT="$(git rev-parse --show-toplevel)" && rg -n -- '->tenant\(Tenant::class' "$REPO_ROOT/apps/platform/app" "$REPO_ROOT/apps/platform/tests" "$REPO_ROOT/apps/platform/database"
  • Fixture / helper / factory / seed / context cost risks: moderate because current tenant fixtures must be replaced, not wrapped
  • Expensive defaults or shared helper growth introduced?: no; keep helper replacement explicit and local
  • Heavy-family additions, promotions, or visibility changes: none
  • Surface-class relief / special coverage rule: one browser smoke required because the shell/context flow changes end-to-end
  • Closing validation and reviewer handoff: rerun the exact commands above, confirm the temporary route exception is documented and bounded, confirm LegacyTenantCoreGuardTest.php proves tenant_id removal only inside the declared core-owned cutover inventory with explicit Spec 281 and 282 exclusions, confirm no provider-specific fields land on ManagedEnvironment, confirm any touched globally searchable resource still satisfies Filaments edit/view eligibility rule or remains out of global search, confirm no new asset registration or deployment-step change was introduced, and confirm provider registration remains unchanged
  • Budget / baseline / trend follow-up: contained feature-local increase only
  • Review-stop questions: did a compatibility layer appear, did provider-specific identity leak onto ManagedEnvironment, did the route exception spread, did any core-owned Tenant references survive, did any unapproved tenant_id usage remain inside the declared core-owned cutover inventory, and did any touched destructive action lose ->requiresConfirmation() or current authorization
  • Escalation path: document-in-feature for the temporary route exception; follow-up-spec already assigned for remaining pack work
  • Active feature PR close-out entry: Guardrail
  • Why no dedicated follow-up spec is needed: this package already owns the first cutover slice; the remaining strategic steps are already reserved as Specs 280-287

Review Checklist Status

  • Review checklist artifact: checklists/requirements.md
  • Review outcome class: documentation-required-exception
  • Workflow outcome: keep
  • Test-governance outcome: keep
  • Escalation rule: if implementation adds compatibility layers, a second route family, provider-specific fields on ManagedEnvironment, or absorbs Spec 280-287 scope, flip the workflow outcome to split or reject-or-split

Rollout Considerations

  • Land the schema/entity cutover and current-context replacement before broad page/UI cleanup.
  • Keep the temporary /admin/t/{environment} path exception explicit and minimal.
  • Replace fixtures, factories, and guard tests in the same cutover PR so mixed core nouns do not linger.
  • Use the feature-local guard tests and grep checks to keep Tenant from re-entering core-owned paths while the rest of the pack is still pending.

Risk Controls

  • Reject any implementation that introduces an alias Tenant model, dual columns, or compatibility writes.
  • Reject any implementation that moves provider-specific identity onto ManagedEnvironment.
  • Reject any implementation that keeps both /admin/t/{environment} and a new workspace-first route family active in the same slice.
  • Reject any implementation that absorbs the broader Spec 280 route/IA rewrite, Spec 281 provider extraction, or Spec 285 RBAC redesign.

Research & Design Outputs

  • research.md records the no-compatibility cutover, temporary route-path exception, provider-boundary discipline, helper-replacement strategy, and proof strategy.
  • data-model.md captures the ManagedEnvironment entity, membership retargeting, current-context model, and foreign-key cutover rules.
  • quickstart.md provides the bounded reviewer flow and exact validation commands.
  • contracts/managed-environment-core-cutover.logical.openapi.yaml captures the logical chooser and temporary environment-shell route contract.
  • checklists/requirements.md records the review outcome, workflow outcome, and test-governance outcome.
  • tasks.md sequences the cutover work and keeps the remaining pack scope deferred.

Project Structure

Documentation (this feature)

specs/279-workspace-managed-environment-core/
├── checklists/
│   ├── constitution-scope-001-exception.md
│   └── requirements.md
├── contracts/
│   └── managed-environment-core-cutover.logical.openapi.yaml
├── data-model.md
├── plan.md
├── quickstart.md
├── research.md
├── spec.md
└── tasks.md

Source Code (expected implementation surfaces)

apps/platform/
├── app/
│   ├── Filament/
│   │   ├── Concerns/
│   │   │   ├── InteractsWithTenantOwnedRecords.php
│   │   │   ├── ResolvesPanelTenantContext.php
│   │   │   └── ScopesGlobalSearchToTenant.php
│   │   ├── Pages/
│   │   │   └── ChooseTenant.php
│   │   └── Resources/
│   │       └── TenantResource.php
│   ├── Models/
│   │   ├── Workspace.php
│   │   ├── Tenant.php
│   │   └── ProviderConnection.php
│   ├── Providers/Filament/
│   │   ├── AdminPanelProvider.php
│   │   └── TenantPanelProvider.php
│   ├── Support/
│   │   ├── OperationRunLinks.php
│   │   ├── WorkspaceIsolation/
│   │   │   └── TenantOwnedModelFamilies.php
│   │   └── Workspaces/
│   │       └── WorkspaceContext.php
│   └── Services/Auth/
│       └── WorkspaceCapabilityResolver.php
├── database/
│   ├── factories/
│   └── migrations/
└── tests/
    ├── Browser/
    ├── Feature/ManagedEnvironment/
    └── Unit/ManagedEnvironment/

Structure decision: keep the documentation package self-contained under specs/279-workspace-managed-environment-core/; implementation later replaces the core managed-target seams in apps/platform/ directly instead of introducing a separate cutover package or adapter layer.