319 lines
34 KiB
Markdown
319 lines
34 KiB
Markdown
# 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.
|