TenantAtlas/specs/311-workspace-environment-surface-scope-contract/plan.md
ahmido bf43dad3d1 fix: enforce workspace surface scope for customer review workspace (#366)
## Summary
- keep `/admin/reviews/workspace` workspace-scoped in shell and sidebar context
- treat `tenant` query hints on the customer review workspace as page-level filters only
- update the customer review workspace tests and Spec 311 navigation contract to match the workspace-hub IA

## Testing
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Reviews/CustomerReviewWorkspacePageTest.php`
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Filament/WorkspaceContextTopbarAndTenantSelectionTest.php tests/Feature/Filament/PanelNavigationSegregationTest.php`
- `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent`
- `git diff --check`

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #366
2026-05-15 20:52:37 +00:00

13 KiB

Implementation Plan: Workspace / Environment Surface Scope Contract

Branch: 311-workspace-environment-surface-scope-contract | Date: 2026-05-15 | Spec: specs/311-workspace-environment-surface-scope-contract/spec.md Input: Feature specification from /specs/311-workspace-environment-surface-scope-contract/spec.md

Summary

Implement the global admin surface scope contract: route scope controls shell/navigation, page filters control data. Explicit workspace-wide surfaces remain tenantless in the shell even with query filters or remembered/Filament tenant context. Canonical environment routes remain environment-bound. Legacy tenant-owned admin lists keep their existing data scoping until a separate route cutover handles them.

Technical Context

Language/Version: PHP 8.4, Laravel 12, Filament v5, Livewire v4 Primary Dependencies: Existing Filament panel, TenantPageCategory, NavigationScope, OperateHubShell, CanonicalAdminTenantFilterState Storage: No database changes Testing: Pest v4 / Livewire component tests / HTTP feature tests Validation Lanes: focused feature/unit tests, Pint dirty, git diff --check Target Platform: TenantPilot admin panel Project Type: Laravel monorepo Performance Goals: No extra database work beyond existing widget/list queries Constraints: No migrations, no RBAC changes, no Customer Review Workspace product completion, no broad navigation rebuild, no sidebar query magic Scale/Scope: Representative workspace-wide surfaces, canonical environment routes, and legacy tenant-owned regression coverage

UI / Surface Guardrail Plan

  • Guardrail scope: shell scope, topbar labels, sidebar classification, and table/widget filter defaults.
  • Native vs custom classification summary: Filament-native pages/resources/widgets only; no custom styling.
  • Shared-family relevance: global admin navigation shell and workspace-wide monitoring/governance/reporting surfaces.
  • State layers in scope: route path, query filter values, table filter session state, remembered tenant session, Filament tenant context.
  • Audience modes in scope: workspace operator/admin only.
  • Decision/diagnostic/raw hierarchy plan: N/A.
  • Raw/support gating plan: N/A.
  • One-primary-action / duplicate-truth control: prevent duplicate truth by making shell route-owned and filters page-owned.
  • Handling modes by drift class or surface: explicit workspace-wide surfaces become tenantless shell; canonical environment routes remain tenant-bound; legacy tenant-owned lists remain current behavior.
  • Repository-signal treatment: test-first red/green on representative surfaces and parity tests.
  • Special surface test profiles: Livewire tests must set a realistic referer when asserting route-derived shell behavior.
  • Required tests or manual smoke: focused Pest/Livewire tests; browser smoke not required because no styling/layout change.
  • Exception path and spread control: future workspace-wide surfaces must be added to TenantPageCategory and covered by the surface matrix.
  • Active feature PR close-out entry: Workspace / Environment Surface Scope Contract.

Shared Pattern & System Fit

  • Cross-cutting feature marker: admin shell/navigation contract.
  • Systems touched: route taxonomy, shell context, workspace-wide filters, monitoring/alerts widgets.
  • Shared abstractions reused: TenantPageCategory, TenantInteractionLane, OperateHubShell, NavigationScope, CanonicalAdminTenantFilterState.
  • New abstraction introduced? why?: No new service or registry. Existing enum gains WorkspaceWideSurface.
  • Why the existing abstraction was sufficient or insufficient: The existing taxonomy already owns page category; it needed one extra category to separate explicit workspace-wide hubs from legacy workspace-scoped tenant-owned lists.
  • Bounded deviation / spread control: explicit path matching stays centralized in TenantPageCategory.

OperationRun UX Impact

  • Touches OperationRun start/completion/link UX?: Yes, Operations list/KPI scope only.
  • Central contract reused: OperationRunLinks and existing Operations page remain.
  • Delegated UX behaviors: no start/completion changes.
  • Surface-owned behavior kept local: Operations tabs/table query remain local.
  • Queued DB-notification policy: unchanged.
  • Terminal notification path: unchanged.
  • Exception path: none.

Provider Boundary & Portability Fit

  • Shared provider/platform boundary touched?: provider connections and alerts as workspace-wide/provider-level surfaces.
  • Provider-owned seams: Microsoft-specific inventory/policy/backup legacy tenant-owned lists remain out of this minimal cutover.
  • Platform-core seams: workspace, environment, provider connection, alert delivery, audit log, governance/workspace hubs.
  • Neutral platform terms / contracts preserved: workspace-wide, environment-bound, provider connection, environment filter.
  • Retained provider-specific semantics and why: legacy tenant-owned direct lists retain current tenant data context pending their own route migration.
  • Bounded extraction or follow-up path: 312 Customer Review Workspace v1 Completion follows after this contract; tenant-owned route cutover remains separate.

Constitution Check

  • Inventory-first: PASS. No inventory truth changes.
  • Read/write separation: PASS. Read-only shell/list/widget behavior only.
  • Graph contract path: PASS. No Graph calls changed.
  • Deterministic capabilities: PASS. No capability mapping changes.
  • Workspace isolation: PASS. Workspace query scopes remain enforced.
  • Tenant isolation: PASS. Environment-bound routes and legacy tenant-owned lists remain protected.
  • Run observability: PASS. OperationRun data visibility remains entitlement-scoped.
  • Test governance: PASS. Focused tests plus parity regression; no new heavy family.
  • Proportionality: PASS. One enum category in an existing taxonomy is narrower than a registry or navigation rebuild.
  • No premature abstraction: PASS. No new service/interface/framework.
  • Persisted truth: PASS. No persisted runtime truth.
  • Behavioral state: PASS. Route category affects shell behavior only.
  • Shared pattern first: PASS. Existing shell/taxonomy path reused.
  • Provider boundary: PASS. Platform-core route contract stays provider-neutral.
  • Filament-native UI: PASS. No assets, no view publishing.

Test Governance Check

  • Test purpose / classification by changed surface: Feature tests for admin shell and Livewire pages/widgets; Unit test for route category classification.
  • Affected validation lanes: focused Feature/Unit lane.
  • Why this lane mix is narrowest sufficient proof: The bug is route/shell/filter behavior, provable by HTTP and Livewire component tests without browser layout verification.
  • Narrowest proving command(s):
    • Focused contract and neighboring surface tests under tests/Feature/...
    • tests/Unit/Tenants/TenantPageCategoryTest.php
    • cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent
    • git diff --check
  • Fixture / helper / factory / seed / context cost risks: Reuses existing factories/helpers; no new heavy fixture family.
  • Expensive defaults or shared helper growth introduced?: no.
  • Heavy-family additions, promotions, or visibility changes: none.
  • Surface-class relief / special coverage rule: Browser smoke not required; no visual layout/styling change.
  • Closing validation and reviewer handoff: Verify representative workspace-wide surfaces, environment-bound routes, and legacy tenant-owned parity tests.
  • Budget / baseline / trend follow-up: none expected.
  • Review-stop questions: any per-page shell exception, query-driven sidebar, RBAC change, migration, or Customer Review product logic.
  • Escalation path: remaining legacy route taxonomy questions belong to a follow-up route cutover/audit spec.
  • Active feature PR close-out entry: Workspace / Environment Surface Scope Contract.

Project Structure

apps/platform/app/Support/Tenants/TenantPageCategory.php
apps/platform/app/Support/Tenants/TenantInteractionLane.php
apps/platform/app/Support/OperateHub/OperateHubShell.php
apps/platform/app/Filament/Pages/Reviews/*
apps/platform/app/Filament/Resources/AlertDeliveryResource.php
apps/platform/app/Filament/Resources/ProviderConnectionResource.php
apps/platform/app/Filament/Widgets/Alerts/AlertsKpiHeader.php
apps/platform/app/Filament/Widgets/Operations/OperationsKpiHeader.php
apps/platform/tests/Feature/**
apps/platform/tests/Unit/Tenants/TenantPageCategoryTest.php
specs/311-workspace-environment-surface-scope-contract/

Structure Decision: Extend existing taxonomy and tests in place. Do not introduce a new surface registry.

Complexity Tracking

Violation Why Needed Simpler Alternative Rejected Because
Add enum category Existing WorkspaceScoped combined explicit workspace hubs with legacy tenant-owned lists Forcing all WorkspaceScoped tenantless regressed legacy tenant-owned resource data scoping; local page exceptions would spread

Proportionality Review

  • Current operator problem: Workspace hubs can display environment context in the shell while also showing page-level environment filters.
  • Existing structure is insufficient because: WorkspaceScoped was too broad for both workspace hubs and legacy tenant-owned direct lists.
  • Narrowest correct implementation: Add WorkspaceWideSurface to the existing category enum and force tenantless shell only there.
  • Ownership cost created: Future workspace-wide routes must be added centrally with tests.
  • Alternative intentionally rejected: CRW-only patch, ?tenant sidebar switching, broad nav rewrite, or converting all legacy tenant-owned routes in this branch.
  • Release truth: Current platform shell contract.

Phase 0: Read-Only Findings

  • Operations already behaved correctly because it had explicit all-environments/page-filter semantics.
  • Customer Review Workspace, Review Register, Audit Log, Alerts, and Alert Delivery could inherit remembered/Filament tenant through shell or filter defaults.
  • Provider Connections defaulted its list filter from the resolved scoped tenant instead of explicit query state.
  • Legacy tenant-owned resource pages still rely on remembered tenant data context; this branch preserves them and protects them with parity tests.

Implementation Notes

  • TenantPageCategory::fromRequest() now uses Livewire referer paths for Livewire update requests so shell resolution matches browser route scope.
  • WorkspaceWideSurface covers explicit workspace-wide admin surfaces, including Operations, Reviews workspace/register, Governance Inbox/Decision Register, Evidence Overview, Audit Log, Provider Connections, Alerts, and Workspace Overview.
  • OperateHubShell returns tenantless workspace context before query/Filament/remembered tenant resolution for WorkspaceWideSurface.
  • Customer Review Workspace and Review Register no longer default their environment filters from remembered tenant and no longer clear global last-tenant state when clearing page filters.
  • Alert Delivery list and Alerts KPI are workspace-wide and no longer use shell tenant as data default.
  • Operations KPI now counts the workspace entitlement scope when the shell is tenantless.
  • Provider Connection list filter defaults only from explicit request state, not remembered/global context.

Validation Plan

Run the focused contract and neighboring tests, then Pint and whitespace checks:

cd apps/platform
./vendor/bin/sail artisan test --compact <focused files>
./vendor/bin/sail bin pint --dirty --format agent
cd ../..
git diff --check

Implementation Close-Out

  • Changed runtime areas: route taxonomy, shell resolution, workspace-wide page filter defaults, Operations KPI, Alerts KPI/list, Provider Connections filter default.
  • Changed tests: focused shell/navigation, Operations, Customer Review Workspace, Review Register, Audit Log, Alerts, Provider Connections, tenant-owned parity, and route category coverage.
  • Validation completed:
    • cd apps/platform && ./vendor/bin/sail artisan test --compact ... for the focused 311 selection: 172 passed, 703 assertions.
    • cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent: pass.
    • git diff --check: pass.
  • No migrations: confirmed.
  • No RBAC changes: confirmed.
  • No Customer Review Workspace product logic: confirmed; only shell/filter-scope behavior changed.
  • No asset changes: confirmed.
  • Follow-up: 312 can continue Customer Review Workspace v1 Completion against this shell contract.

Filament v5 Output Contract

  1. Livewire v4.0+ compliance: This app uses Livewire v4 with Filament v5; tests mount Filament Livewire pages/widgets.
  2. Provider registration: No panel provider registration changes. Laravel 11+/12 provider registration remains in apps/platform/bootstrap/providers.php.
  3. Global search: No globally searchable resource behavior changed. Provider Connection remains globally searchable disabled; Alert Delivery changes do not enable search.
  4. Destructive actions: No destructive actions added or changed.
  5. Assets: No assets added or changed; filament:assets deployment process unchanged.
  6. Testing: HTTP feature tests, Livewire component tests, and route category unit tests cover the contract.