TenantAtlas/specs/338-workspace-environment-resource-scope-contract/spec.md
ahmido e0c2cdb1f4 feat: enforce workspace and environment scope contract (Spec 338) (#409)
## 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
2026-05-31 01:36:08 +00:00

16 KiB
Raw Permalink Blame History

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: TenantPilots 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 pages 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”.

Workspace-owned surface without environment filter.

Environment-owned surface with environment in the route.

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 specs 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

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
  • 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_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)
    • 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)