# Feature Specification: Workspace-first Managed Environment Core Cutover **Feature Branch**: `279-workspace-managed-environment-core` **Created**: 2026-05-06 **Status**: Ready with approved feature-local exception **Input**: User description: "Use the next unspecced reserved cutover slot from the roadmap/spec-candidate pack and prepare the `Workspace-first Managed Environment Core Cutover` package as the next bounded implementation-ready spec." ## Spec Candidate Check - **Problem**: TenantPilot still treats `Tenant` as the active managed-target core across models, route binding, Filament tenancy, memberships, query helpers, and operator context. That blocks the planned workspace-first / provider-neutral evolution because the platform core still assumes the managed target is a Microsoft-shaped tenant instead of a generic managed environment inside a workspace. - **Today's failure**: Current repo seams such as `TenantPanelProvider`, `Tenant`, `ProviderConnection::tenant()`, tenant-owned query helpers, `Filament::getTenant()` usage, and many `tenant_id` foreign keys force every new workspace-first or provider-neutral change to either deepen `Tenant` coupling or add compatibility shims the constitution explicitly rejects. - **User-visible improvement**: Operators and future features get one consistent managed-target identity inside a workspace. Context selection, authorization boundaries, route binding, and current environment-scoped surfaces stop depending on `Tenant` as platform-core truth. - **Smallest enterprise-capable version**: Replace `Tenant` as the active managed-target core with `ManagedEnvironment`, retarget current core foreign-key anchors and context helpers to `managed_environment_id`, keep the existing panel bootable by temporarily rebinding the current `/admin/t/{environment}` path family to `ManagedEnvironment`, and defer the public workspace-first route/IA rewrite, provider-profile extraction, artifact retargeting refinements, and RBAC scope redesign to follow-up specs `280`-`287`. - **Explicit non-goals**: No dual-read or dual-write compatibility layer, no legacy `Tenant` alias model, no production-data backfill strategy, no full workspace-first public route-family rewrite, no provider-profile extraction, no artifact-language rewrite, no package engine, no guided-operations layer, and no speculative multi-provider framework. - **Permanent complexity imported**: One new managed-target root entity, one breaking schema cutover from `tenant_id` to `managed_environment_id`, one renamed environment-membership seam, existing context-helper replacement, and focused unit/feature/browser plus guard-test coverage. - **Why now**: `279` is the first unspecced reserved slot in the roadmap-backed cutover pack, and every later cutover spec (`280`-`287`) depends on this core replacement. Waiting adds more `Tenant`- and Microsoft-specific coupling to the platform core. - **Why not local**: The problem spans schema, Eloquent relations, route binding, Filament tenancy, current-context resolution, authorization helpers, commands, and tests. A local rename or adapter would immediately drift. - **Approval class**: Core Enterprise - **Red flags triggered**: New persisted truth, new state family, many touched seams, and a foundation-sounding cutover. Defense: this is current-release architecture truth, not speculative extensibility; it explicitly forbids compatibility shims and defers broader route, copy, provider, and artifact follow-through to the next reserved specs. - **Score**: Nutzen: 2 | Dringlichkeit: 2 | Scope: 1 | Komplexitaet: 1 | Produktnaehe: 1 | Wiederverwendung: 2 | **Gesamt: 9/12** - **Decision**: approve ## Spec Scope Fields - **Scope**: workspace - **Primary Routes**: - existing workspace chooser route under `/admin` - existing tenant chooser surface, retargeted to managed-environment selection - current environment-scoped panel routes under `/admin/t/{environment}` as a temporary bounded exception until Spec `280` replaces the public path family - current admin resource/detail routes that bind the managed target model directly or derive environment context from it - **Data Ownership**: - `Workspace` remains the primary SaaS and organization context - `ManagedEnvironment` becomes the new managed-target root record inside a workspace - current core-owned managed-target anchors and context-owned relations retarget from `tenant_id` to `managed_environment_id`; provider-connection extraction and broader governance-artifact retargeting remain follow-up work for Specs `281` and `282` - **RBAC**: - workspace membership remains the first isolation boundary - current tenant-membership semantics are retargeted to managed-environment membership semantics without widening role scope in this spec - wrong-workspace and non-member access remain `404` - in-scope actors missing a capability remain `403` Canonical-view handling for current admin surfaces: - **Default filter behavior when tenant-context is active**: current canonical admin views continue to prefilter to the active managed environment whenever they currently prefilter to the active tenant; broader workspace-first canonical-view semantics remain Spec `280` follow-up work - **Explicit entitlement checks preventing cross-tenant leakage**: any route, query helper, or global-search path that currently resolves tenant scope must resolve the active managed environment plus current workspace membership before revealing environment-bound records ## Cross-Cutting / Shared Pattern Reuse - **Cross-cutting feature?**: yes - **Interaction class(es)**: context selection, route links, global-search scoping, current-target chips, resource query helpers, and environment-bound deep links - **Systems touched**: `TenantPanelProvider`, `WorkspaceContext`, `ChooseTenant`, `ResolvesPanelTenantContext`, `InteractsWithTenantOwnedRecords`, `ScopesGlobalSearchToTenant`, `TenantOwnedModelFamilies`, `OperationRunLinks`, current tenant widgets, and tenant-scoped resources/pages - **Existing pattern(s) to extend**: workspace-first context storage, current Filament panel-provider seams, current deny-as-not-found membership enforcement, and existing environment-bound route builders - **Shared contract / presenter / builder / renderer to reuse**: existing `WorkspaceContext`, panel-provider registration, current resource query scoping helpers, `OperationRunLinks`, and the existing workspace capability resolver - **Why the existing shared path is sufficient or insufficient**: those seams are sufficient as cutover points, but insufficient because they still encode `Tenant` as the platform-core managed target - **Exception type**: `Cross-panel Canonical Route Exception` - **Allowed deviation and why**: one bounded deviation is allowed: the current `/admin/t/{environment}` shell may remain temporarily while binding `ManagedEnvironment` instead of `Tenant`, even though the source-pack end state moves Filament tenancy to `Workspace` in Spec `280`. This temporary bridge exists only to keep the runtime operable while `Tenant` is removed from the core. No second compatibility route family is allowed. - **Reason block**: only one canonical surface makes sense during the cutover. Keeping the existing shell avoids a second public route family, keeps shell transition explicit, preserves truthful scope signals, and leaves final canonical Workspace tenancy to Spec `280`. - **Dedicated proof**: `apps/platform/tests/Feature/ManagedEnvironment/ManagedEnvironmentPanelContextTest.php`, `apps/platform/tests/Browser/Spec279ManagedEnvironmentCoreCutoverSmokeTest.php`, and `specs/279-workspace-managed-environment-core/checklists/constitution-scope-001-exception.md` - **Consistency impact**: route keys, chooser labels, context bars, global-search scope resolution, tenant-owned query helpers, and deep-link builders must all agree on `ManagedEnvironment` as the active managed target - **Review focus**: reviewers must verify that the cutover replaces current shared seams in place, that the temporary `/admin/t` path is the only documented exception, and that no alias model or second context stack appears ## OperationRun UX Impact - **Touches OperationRun start/completion/link UX?**: yes, link semantics only - **Shared OperationRun UX contract/layer reused**: `OperationRunLinks` and the existing OperationRun URL-resolution helpers - **Delegated start/completion UX behaviors**: tenant/workspace-safe environment link resolution only; no new queued toasts, start events, completion notifications, or dedupe messaging are added in this slice - **Local surface-owned behavior that remains**: none; existing surfaces continue to use the central OperationRun link helpers after their context model is retargeted - **Queued DB-notification policy**: `N/A` - **Terminal notification path**: `N/A` - **Exception required?**: yes - the temporary `/admin/t/{environment}` path retention described above. This is a bounded route exception only and does not permit a second compatibility family. ## Provider Boundary / Platform Core Check - **Shared provider/platform boundary touched?**: yes - **Boundary classification**: mixed - **Seams affected**: managed-target identity, provider-connection foreign-key anchors, current context model, operator vocabulary, route binding, and current target selection - **Neutral platform terms preserved or introduced**: `workspace`, `managed environment`, `provider connection`, `target scope`, `operation`, `finding`, `review`, and `governance artifact` - **Provider-specific semantics retained and why**: Microsoft-specific identifiers such as `entra_tenant_id`, Graph consent details, and portal metadata may remain in provider-owned tables or provider metadata until Spec `281` extracts and normalizes them. They must not live on `ManagedEnvironment`. - **Why this does not deepen provider coupling accidentally**: the core cutover explicitly forbids Microsoft-specific identity, Graph, or Intune fields on `ManagedEnvironment` and replaces `Tenant` as the shared core noun - **Follow-up path**: Spec `281` for provider-profile extraction, Spec `284` for provider-neutral artifact-source taxonomy, and Spec `286` for copy/localization cleanup ## UI / Surface Guardrail Impact | Surface / Change | Operator-facing surface change? | Native vs Custom | Shared-Family Relevance | State Layers Touched | Exception Needed? | Low-Impact / `N/A` Note | |---|---|---|---|---|---|---| | Managed-environment chooser replacing current tenant chooser | yes | Native Filament page | context selection, navigation entry point | page, URL/query | no | Keeps the existing chooser family and retargets it to managed environments | | Current environment-scoped panel routes under `/admin/t/{environment}` | yes | Native Filament panel shell | route links, context bar, global search, resource scope | shell, page, detail, URL/query | yes | Temporary path retention only until Spec `280` owns the public route-family rewrite | | Admin directory/resource surfaces that currently list `Tenant` records | yes | Native Filament resources/pages | detail links, current-target summaries | page, detail | no | Core noun and route binding change here; broader IA and copy polish remain deferred | ## Decision-First Surface Role | Surface | Decision Role | Human-in-the-loop Moment | Immediately Visible for First Decision | On-Demand Detail / Evidence | Why This Is Primary or Why Not | Workflow Alignment | Attention-load Reduction | |---|---|---|---|---|---|---|---| | Managed-environment chooser | Primary Decision Surface | Operator selects the current managed target inside the active workspace | environment name, display name, lifecycle posture, and current workspace | provider-specific diagnostics stay secondary and remain follow-up work | Primary because current environment selection is the first explicit decision before any environment-scoped workflow begins | Keeps the existing current-target selection workflow intact | Removes ambiguity between workspace scope and managed-target scope | | Current environment-scoped panel shell | Secondary Context Surface | Operator verifies they are acting in the correct managed environment while using existing pages | active workspace plus active managed-environment identity | deeper provider or governance detail remains on the current pages | Not primary because it carries context while work happens elsewhere | Aligns with existing panel-shell behavior instead of inventing a new workbench | Reduces wrong-target actions during the cutover | ## Audience-Aware Disclosure | Surface | Audience Modes In Scope | Decision-First Default-Visible Content | Operator Diagnostics | Support / Raw Evidence | One Dominant Next Action | Hidden / Gated By Default | Duplicate-Truth Prevention | |---|---|---|---|---|---|---|---| | Managed-environment chooser | operator-MSP, support-platform | environment identity, display name, lifecycle posture, and workspace context | provider-health detail and migration notes remain secondary | raw provider metadata stays hidden | `Enter environment` | provider-specific raw metadata | chooser cards show selection truth once and defer diagnostics | | Current environment-scoped panel shell | operator-MSP, support-platform | active workspace and active managed-environment context | deeper per-page diagnostics remain where they already live | raw payloads remain page-local and secondary | existing page action | any migration/debug hints stay lower-priority | shell surfaces should not repeat full page-level summaries | ## UI/UX Surface Classification | Surface | Action Surface Class | Surface Type | Likely Next Operator Action | Primary Inspect/Open Model | Row Click | Secondary Actions Placement | Destructive Actions Placement | Canonical Collection Route | Canonical Detail Route | Scope Signals | Canonical Noun | Critical Truth Visible by Default | Exception Type / Justification | |---|---|---|---|---|---|---|---|---|---|---|---|---|---| | Managed-environment chooser | Selector / Context / Entry | Context-selection page | Enter the intended managed environment | card/list selection | optional current pattern | workspace switch remains secondary | none | `/admin/choose-environment` (logical) | environment entry path | active workspace plus environment identity | Managed environment | selected target identity and lifecycle posture | none | | Current environment-scoped panel shell | Shell / Context / Navigation | Filament panel shell | Continue into the current environment page | current shell context | forbidden | page-local links stay secondary | none | `/admin/t/{environment}` (temporary) | existing page routes under the shell | active workspace plus active managed environment | Managed environment | current environment identity | temporary route retention until Spec `280` | ## Operator Surface Contract | Surface | Primary Persona | Decision / Operator Action Supported | Surface Type | Primary Operator Question | Default-visible Information | Diagnostics-only Information | Status Dimensions Used | Mutation Scope | Primary Actions | Dangerous Actions | |---|---|---|---|---|---|---|---|---|---|---| | Managed-environment chooser | TenantPilot operator | Decide which managed environment inside the current workspace should become active | Context-selection page | Which managed environment am I about to act in? | environment identity, workspace context, lifecycle posture | provider-specific diagnostics and migration detail | lifecycle, access posture | none | Enter environment | none | | Current environment-scoped panel shell | TenantPilot operator | Confirm that all current environment-bound pages resolve the right managed target | Panel shell | Am I in the correct workspace and managed environment? | active workspace and active managed-environment identity | page-local diagnostics only | access posture, lifecycle | none | existing page action | none | ## Proportionality Review - **New source of truth?**: yes - **New persisted entity/table/artifact?**: yes - **New abstraction?**: no new generic abstraction; existing tenant-context seams are replaced or neutralized in place - **New enum/state/reason family?**: yes, bounded managed-environment kind and lifecycle posture fields - **New cross-domain UI framework/taxonomy?**: no - **Current operator problem**: the product cannot become workspace-first or provider-neutral while the core managed target, current context, and route binding still assume `Tenant` - **Existing structure is insufficient because**: the current `Tenant` model mixes managed-target identity, provider-specific metadata, current-context behavior, and Filament tenant binding across the app - **Narrowest correct implementation**: a single breaking core cutover to `ManagedEnvironment` with one documented temporary route-path exception and no compatibility aliases - **Ownership cost**: broad schema and relation retargeting, command and test updates, temporary exception tracking for `/admin/t/{environment}`, and explicit follow-up ownership for Specs `280`-`287` - **Alternative intentionally rejected**: dual-read/dual-write aliases, a `Tenant` wrapper around `ManagedEnvironment`, or one giant combined route/IA/provider/artifact rewrite. Those options either violate the constitution or make the first cutover slice too broad to review safely. - **Release truth**: current-release truth with explicit follow-up slices for the remaining cutover pack ### Compatibility posture This feature assumes a pre-production environment. Backward compatibility, legacy aliases, dual-read/dual-write shims, historical fixtures, and compatibility-specific tests are out of scope unless explicitly required by this spec. Canonical replacement is preferred over preservation. ## Testing / Lane / Runtime Impact - **Test purpose / classification**: Unit, Feature, Browser - **Validation lane(s)**: fast-feedback, confidence, browser - **Why this classification and these lanes are sufficient**: the cutover changes core model truth, route/model binding, and Filament context selection. Unit tests prove model and context rules, feature tests prove scope and routing semantics, and one browser smoke proves the workspace-to-environment selection flow stays operable after the cutover. - **New or expanded test families**: one managed-environment unit family, one managed-environment feature family, and one narrow browser smoke - **Fixture / helper cost impact**: moderate; existing tenant fixtures and helper families must be replaced with managed-environment equivalents rather than layered adapters - **Heavy-family visibility / justification**: none - **Special surface test profile**: standard-native-filament, global-context-shell, exception-coded-surface - **Standard-native relief or required special coverage**: standard Filament feature coverage is sufficient for chooser and panel-context semantics; one browser smoke is required because the cutover changes end-to-end context selection and panel bootstrapping - **Reviewer handoff**: reviewers must verify that `ManagedEnvironment` replaces `Tenant` in core-owned paths, the temporary `/admin/t` exception is the only path deviation, the legacy-core guard proves `tenant_id` removal inside the declared core-owned cutover inventory while keeping Specs `281` and `282` exclusions explicit, `ManagedEnvironment` carries no Microsoft-specific identity fields, any touched globally searchable resource still satisfies Filament’s edit/view eligibility rule or remains out of global search, no new asset registration or deployment-step change appears, and provider registration remains in `apps/platform/bootstrap/providers.php` - **Budget / baseline / trend impact**: moderate feature-local increase only - **Escalation needed**: `document-in-feature` for the temporary `/admin/t` path exception; `follow-up-spec` already assigned for the remaining cutover pack - **Active feature PR close-out entry**: Guardrail - **Planned validation commands**: - `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"` ## Scope Boundaries *(required for this slice)* ### In Scope - introduce `ManagedEnvironment` as the active managed-target root inside a workspace - introduce the breaking schema/model cutover away from `Tenant` and `tenant_id` in core-owned paths - retarget current context selection, route binding, query helpers, memberships, and the current tenant-panel shell bridge to `ManagedEnvironment` while keeping the final `Workspace` Filament-tenancy switch deferred to Spec `280` - keep the current environment-scoped panel operable through the documented temporary `/admin/t/{environment}` exception until Spec `280` - update factories, seeders, policies, guards, route binding, and tests so core-owned paths no longer rely on `App\Models\Tenant` - ensure `ManagedEnvironment` carries no Microsoft-specific identity, Graph, or Intune fields ### Non-Goals - final workspace-first public route family and navigation IA rewrite - provider-profile extraction and provider-scope normalization - governance artifact naming and route retargeting polish - RBAC scope redesign or new capability families - copy/localization neutralization beyond the minimum core cutover terms - quality-gate automation beyond the feature-local guard tests needed for this slice - package execution, guided operations, virtual consultant, or any new provider implementation ## Assumptions - the repo remains pre-production, so destructive in-place replacement is acceptable and compatibility shims are not - the temporary `/admin/t/{environment}` path retention is the only allowed path exception for this slice - broader workspace-first IA, provider extraction, artifact retargeting, and RBAC scoping remain explicitly deferred to Specs `280`-`287` - current workspace membership and capability semantics stay materially unchanged in this slice; only the managed-target noun and foreign-key anchors move ## Risks - the implementation could accidentally combine the core cutover with the public route/IA rewrite, which would make review and rollback reasoning too broad - lingering `Tenant` references in commands, tests, or helper traits could hide cutover drift and produce mixed context behavior - provider-specific identity could accidentally land on `ManagedEnvironment` if current `Tenant` fields are copied forward without discipline - the temporary `/admin/t` exception could become permanent if Spec `280` is not treated as mandatory follow-up ## Candidate Selection Gate Summary - **Selected candidate**: `279 - Workspace-first Managed Environment Core Cutover` - **Source locations**: - `docs/product/spec-candidates.md` under the `Workspace-first / ManagedEnvironment Core Cutover candidate pack` - `docs/product/roadmap.md` under the planned cutover pack ordering - **Why selected now**: `279` is the first unspecced reserved slot in the cutover pack and is the prerequisite for the remaining reserved follow-ups - **Why close alternatives were deferred**: - `280` depends on the core entity and context replacement from `279` - `281` depends on the new managed-target root before provider-profile extraction makes sense - `282` depends on `managed_environment_id` becoming the core anchor first - `283`-`287` are later normalization and guardrail layers, not the initial breaking cutover - **Smallest viable implementation slice**: core entity replacement, foreign-key retargeting, current-context replacement, and one bounded path exception only - **Documented deviation from raw candidate wording**: this package defers the public workspace-first route-family rewrite and final `Workspace` Filament-tenancy end state to Spec `280`. To keep the implementation bounded and the runtime operable, `279` allows the current `/admin/t/{environment}` shell to remain temporarily while rebinding it to `ManagedEnvironment`. ## Completed-Spec Guardrail Result - `specs/276-support-access-governance/` already exists and remains a separate prepared package - `specs/277-stored-reports-surface/` already exists and remains a separate prepared package - `specs/278-cross-domain-indicator-audit/` already exists and carries completed docs-only close-out history - `specs/247-plans-entitlements-billing-readiness/`, `specs/251-commercial-entitlements-billing-state/`, `specs/252-platform-localization-v1/`, `specs/262-lifecycle-governance-taxonomy/`, and `specs/264-cross-tenant-promotion-execution/` remain context only and are not rewritten by this package ## Deferred Adjacent Candidates - `280 - Filament Workspace Tenancy & Environment Routing Cutover` - `281 - Provider Connection, Provider Scope & Microsoft Profile Extraction` - `282 - Governance Artifact Retargeting to ManagedEnvironment` - `283 - Provider Capability Registry v1` - `284 - Provider-neutral Artifact Source Taxonomy v1` - `285 - Workspace-first RBAC & Environment Access Scoping` - `286 - UI Copy, IA & Localization Neutralization` - `287 - Cutover Quality Gates & No-Legacy Enforcement` ## User Scenarios & Testing ### User Story 1 - Resolve managed environments as the active target inside a workspace (Priority: P1) As a platform operator, I want the active managed target to be a `ManagedEnvironment` inside my current workspace so the platform core stops depending on `Tenant` as its primary identity model. **Why this priority**: every later cutover slice depends on the managed-target root being replaced first. **Independent Test**: Seed a workspace plus one managed environment, resolve the record through route binding and current-context helpers, and confirm the core paths no longer require `App\Models\Tenant`. **Acceptance Scenarios**: 1. **Given** a workspace has a managed environment, **When** the current environment is resolved by route key, **Then** the app returns the `ManagedEnvironment` record and current workspace context instead of a `Tenant` model. 2. **Given** a wrong-workspace or non-member actor, **When** they try to resolve the same managed environment, **Then** the app responds with `404` and leaks no environment-bound data. --- ### User Story 2 - Keep current environment-scoped surfaces operable through the cutover (Priority: P1) As an operator using the current admin panel, I want the existing environment-scoped shell to keep working while the core noun changes so the cutover is reviewable without bundling the full route/IA rewrite. **Why this priority**: the cutover is not implementation-ready unless the current panel still boots on the new core entity. **Independent Test**: Select a workspace, enter a managed environment from the retargeted chooser, and open the current environment dashboard through the temporary `/admin/t/{environment}` shell. **Acceptance Scenarios**: 1. **Given** an operator selected a workspace, **When** they choose a managed environment, **Then** the current panel shell loads with that managed environment as the active bound model. 2. **Given** the same environment-scoped shell, **When** a page or deep link resolves the active target, **Then** route builders, query helpers, and current-target chips use `ManagedEnvironment` consistently. --- ### User Story 3 - Remove active tenant-core drift from schema, policies, and tests (Priority: P2) As a maintainer, I want core-owned schema, policies, factories, and guard tests to use `ManagedEnvironment` and `managed_environment_id` so later specs do not inherit mixed core nouns. **Why this priority**: lingering tenant-core drift would make the follow-up cutover pack unstable and ambiguous. **Independent Test**: Run the managed-environment unit/feature suite and the legacy-core guard checks, then confirm no active `App\Models\Tenant` or `->tenant(Tenant::class)` binding remains in core-owned paths. **Acceptance Scenarios**: 1. **Given** the cutover implementation is complete, **When** factories, policies, and tests create the managed target, **Then** they use `ManagedEnvironment` and managed-environment memberships rather than `Tenant`. 2. **Given** the final core-owned paths are searched or guard-tested, **When** forbidden legacy core patterns are checked, **Then** no active `Tenant` core model or Filament tenant binding remains. ### Edge Cases - The current public `/admin/t/{environment}` path may remain temporarily, but it must bind `ManagedEnvironment` and must not coexist with a second compatibility route family. - Microsoft-specific external terms such as `Microsoft Entra Tenant ID` may remain only in provider-owned metadata or provider profiles, not on `ManagedEnvironment`. - Archived or inactive managed environments must not become selectable current context by default. - Old tenant-specific factories, seeders, and fixtures must be removed or replaced in the same cutover PR rather than kept for convenience. ## Requirements *(mandatory)* **Constitution alignment (required):** This feature changes core data ownership, route binding, context selection, and existing operator-facing shell behavior. It adds no Microsoft Graph calls and no new `OperationRun` family. Any security-relevant mutation that is not represented by `OperationRun` still uses existing audit behavior where applicable. **Constitution alignment (PROP-001 / ABSTR-001 / PERSIST-001 / STATE-001 / BLOAT-001):** The feature introduces a new persisted root entity because the current product truth needs a provider-neutral managed-target core now. A narrower alias or adapter would preserve the wrong truth and would violate the repo’s pre-production lean doctrine. **Constitution alignment (XCUT-001):** Current context-selection, route-building, query-scoping, and shell-context seams must be replaced in place. No second local environment-context language is allowed. **Constitution alignment (PROV-001):** `ManagedEnvironment` must remain provider-neutral. Provider-specific identity stays in provider-owned seams and follow-up Spec `281`. **Constitution alignment (SCOPE-001 exception/update prerequisite):** The current constitution still requires tenant-owned tables to use `workspace_id` plus `tenant_id` as `NOT NULL`. This package intentionally replaces active core-owned managed-target keys with `managed_environment_id`. Implementation is gated by the explicit exception record in `specs/279-workspace-managed-environment-core/checklists/constitution-scope-001-exception.md` until the constitution itself is amended. ### Functional Requirements - **FR-001**: The package MUST introduce `ManagedEnvironment` as the active managed-target root record inside a workspace. - **FR-002**: The package MUST remove active core dependence on `App\Models\Tenant` and retarget core-owned foreign keys from `tenant_id` to `managed_environment_id`. - **FR-003**: `ManagedEnvironment` MUST NOT contain Microsoft-specific identity, Graph, or Intune fields. - **FR-004**: Current workspace-plus-target context resolution MUST bind `ManagedEnvironment` instead of `Tenant`. - **FR-005**: Current environment membership and capability checks MUST continue to enforce `404` for non-members and `403` for in-scope capability denials. - **FR-006**: The current environment-scoped panel shell may retain the `/admin/t/{environment}` path temporarily, but it MUST bind `ManagedEnvironment` and MUST remain the only documented path exception in this slice. - **FR-007**: Current tenant-scoped query helpers, global-search scoping, route builders, and current-target UI seams MUST resolve `ManagedEnvironment` consistently. - **FR-008**: Tests, factories, policies, and seeders in core-owned paths MUST use `ManagedEnvironment` naming and IDs after the cutover. - **FR-009**: The package MUST not introduce dual columns, alias models, dual-write logic, or compatibility routes. - **FR-010**: The package MUST explicitly defer the remaining workspace-first route-family rewrite, provider extraction, artifact retargeting, RBAC scope redesign, copy neutralization, and long-lived cutover quality gates to Specs `280`-`287`. ### Authorization and Safety Requirements - **AR-001**: Workspace membership remains the first access boundary and must still deny non-members as `404`. - **AR-002**: Managed-environment membership remains the second access boundary and must still deny wrong-environment access as `404`. - **AR-003**: No new destructive action semantics are introduced. Any touched existing destructive actions must preserve `->requiresConfirmation()` and current authorization checks. ### Non-Functional Requirements - **NFR-001**: Filament remains v5 on Livewire v4. - **NFR-002**: Provider registration stays in `apps/platform/bootstrap/providers.php`; no new panel provider registration location is introduced. - **NFR-003**: Any existing globally searchable resource touched by the cutover must continue to satisfy Filament’s edit/view-page requirement or remain out of global search. - **NFR-004**: No new asset registration or `filament:assets` deployment change is introduced in this slice. - **NFR-005**: The cutover must remain reviewable as one bounded implementation loop and must not silently absorb the broader Spec `280`-`287` work.