TenantAtlas/specs/338-workspace-environment-resource-scope-contract/plan.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

7.2 KiB
Raw Permalink Blame History

Implementation Plan: Spec 338 - Workspace / Environment Resource Scope Contract

  • Branch: 338-workspace-environment-resource-scope-contract
  • Date: 2026-05-30
  • Spec: specs/338-workspace-environment-resource-scope-contract/spec.md
  • Input: User-provided Spec 338 draft + repo inspection for link/query seams.

Summary

Harden TenantPilots resource scope contract by tightening the canonical deep-link and query contract for workspace hubs and by eliminating first-party helper outputs that encode Filament internals (tableFilters[...]) as a product-level URL contract.

This is contract-first and targeted:

  • OperationRunLinks::index(..., operationType: ...) must stop emitting tableFilters[type][value].
  • Evidence scope special casing under /admin/evidence/* must be either proven real and contractual, or removed as stale ambiguity.
  • Environment-owned sidebar navigation must keep environment-owned entries primary and move workspace-wide/admin links into explicitly labeled cross-scope groups.
  • Baseline ownership/navigation is regression-only (Spec 320 already completed; do not reopen).

Technical Context

  • Language/Version: PHP 8.4.15, Laravel 12.52.x.
  • Primary Dependencies: Filament 5.2.x, Livewire 4.1.x, Pest 4.x, Tailwind CSS 4.x.
  • Storage: PostgreSQL; no schema change expected.
  • Testing: Pest Feature tests + minimal browser smoke only if navigation presentation is materially affected.
  • Validation Lanes: fast-feedback (Feature) + browser (smoke, scoped).
  • Target Platform: Sail locally; Dokploy/container deployment posture unchanged.
  • Project Type: Laravel monolith under apps/platform.
  • Constraints: No new persisted truth, migrations, packages, env vars, queue/scheduler changes, or route architecture rewrite.

UI / Surface Guardrail Plan

  • Guardrail scope: changed existing operator-facing scope/link behavior (navigation + deep links).
  • Affected surfaces:
    • Workspace hub links to Operations (OperationRunLinks and any CanonicalNavigationContext filter payload usage).
    • Evidence Overview hub + “clear environment context” redirect behavior.
    • Environment → workspace hub “filtered” links (environment_id must remain canonical).
    • Environment sidebar grouping for workspace-wide/admin links.
  • Native vs custom: native Filament + existing project navigation helpers; no custom UI framework.
  • Shared-family relevance: navigation entry points, scope presentation, deep links, hub filtering, OperationRun “view in collection” links.
  • State layers in scope: shell scope (route-owned), URL query contract, local table filter state (internal translation only).
  • Handling modes: review-mandatory.
  • Required tests / smoke:
    • Feature tests for URL contract + helper output.
    • Optional minimal browser smoke when sidebar/scope presentation changes are user-visible.
  • UI/Productization coverage: no new routes/pages expected; capture screenshots only when needed to prove a scope regression fix.

Shared Pattern & System Fit

  • Cross-cutting feature marker: yes.
  • Systems touched (expected):
    • apps/platform/app/Support/OperationRunLinks.php
    • apps/platform/app/Support/Navigation/CanonicalNavigationContext.php
    • apps/platform/app/Support/Navigation/AdminSurfaceScope.php
    • apps/platform/app/Support/Navigation/WorkspaceHubNavigation.php
    • apps/platform/app/Http/Controllers/ClearEnvironmentContextController.php
    • apps/platform/app/Support/Navigation/WorkspaceHubRegistry.php
    • apps/platform/app/Support/Navigation/WorkspaceSidebarNavigation.php
  • New abstraction introduced?: WorkspaceHubNavigation, a narrow helper for environment-surface hub grouping and explicit environment_id URL carry.
  • Shared abstractions reused: existing AdminSurfaceScope + hub registry + navigation context; do not create a second taxonomy framework.
  • Bounded deviation: if Filament requires tableFilters internally, keep it internal (page-level translation) and keep first-party helper output contract stable.

OperationRun UX Impact

Link semantics only (no new OperationRun types, no lifecycle changes):

  • Stop emitting Filament internals as deep-link contract for operation type filtering.
  • Decide between:
    1. operation_type=<canonical-code> accepted by Operations page and mapped to internal table state, or
    2. removing operation-type deep-linking entirely if safe mapping is not feasible without bloat.

Implementation Approach

Phase 1 — Repo truth + failing tests first

  • Inventory current first-party helper outputs and navigation contexts that emit:
    • tableFilters[...] (confirmed in OperationRunLinks; re-check CanonicalNavigationContext usage and call sites)
    • legacy /admin/evidence/* special casing branches (AdminSurfaceScope, ClearEnvironmentContextController)
  • Add failing tests that lock the desired contract:
    • OperationRunLinks::index(..., operationType: ...) must not contain tableFilters.
    • Evidence Overview is workspace hub; any /admin/evidence/* environment-scope handling is either intentional + tested or removed.
  • Change OperationRunLinks::index:
    • replace tableFilters[type][value] emission with a stable query key (operation_type) or remove operation-type deep linking.
  • Update the Operations page boundary to translate operation_type into internal table state where needed (keep environment_id canonical).

Phase 3 — Navigation context payload hygiene

  • Re-check CanonicalNavigationContext::toQuery() usage:
    • prefer keeping navigation metadata under nav[...] only,
    • avoid emitting additional top-level filter payload that encodes tableFilters for hub filtering when environment_id is sufficient.
  • Adjust the specific call sites (e.g. RelatedNavigationResolver contexts) that currently inject tableFilters[managed_environment_id] into query strings when linking to Operations.

Phase 4 — Evidence scope special casing

  • Verify actual route inventory for /admin/evidence/* beyond overview.
  • Remove stale classification or redirect rules only when route inventory proves they are not real, or explicitly document + test the remaining route family if it is still reachable.

Phase 5 — Validation and regression posture

  • Split Environment sidebar IA:
    • keep environment-owned resources in their domain groups,
    • move workspace hub entries into “Workspace-wide” on environment pages,
    • move workspace configuration/admin entries into “Workspace admin” on environment pages,
    • preserve explicit environment_id only for workspace hubs that already accept that filter.

Run narrow tests first:

  • cd apps/platform && ./vendor/bin/sail artisan test --compact <new/updated Spec 338 tests>
  • cd apps/platform && ./vendor/bin/sail pint --dirty --format agent
  • git diff --check

Run minimal browser smoke only if link/scope changes are user-visible in navigation:

  • cd apps/platform && php vendor/bin/pest tests/Browser --filter=Spec338 --compact

Deployment / Ops Impact

  • Migrations: none expected.
  • Env vars: none expected.
  • Queues/scheduler: none expected.
  • Filament assets: no new registered assets expected; filament:assets posture unchanged.