# Implementation Plan: Workspace-Owned Analysis Surface Registration & Shell Cutover **Branch**: `320-workspace-owned-analysis-surface-registration-shell-cutover` | **Date**: 2026-05-16 | **Spec**: [spec.md](/Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/320-workspace-owned-analysis-surface-registration-shell-cutover/spec.md) **Input**: Feature specification from `/specs/320-workspace-owned-analysis-surface-registration-shell-cutover/spec.md` **Preparation status**: Specification artifacts only. No runtime implementation has been performed by this preparation step. ## Summary Spec 320 hard-cuts workspace-owned analysis/library pages away from remembered Environment shell inheritance: ```text Workspace-owned analysis surface -> clean workspace route -> Workspace shell only -> no active Environment shell -> no remembered Environment fallback -> optional Environment filter only through canonical environment_id + visible chip ``` The implementation must classify the audited workspace-owned analysis surfaces from Spec 318, especially Baselines/Baseline Profiles, Baseline Snapshots, Baseline Compare Matrix, My Findings, Findings Intake, Findings Hygiene, and Cross-environment Compare. Baseline Compare remains Environment-owned from Spec 319. Alerts/Audit Log filter decisions remain Spec 321. ## Technical Context **Language/Version**: PHP 8.4.15, Laravel 12.52.0 **Primary Dependencies**: Filament 5.2.1, Livewire 4.1.4, Pest 4.3.1, Laravel Sail **Storage**: PostgreSQL; no schema changes planned **Testing**: Pest 4 / PHPUnit 12, Filament/Livewire tests, focused browser smoke **Validation Lanes**: fast-feedback, confidence, browser **Target Platform**: `apps/platform` Laravel/Filament admin panel, local Sail, staging/production through Dokploy **Project Type**: Web application **Performance Goals**: No material performance change. Classification and shell resolution must remain cheap request-time logic. **Constraints**: No migrations, seeders, packages, env vars, queues, scheduler, storage, deployment assets, compatibility routes, redirects, or legacy query alias support. **Scale/Scope**: Focused shell/context classification for workspace-owned analysis surfaces plus targeted regressions for Specs 314-319. ## UI / Surface Guardrail Plan - **Guardrail scope**: route/shell/query/copy/navigation behavior for existing operator-facing workspace-owned analysis pages. - **Native vs custom classification summary**: Existing Filament Resources/Pages and Blade views. No styling redesign. - **Shared-family relevance**: navigation, shell context, breadcrumbs, filter chips, clear actions, OperationRun links. - **State layers in scope**: shell, page, record/detail, URL query, Filament table filter state, Livewire referer classification. - **Audience modes in scope**: operator-MSP and support-platform. - **Decision/diagnostic/raw hierarchy plan**: Workspace ownership must be default-visible; Environment context may appear as data/filter state only. - **Raw/support gating plan**: no raw/support evidence exposure change. - **One-primary-action / duplicate-truth control**: The shell is the single ownership signal; page filters/selectors do not become shell ownership. - **Handling modes by drift class or surface**: hard-stop for remembered Environment shell on in-scope clean URLs; review-mandatory for any newly discovered workspace analysis route. - **Repository-signal treatment**: Spec 318 M2/M4 are implementation inputs; pages outside those findings are inspect-only unless repo evidence proves same mismatch. - **Special surface test profiles**: `global-context-shell`. - **Required tests or manual smoke**: classifier tests, URL/query tests, Livewire/Feature render tests, browser smoke for workspace origin, Environment origin, reload, and back/forward. - **Exception path and spread control**: A distinct workspace-owned analysis classifier is allowed only if `WorkspaceHubRegistry` would overstate hub/filter behavior. Document the choice in implementation close-out. - **Active feature PR close-out entry**: Guardrail / Exception / Smoke Coverage. ## Shared Pattern & System Fit - **Cross-cutting feature marker**: yes. - **Systems touched**: `AdminSurfaceScope`, `WorkspaceHubRegistry`, `WorkspaceSidebarNavigation`, `WorkspaceHubEnvironmentFilter`, `WorkspaceHubFilterStateResetter`, `WorkspaceScopedEnvironmentRoutes`, `ManagedEnvironmentLinks`, `OperateHubShell`, baseline resources/pages, findings analysis pages, cross-environment compare page, tests, browser smoke artifacts. - **Shared abstractions reused**: existing workspace hub shell contract, `AdminSurfaceScope`, `WorkspaceHubRegistry` query cleaning, `WorkspaceHubEnvironmentFilter`, `CanonicalAdminEnvironmentFilterState`, and current route/link helpers. - **New abstraction introduced? why?**: none preferred. If necessary, add the narrowest possible `AdminSurfaceScope` classification for workspace-owned analysis surfaces to force environmentless shell without implying full workspace-hub behavior. - **Why the existing abstraction was sufficient or insufficient**: `WorkspaceHubRegistry` works for registered hubs; generic `WorkspaceScoped` is insufficient because it allows remembered Environment restore. - **Bounded deviation / spread control**: Do not introduce a general surface taxonomy beyond the audited route list. Do not register pages as hubs unless they truly satisfy hub/filter/clear behavior. ## OperationRun UX Impact - **Touches OperationRun start/completion/link UX?**: yes, as regression only for existing baseline capture/compare actions. - **Central contract reused**: `OperationUxPresenter`, `OpsUxBrowserEvents`, `OperationRunLinks`, current services/jobs. - **Delegated UX behaviors**: existing queued toast, run link, browser event, authorization, and audit behavior remain unchanged. - **Surface-owned behavior kept local**: existing initiation inputs/actions on Baseline Profile and Baseline Compare Matrix. - **Queued DB-notification policy**: N/A. - **Terminal notification path**: Existing central lifecycle only. - **Exception path**: none. ## Provider Boundary & Portability Fit - **Shared provider/platform boundary touched?**: yes. - **Provider-owned seams**: Microsoft/Intune baseline content, provider IDs, Graph-backed snapshot evidence, compare strategy internals. - **Platform-core seams**: Workspace/Environment route ownership, shell classification, query key semantics, navigation ownership language. - **Neutral platform terms / contracts preserved**: Workspace, Environment, workspace-owned analysis surface, workspace hub, filtered workspace hub, Environment-owned page. - **Retained provider-specific semantics and why**: Baseline content remains Microsoft/Intune-shaped because it is current provider implementation truth. - **Bounded extraction or follow-up path**: Spec 321 and Spec 322 remain follow-ups; no provider abstraction work here. ## Constitution Check *GATE: Must pass before implementation. Re-check after runtime changes.* - Inventory-first: no inventory/snapshot truth change. Baseline snapshots remain immutable evidence/artifacts. - Read/write separation: no new write behavior. Existing capture/compare/preflight actions keep confirmation, authorization, audit, and OperationRun semantics. - Graph contract path: no new Graph calls; page render must remain DB-only. - Deterministic capabilities: existing capability checks remain; route/shell classification must be testable. - RBAC-UX: non-member / not entitled Workspace or Environment access remains deny-as-not-found where applicable; member missing capability follows existing behavior. - Workspace isolation: current Workspace remains the primary shell and authorization boundary. - Tenant/Environment isolation: Environment-owned rows remain scoped by accessible environments; optional filters must resolve only inside current Workspace and entitlement. - Run observability: existing baseline operation runs remain observable; no new operation type. - Test governance: lane purpose, heavy/browser visibility, fixture cost, and reviewer handoff are explicit. - Proportionality: possible classifier addition is justified by current browser-proven shell mismatch and bounded route list. - No premature abstraction: prefer extending existing classifier/registry paths over a new framework. - Persisted truth: no tables/entities/artifacts added. - Behavioral state: no new business state/status/reason family. - UI semantics: no new badge/status taxonomy; direct shell/page/filter truth only. - Shared pattern first: reuse workspace hub and shell resolution contracts. - Provider boundary: no provider tenant ID alias or fallback becomes platform shell truth. - V1 explicitness / few layers: hard cutover, no compatibility layer. - Filament-native UI: no ad-hoc styling or published internals. - Filament v5 / Livewire v4: Livewire 4.1.4 satisfies Filament v5 requirement; no Livewire v3 APIs. - Provider registration: Laravel 12 panel providers remain in `apps/platform/bootstrap/providers.php`; no provider registration work planned. - Global search: no global search behavior should change. Baseline Profile and Baseline Snapshot global search remain disabled unless implementation explicitly verifies a safe View/Edit route and updates tests. - Destructive/high-impact actions: no new destructive action. Existing archive/capture/compare/preflight actions must keep `->action(...)`, confirmation where required, authorization, audit, notifications, and tests. - Asset strategy: no Filament assets planned; no new `filament:assets` deployment requirement. ## Test Governance Check - **Test purpose / classification by changed surface**: Unit for classifier/registry; Feature/Livewire for shell/access/filter behavior; Browser for integrated shell/sidebar/reload/history. - **Affected validation lanes**: fast-feedback, confidence, browser. - **Why this lane mix is the narrowest sufficient proof**: The defect is both classification logic and browser-visible shell drift. - **Narrowest proving command(s)**: - `cd apps/platform && ./vendor/bin/sail artisan test --filter=AdminSurfaceScope` - `cd apps/platform && ./vendor/bin/sail artisan test --filter=WorkspaceHubRegistry` - `cd apps/platform && ./vendor/bin/sail artisan test --filter=WorkspaceOwnedAnalysis` - `cd apps/platform && ./vendor/bin/sail artisan test --filter=BaselineCompareEnvironmentRouteContract` - `cd apps/platform && ./vendor/bin/sail artisan test --filter=WorkspaceHubEnvironmentFilterContract` - focused Spec 320 browser smoke. - **Fixture / helper / factory / seed / context cost risks**: workspace/environment/member plus small baseline/finding fixtures; keep any full browser fixture explicit and named. - **Expensive defaults or shared helper growth introduced?**: no. If a helper is needed, make expensive setup opt-in. - **Heavy-family additions, promotions, or visibility changes**: explicit browser smoke only; no broad discovery guard in this spec. - **Surface-class relief / special coverage rule**: `global-context-shell`. - **Closing validation and reviewer handoff**: prove clean URLs, filtered URLs, legacy aliases, remembered fallback rejection, Baseline Compare regression, Decision Register regression, and browser screenshots. - **Budget / baseline / trend follow-up**: none expected; document if browser smoke runtime grows materially. - **Review-stop questions**: Are any workspace-owned analysis URLs still generic `WorkspaceScoped` with remembered restore? Did any page get registered as a hub without supporting hub/filter/clear behavior? Did legacy aliases return? - **Escalation path**: document-in-feature. - **Active feature PR close-out entry**: Guardrail / Exception / Smoke Coverage. - **Why no dedicated follow-up spec is needed**: This is the dedicated follow-up for Spec 318 M2/M4. Alerts/Audit and durable browser guard already have 321/322. ## Project Structure ### Documentation (this feature) ```text specs/320-workspace-owned-analysis-surface-registration-shell-cutover/ +-- spec.md +-- plan.md +-- tasks.md +-- checklists/ +-- requirements.md ``` ### Source Code (likely affected during later implementation) ```text apps/platform/app/ +-- Support/Navigation/ | +-- AdminSurfaceScope.php | +-- WorkspaceHubRegistry.php | +-- WorkspaceSidebarNavigation.php +-- Support/OperateHub/OperateHubShell.php +-- Support/ManagedEnvironmentLinks.php +-- Filament/Resources/ | +-- BaselineProfileResource.php | +-- BaselineSnapshotResource.php +-- Filament/Pages/ | +-- BaselineCompareMatrix.php | +-- CrossEnvironmentComparePage.php | +-- Findings/ | +-- MyFindingsInbox.php | +-- FindingsIntakeQueue.php | +-- FindingsHygieneReport.php +-- Filament/Concerns/WorkspaceScopedEnvironmentRoutes.php apps/platform/tests/ +-- Unit/Tenants/AdminSurfaceScopeTest.php +-- Unit/Support/OperateHub/OperateHubShellResolutionTest.php +-- Feature/Navigation/WorkspaceHubRegistryTest.php +-- Feature/Navigation/WorkspaceHubEnvironmentFilterContractTest.php +-- Feature/Filament/ | +-- BaselineCompareEnvironmentRouteContractTest.php | +-- BaselineProfile*.php | +-- BaselineSnapshot*.php +-- Feature/Findings/ | +-- MyFindingsInboxNavigationContextTest.php | +-- FindingsIntakeQueueNavigationContextTest.php +-- Browser/ +-- Spec320WorkspaceOwnedAnalysisSurfaceSmokeTest.php ``` **Structure Decision**: Use existing Laravel/Filament app structure. Do not create new base folders. Add test files only where the existing test families already live, or extend existing focused files when lower churn is clearer. ## Complexity Tracking | Violation | Why Needed | Simpler Alternative Rejected Because | |-----------|------------|-------------------------------------| | Possible new `AdminSurfaceScope` value | Generic `WorkspaceScoped` allows remembered Environment restore; workspace hubs already force environmentless shell but not every analysis page is a hub | Blindly adding every page to `WorkspaceHubRegistry` could falsely imply hub/filter/clear behavior | ## Proportionality Review - **Current operator problem**: Workspace-owned pages can appear Environment-owned in the shell with no filter chip or route ownership. - **Existing structure is insufficient because**: `WorkspaceScoped` restores remembered Environment; `WorkspaceHubRegistry` is reserved for hub-like surfaces. - **Narrowest correct implementation**: Classify a route list or narrow scope category as workspace-owned analysis and make it force environmentless shell. - **Ownership cost created**: Focused classifier tests, URL/filter tests, browser smoke, and implementation close-out documentation. - **Alternative intentionally rejected**: Keep generic fallback, add legacy aliases, or add all pages to workspace hub registry regardless of actual hub behavior. - **Release truth**: Current-release cleanup before production. ## Implementation Phases ### Phase 0 - Repo Verification Re-read Spec 318 artifacts and current code paths. Confirm the final in-scope list before runtime edits: - Baselines/Baseline Profiles. - Baseline Profile view/edit/create if applicable. - Baseline Compare Matrix. - Baseline Snapshots. - My Findings, Findings Intake, Findings Hygiene. - Cross-environment Compare. - Any other Spec 318 unregistered workspace analysis surface still present in routes/navigation. ### Phase 1 - Tests First Add focused tests that fail on current remembered Environment inheritance: - `AdminSurfaceScope` classification for in-scope paths. - Shell resolution with remembered Environment present. - Clean URL opens without active Environment. - Legacy alias rejection. - Optional `environment_id` filter behavior where supported. - Baseline Compare and Decision Register regressions. ### Phase 2 - Classification / Registry Cutover Implement the narrowest classifier mechanism: - Prefer existing registry/classifier extensions. - Add a distinct workspace-owned analysis surface classification only if needed. - Ensure `forcesEnvironmentlessShellContext()` returns true for these paths. - Ensure `allowsRememberedEnvironmentRestore()` is false for these paths. - Preserve Workspace state and authorization. ### Phase 3 - Page / Link / Copy Alignment Update only in-scope surfaces: - Sidebar/global/workspace links emit clean workspace URLs unless explicit `environment_id` filter is supported. - Environment-origin links do not carry active Environment shell ownership. - Headers, breadcrumbs, empty states, and copy use workspace/library/work-queue wording. - Existing Environment columns, selectors, badges, and filters remain data/filter state. ### Phase 4 - Regression and Browser Proof Run focused tests and browser smoke: - Baselines workspace origin, environment origin, reload. - Baseline Snapshots workspace origin, environment origin, reload. - Baseline Compare Matrix direct/reload/back-forward. - My Findings/Intake/Hygiene and Cross-environment Compare direct/from Environment context. - Baseline Compare Environment-owned regression. - Decision Register workspace hub/filtered hub regression. Save screenshots under: ```text specs/320-workspace-owned-analysis-surface-registration-shell-cutover/artifacts/screenshots/ ``` ### Phase 5 - Close-Out Document: - classification outcomes for Baselines/Baseline Snapshots. - unregistered workspace analysis pages fixed or excluded. - whether any page was reclassified Environment-owned. - how remembered Environment inheritance was prevented. - tests and browser verification. - no migrations/packages/env vars/queues/scheduler/storage/assets. - no backwards compatibility or legacy alias support. ## Rollout Considerations - No database migration or data backfill. - No production compatibility burden because pre-production hard cutover is the standing policy. - No Dokploy runtime change. - No new `filament:assets` deployment step. - Staging validation should run focused tests and browser smoke before promotion. ## Risk Controls - Keep route list explicit and tested. - Do not generalize classifier beyond audited need. - Do not add Environment filter feature work unless a page already has product need and visible chip/clear behavior can be proven. - Replace old tests that assert broken remembered fallback; do not keep them as compatibility coverage. - Keep Spec 321 and Spec 322 out of scope.