## Summary - enforce the canonical workspace/environment scope contract for workspace hubs and environment-owned surfaces - replace first-party Operations deep links that leaked Filament `tableFilters[...]` internals with stable product-level query behavior - add the sidebar scope indicator and split environment-page navigation into explicit `Workspace-wide` and `Workspace admin` groups - remove redundant tenantless `All environments` scope badges from workspace-wide pages while preserving explicit environment filter affordances - include the Spec 338 artifacts, guard tests, and browser smoke coverage for the new contract ## Validation - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Navigation/Spec338EnvironmentSidebarSeparationTest.php tests/Feature/Navigation/Spec338OperationRunLinksQueryContractTest.php tests/Feature/Navigation/Spec338SidebarScopeIndicatorTest.php tests/Feature/Filament/PanelNavigationSegregationTest.php` - `cd apps/platform && ./vendor/bin/sail php vendor/bin/pest tests/Browser/Spec338ScopeContractSmokeTest.php --compact` ## Notes - Livewire v4 compliance unchanged - Filament provider registration remains in `bootstrap/providers.php` - no destructive action behavior changed - no migrations, env var changes, or new Filament asset registration Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #409
16 KiB
Feature Specification: Spec 338 - Workspace / Environment Resource Scope Contract
Feature Branch: 338-workspace-environment-resource-scope-contract
Created: 2026-05-30
Status: Draft
Input: User-provided Spec 338 draft (“Contract-/Guard-Spec” for workspace/environment resource ownership + link/query hygiene)
Spec Candidate Check (mandatory — SPEC-GATE-001)
- Problem: TenantPilot’s workspace/environment scope foundations exist (Specs 311/319/320/321), but remaining link/query seams and navigation registration can still encode “mixed ownership” (workspace-owned surfaces appearing environment-owned, or workspace hub filters encoded as hidden context / framework internals).
- Today's failure:
- First-party deep links can still emit Filament internal query keys (notably
tableFilters[...]) instead of a stable product-level contract. - Environment → workspace hub links can drift between “route scope” and “filter scope” depending on the helper used.
- Evidence has legacy
/admin/evidence/*classification/special casing that must be either proven real and intentional, or removed as stale to reduce ambiguity.
- First-party deep links can still emit Filament internal query keys (notably
- User-visible improvement: Operators can trust that:
- route scope determines shell/sidebar;
- workspace hubs filter by a stable, explicit query contract (
environment_id, and where neededoperation_type); - environment navigation does not claim workspace-owned portfolio surfaces as environment-owned;
- the sidebar exposes a direct scope signal so workspace-level and environment-level pages are distinguishable without reading the URL.
- workspace-wide pages do not render a generic “All environments” header/scope badge when the page is already tenantless; explicit environment filters remain visible through filter banners and table chips.
- Smallest enterprise-capable version: Document the canonical ownership taxonomy and enforce only the highest-risk seams with tests:
- stop first-party helpers from emitting
tableFilters[...]for hub deep links (especially Operations), - ensure Evidence scope is explicit (workspace hub vs environment-owned resources),
- keep baseline ownership/navigation contract regression-proof (no reopen of Spec 320; fix only if regression is proven).
- stop first-party helpers from emitting
- Explicit non-goals:
- no broad UI redesign of the admin shell, sidebars, or page layouts,
- no route restructuring (keep canonical route families as-is),
- no workspace/environment data model or schema changes,
- no Provider Connections “scope split” feature (defer to the already-listed candidate),
- no rewrite of Spec 311/320 behavior unless a regression is proven by tests.
- Permanent complexity imported: narrow link/query contract mapping (
operation_typedeep-link), a small set of guard tests, and clarified operator-copy expectations (only where proven misleading). - Why now: Current productization and audit lanes depend on stable, explicit scope and deep links; leaving internal query keys in first-party helpers makes future specs copy the wrong contract.
- Why not local: Fixing one page’s deep link without a contract + guard tests leaves the next helper or navigation entry free to reintroduce the same ambiguity.
- Approval class: Cleanup / Consolidation (contract + guard hardening over existing foundations).
- Red flags triggered: Cross-surface contract + shared link helper changes. Defense: scope is bounded to confirmed seams; no new taxonomy framework or persisted truth; tests enforce stability.
- Score: Nutzen: 2 | Dringlichkeit: 2 | Scope: 1 | Komplexität: 1 | Produktnähe: 2 | Wiederverwendung: 2 | Gesamt: 10/12
- Decision: approve.
Summary
TenantPilot already has strong workspace/environment scope foundations. This spec locks down a resource ownership + link/query contract so that:
- workspace-owned surfaces stay workspace-owned (even when entered from environment context),
- workspace hubs are filtered only via explicit, product-level query keys (
environment_id, and optionallyoperation_type), - environment-owned detail surfaces remain environment-route-owned,
- first-party helpers stop emitting Filament table internals (
tableFilters[...]) as canonical deep link contract, - the sidebar presents explicit workspace vs environment scope identity.
This is a contract-first spec with targeted runtime fixes only.
Spec Scope Fields (mandatory)
- Scope: canonical-view (navigation + link/query contract)
- Primary Routes (representative):
- Workspace hubs:
/admin/workspaces/{workspace}/operations,/admin/evidence/overview,/admin/alerts,/admin/audit-log - Workspace-owned portfolio surfaces:
/admin/baseline-profiles,/admin/baseline-snapshots - Environment-owned detail surfaces:
/admin/workspaces/{workspace}/environments/{environment}/...
- Workspace hubs:
- Data Ownership: no ownership model change. This spec is about UI scope signals + link/query contracts, not table ownership.
- RBAC: no new capabilities. Existing workspace membership and tenant/environment membership continue to gate visibility and access.
For canonical-view specs, the spec MUST define:
- Default filter behavior when tenant-context is active:
- Workspace hubs MUST NOT silently infer environment filtering from remembered environment/topbar selection.
- If a workspace hub is filtered, it MUST be via explicit query (
environment_id) or explicit visible UI filter state. - Environment-owned routes MUST include the environment in the route (no query-derived environment ownership).
- Explicit entitlement checks preventing cross-tenant leakage:
environment_idMUST be validated as “belongs to current workspace” AND “actor is entitled”; otherwise ignore/deny safely.- Canonical deep links must not widen scope through implicit session context.
Canonical Scope Taxonomy (product contract)
Every reachable surface is classified as exactly one:
A. Workspace-owned source of truth
Workspace-owned; may aggregate across environments; does not require an environment route.
B. Workspace hub with optional local environment filter
Workspace-owned monitoring/governance hubs that may filter by environment via explicit query/UI.
Rules:
- Route determines shell (workspace shell).
- Public filter query key is
environment_id. - Hubs must not infer filters from topbar “remembered environment”.
C. Environment-owned detail surface
Belongs to exactly one Managed Environment.
Rules:
- Route includes workspace + environment:
/admin/workspaces/{workspace}/environments/{environment}/... - Environment is not optional or query-derived.
D. Cross-environment / portfolio aggregation
Compares/aggregates across multiple environments; must not pretend to be “current environment owned”.
E. Platform / system / utility
System pages (/system, auth callbacks, choosers). Must not create hidden environment filters.
F. Invalid / needs split
Any surface that mixes route scope, navigation scope, and data scope such that the operator cannot tell “what owns this”.
Routing / Link Contract
WorkspaceLink
Workspace-owned surface without environment filter.
EnvironmentLink
Environment-owned surface with environment in the route.
WorkspaceFilteredLink
Workspace-owned hub filtered to one environment via explicit query.
Allowed public filter keys:
environment_id(canonical)operation_type(Operations-only, optional; see required decision D1)
Forbidden as first-party helper output for hub scope (canonical deep-link contract):
tenanttenant_idmanaged_environment_idtenant_scopetableFilters
Note: Filament may still persist table state in the URL after user interactions. This spec’s restriction is about first-party helper outputs and canonical deep links, not about banning every possible tableFilters appearance after manual operator filtering.
Required Runtime Decisions
D1 — OperationRunLinks operation type filter (confirmed repo seam)
Repo evidence: apps/platform/app/Support/OperationRunLinks.php currently emits tableFilters[type][value] when operationType is provided.
Decision:
tableFilters[...]must not be emitted by first-party helpers for operation-type deep links.- If operation-type deep-linking is needed, use a stable query key:
operation_type=<canonical-code>
Acceptance:
OperationRunLinks::index(..., operationType: ...)does not emittableFilters.- Operations page accepts
operation_typeand translates it into local table state, or operation-type deep links are removed (prefer correctness over leaking internals).
D2 — Evidence route special casing (confirmed repo seam)
Repo evidence:
/admin/evidence/overviewis a workspace hub route (admin.evidence.overview).apps/platform/app/Http/Controllers/ClearEnvironmentContextController.phpandapps/platform/app/Support/Navigation/AdminSurfaceScope.phpcontain legacy handling/classification for/admin/evidence/*paths.
Decision:
- Keep Evidence Overview as workspace hub, with optional explicit
environment_idfilter. - Confirm whether any
/admin/evidence/*non-overview paths are still real and intended:- If not real, remove/neutralize stale classification branches.
- If real, document the intended contract and stop treating it as “mystery scope”.
Acceptance:
- No ambiguous third “environment-scoped evidence under
/admin/evidence/*” remains without explicit contract + test.
D3 — Baseline ownership & navigation (regression-only)
Repo evidence: Spec 320 completed classification for baseline library surfaces as workspace-owned analysis.
Decision:
- Do not reopen baseline ownership decisions in Spec 338.
- Only change baseline navigation registration if a regression is proven by tests or UI contract failures on current branch.
Acceptance:
- Baseline Profiles/Snapshots remain workspace-owned surfaces; environment navigation must not claim them as environment-owned.
UI Surface Impact (mandatory — UI-COV-001)
- No UI surface impact
- Existing page changed
- New page/route added
- Navigation changed
- Filament panel/provider surface changed
- New modal/drawer/wizard/action added
- New table/form/state added
- Customer-facing surface changed
- Dangerous action changed
- Status/evidence/review presentation changed
- Workspace/environment context presentation changed
Scope Badge Contract Addendum
- Tenantless workspace-wide pages MUST NOT render a generic “All environments” header action or workbench badge as the primary context signal.
- When
environment_idis present on a workspace hub, the explicit filter banner/chip is the source of truth for the narrowed dataset. - Environment-scoped shell labels remain valid only when the route truly resolves to an environment-owned context.
UI/Productization Coverage (UI-COV-001)
- Route/page/surface: Operations hub deep links; Evidence Overview hub; environment sidebar vs workspace sidebar entries and scope identity, including separated workspace-wide/admin groups on environment-owned pages (baseline library surfaces, regression-only)
- Current page archetype: Monitoring hub (Operations/Evidence); navigation shell contract
- Design depth: Domain Pattern Surface (contract hardening, minimal visual work)
- Repo-truth level: repo-verified (Spec 311/320/322 + current helper code)
- Existing pattern reused:
AdminSurfaceScope,WorkspaceHubRegistry, FilamentSIDEBAR_NAV_STARTrender hook, Filament navigation groups,CanonicalNavigationContext,OperationRunLinks - New pattern required: small scope-aware workspace hub navigation helper, limited to grouping and environment filter URL carry for existing hub entries
- Screenshot required: yes, only for scope-regression proof in the implementation PR (light/dark where relevant)
- Page audit required: no (existing archetypes; update coverage artifacts only if new navigation entries are introduced)
- Dangerous-action review required: no (no destructive action changes)
- Coverage files to update (in implementation PR):
docs/ui-ux-enterprise-audit/route-inventory.md(only if navigation entries/routes change)docs/ui-ux-enterprise-audit/design-coverage-matrix.md(only if new surface created; expectedno)N/A - no new reachable UI surface added; contract hardening only
Cross-Cutting / Shared Pattern Reuse (mandatory)
- Cross-cutting feature?: yes
- Interaction class(es): navigation entry points, scope presentation, deep links, hub filtering
- Systems touched:
AdminSurfaceScope,WorkspaceHubRegistry,WorkspaceHubNavigation, Filament sidebar render hook/navigation groups,CanonicalNavigationContext,OperationRunLinks,ClearEnvironmentContextController - Existing pattern(s) to extend: canonical workspace/environment scope contract (Specs 311/320/322)
- Allowed deviation and why: none (prefer tightening existing helpers over new frameworking)
- Consistency impact: “Route determines shell; query determines filter; helpers emit canonical keys.”
- Review focus: no new scope magic; no helper outputs that encode Filament internals as canonical contract.
OperationRun UX Impact
- Touches OperationRun start/completion/link UX?: yes (link semantics only)
- Shared OperationRun UX contract/layer reused:
App\\Support\\OperationRunLinks - Delegated behaviors: operation collection URL generation; environment filter key; operation type deep link key
- Queued DB-notification policy:
N/A - Terminal notification path:
N/A - Exception required?: none
Provider Boundary / Platform Core Check
- Shared provider/platform boundary touched?: yes (terminology + query keys must not reintroduce legacy “tenant” meaning at platform scope)
- Boundary classification: platform-core (workspace/environment scope) + provider-owned (Entra “tenant” identity) must remain separated
- Seams affected: query keys and helper naming only
- Why this does not deepen provider coupling accidentally: enforce
environment_id/operation_typeat platform scope; keeptenantterminology provider-boundary-only.
Testing / Lane / Runtime Impact (mandatory for runtime behavior changes)
- Test purpose / classification: Feature (contract tests) + Browser (minimal smoke for sidebar/scope)
- Validation lane(s): fast-feedback (Feature) + browser (smoke), no heavy-governance required for this slice
Acceptance Criteria
- AC1: First-party deep links use canonical query keys (
environment_id, and where neededoperation_type), nottableFilters[...]. - AC2: Evidence scope is explicit: Evidence Overview is a workspace hub; any remaining
/admin/evidence/*special casing is either removed as stale or documented + tested as intentional. - AC3: Baseline library ownership remains workspace-owned and does not regress (no baseline ownership reopen).
- AC4: Targeted tests are green (feature contract tests + minimal browser smoke if UI navigation is involved).
- AC5: Workspace-owned and environment-owned pages show an explicit sidebar scope indicator that names the active workspace or environment, while tenantless workspace topbars and environment pickers do not render a negative “No environment selected” status.
- AC6: Environment-owned sidebars separate workspace-wide/admin links into clearly labeled groups and carry
environment_idonly to workspace hubs that support explicit environment filtering. - AC7: Managed Environments registry pages do not duplicate the
/admin/choose-environmentflow with a redundant “Choose environment” CTA; environment cards remain the entry point, with Add Environment and Switch Workspace as the supporting actions.
Follow-up spec candidates
- Provider Connection Scope Hardening (credential-adjacent authority semantics)
- Canonical Link / Query Cleanup (broader inventory + replacement beyond Operations/Evidence)
- Environment Resource Context Follow-through (reduce hidden context reliance inside environment-owned resources)