# 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`.