## 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
134 lines
6.1 KiB
Markdown
134 lines
6.1 KiB
Markdown
# Data Model: Workspace-first Managed Environment Core Cutover
|
|
|
|
**Date**: 2026-05-06
|
|
**Branch**: `279-workspace-managed-environment-core`
|
|
|
|
## Overview
|
|
|
|
This slice introduces one new managed-target root record and replaces the current tenant-core anchor. `Workspace` remains the primary SaaS context. `ManagedEnvironment` becomes the environment-scoped root record under a workspace. Current environment-scoped records retarget from `tenant_id` to `managed_environment_id` in core-owned paths only, with no compatibility columns.
|
|
|
|
## New Persisted Truth
|
|
|
|
### 1. Managed Environment
|
|
|
|
**Persistence**: new `managed_environments` table
|
|
**Ownership**: managed-target root scoped to one workspace
|
|
**Lifecycle**: current-release core entity
|
|
|
|
| Field | Type | Nullable | Notes |
|
|
|---|---|---|---|
|
|
| `id` | bigint | no | Internal primary key |
|
|
| `workspace_id` | bigint | no | Required parent workspace |
|
|
| `slug` | string | no | Neutral route key inside the workspace |
|
|
| `name` | string | no | Internal canonical name |
|
|
| `display_name` | string | yes | Operator-facing label when it differs from `name` |
|
|
| `kind` | string | no | Bounded current-release category for the managed target |
|
|
| `lifecycle_status` | string | no | Selectability and lifecycle posture for current context |
|
|
| `metadata` | jsonb | yes | Provider-neutral environment metadata only |
|
|
| `created_at` | timestamp | yes | Standard timestamp |
|
|
| `updated_at` | timestamp | yes | Standard timestamp |
|
|
|
|
**Rules**:
|
|
|
|
- `ManagedEnvironment` must not store Microsoft-specific identity, Graph, or Intune fields.
|
|
- `metadata` may store only neutral presentation or lifecycle hints such as internal labels, neutral tags, or operator notes; it must not store provider tenant IDs, domains, Graph scopes, consent identifiers, or raw provider profiles.
|
|
- The route key should be neutral and operator-safe.
|
|
- Archived or inactive environments are not selectable current context by default.
|
|
|
|
### 2. Managed Environment Membership
|
|
|
|
**Persistence**: renamed or replaced environment-membership table
|
|
**Ownership**: environment-scoped access truth
|
|
|
|
| Field | Type | Nullable | Notes |
|
|
|---|---|---|---|
|
|
| `id` | bigint | no | Internal primary key |
|
|
| `user_id` | bigint | no | Member actor |
|
|
| `managed_environment_id` | bigint | no | Environment scope |
|
|
| `role` | string | no | Existing role semantics preserved in this slice |
|
|
| `source` | string | yes | Existing source semantics preserved where still needed |
|
|
| `source_ref` | string | yes | Existing external source reference if still used |
|
|
| `created_by_user_id` | bigint | yes | Existing audit-facing author field if still used |
|
|
| `created_at` | timestamp | yes | Standard timestamp |
|
|
| `updated_at` | timestamp | yes | Standard timestamp |
|
|
|
|
**Rules**:
|
|
|
|
- This slice preserves current membership semantics; it only changes the scoped target noun and foreign key.
|
|
- Workspace membership remains the first access boundary.
|
|
|
|
## Retargeted Existing Truth
|
|
|
|
### 3. Workspace Relation
|
|
|
|
`Workspace` changes from `hasMany(Tenant::class)` to `hasMany(ManagedEnvironment::class)`.
|
|
|
|
### 4. Environment-scoped Foreign-Key Contract
|
|
|
|
Current environment-scoped records move from `tenant_id` to `managed_environment_id` in core-owned paths only.
|
|
|
|
This package intentionally treats the precise table list as implementation inventory driven by current repo truth through `TenantOwnedModelFamilies` plus schema grep. The retargeting set is limited to current core-owned model families that use `tenant_id` as the active managed-target key in:
|
|
|
|
- current root-selection, membership, and current-context families
|
|
- current panel/query/helper seams that need the new managed-target key to resolve scope honestly
|
|
- current commands, fixtures, and tests that resolve active target scope directly through `tenant_id`
|
|
|
|
**Rules**:
|
|
|
|
- no dual columns
|
|
- no `tenant_id` plus `managed_environment_id` overlap
|
|
- no alias relationships that keep both nouns active
|
|
|
|
## Derived Runtime Contracts
|
|
|
|
### 5. Current Context Contract
|
|
|
|
**Persistence**: existing context/session helpers, retargeted
|
|
**Owner**: workspace + managed-environment selection flow
|
|
|
|
| Field | Type | Required | Notes |
|
|
|---|---|---|---|
|
|
| `current_workspace_id` | int | yes | Existing primary SaaS context |
|
|
| `current_managed_environment_id` | int | yes | Replaces current tenant-scoped active target |
|
|
| `current_managed_environment_slug` | string | yes | Route-safe current target key |
|
|
|
|
**Rules**:
|
|
|
|
- The app must not keep an active current-tenant contract in parallel.
|
|
- Existing chooser and panel-shell flows resolve through workspace plus managed environment together.
|
|
|
|
### 6. Temporary Environment Shell Route Contract
|
|
|
|
**Persistence**: none, route-model binding only
|
|
**Owner**: current environment-scoped panel shell
|
|
|
|
| Field | Type | Required | Notes |
|
|
|---|---|---|---|
|
|
| `path_family` | string | yes | Temporarily `/admin/t/{environment}` |
|
|
| `bound_model` | string | yes | `ManagedEnvironment` |
|
|
| `exception_owner` | string | yes | Spec `279`, closed by Spec `280` |
|
|
|
|
**Rules**:
|
|
|
|
- this is the only allowed public-path exception in this slice
|
|
- the path family must not coexist with a second compatibility route family
|
|
- the route parameter meaning changes from tenant to managed environment immediately
|
|
|
|
## Boundaries Explicitly Preserved
|
|
|
|
- `Workspace` remains the primary SaaS context.
|
|
- Provider-specific identity remains outside `ManagedEnvironment`.
|
|
- Provider-connection extraction and broader governance-artifact retargeting remain follow-up work for Specs `281` and `282`.
|
|
- Broader workspace-first route-family and breadcrumb rewrite remain Spec `280`.
|
|
- Provider-profile extraction remains Spec `281`.
|
|
- Artifact retargeting polish remains Spec `282`.
|
|
- RBAC redesign remains Spec `285`.
|
|
- Copy/localization neutralization remains Spec `286`.
|
|
|
|
## Cutover Invariants
|
|
|
|
- No active `App\Models\Tenant` remains in core-owned paths after implementation.
|
|
- No `->tenant(Tenant::class)` Filament tenant binding remains.
|
|
- No `tenant_id` compatibility columns remain in the cutover set.
|
|
- No Microsoft-specific identity fields land on `ManagedEnvironment`.
|
|
- The only temporary exception is the public `/admin/t/{environment}` path family until Spec `280`. |