## 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
260 lines
16 KiB
Markdown
260 lines
16 KiB
Markdown
# 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.
|
||
- **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 needed `operation_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).
|
||
- **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_type` deep-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:
|
||
|
||
1) workspace-owned surfaces stay workspace-owned (even when entered from environment context),
|
||
2) workspace hubs are filtered only via explicit, product-level query keys (`environment_id`, and optionally `operation_type`),
|
||
3) environment-owned detail surfaces remain environment-route-owned,
|
||
4) first-party helpers stop emitting Filament table internals (`tableFilters[...]`) as canonical deep link contract,
|
||
5) 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}/...`
|
||
- **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_id` MUST 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):
|
||
|
||
- `tenant`
|
||
- `tenant_id`
|
||
- `managed_environment_id`
|
||
- `tenant_scope`
|
||
- `tableFilters`
|
||
|
||
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 emit `tableFilters`.
|
||
- Operations page accepts `operation_type` and 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/overview` is a workspace hub route (`admin.evidence.overview`).
|
||
- `apps/platform/app/Http/Controllers/ClearEnvironmentContextController.php` and `apps/platform/app/Support/Navigation/AdminSurfaceScope.php` contain legacy handling/classification for `/admin/evidence/*` paths.
|
||
|
||
Decision:
|
||
- Keep Evidence Overview as workspace hub, with optional explicit `environment_id` filter.
|
||
- 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
|
||
- [x] Existing page changed
|
||
- [ ] New page/route added
|
||
- [x] Navigation changed
|
||
- [x] 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
|
||
- [x] 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_id` is 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`, Filament `SIDEBAR_NAV_START` render 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; expected `no`)
|
||
- [x] `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_type` at platform scope; keep `tenant` terminology 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 needed `operation_type`), not `tableFilters[...]`.
|
||
- **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_id` only to workspace hubs that support explicit environment filtering.
|
||
- **AC7**: Managed Environments registry pages do not duplicate the `/admin/choose-environment` flow 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)
|