TenantAtlas/specs/315-environment-cta-explicit-filter-contract/plan.md
ahmido eced9ad50c Spec 315: implement environment CTA explicit filter contract (#370)
## Summary
- hard-cut environment-owned CTA links into workspace hubs to canonical `environment_id` filters
- add shared workspace-hub environment filter resolution and visible filtered-state rendering across in-scope hubs
- update workspace hub pages, link helpers, and focused test coverage for explicit environment CTA filtering

## Validation
- Not run in this workflow

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #370
2026-05-16 11:50:20 +00:00

21 KiB

Implementation Plan: Environment CTA Explicit Filter Contract

Branch: 315-environment-cta-explicit-filter-contract | Date: 2026-05-16 | Spec: spec.md
Input: Feature specification from /specs/315-environment-cta-explicit-filter-contract/spec.md

Preparation status: Specification artifacts only. No runtime implementation has been performed by this preparation step.

Summary

Spec 315 hard-cuts Environment-owned CTA links into workspace hubs to one canonical explicit filter contract:

Environment Dashboard / Environment-owned CTA -> Workspace Hub ?environment_id={managed_environment_id}

Workspace hubs remain workspace-scoped. environment_id is resolved only inside the selected Workspace, represented as a visible page-level Environment filter chip, applied to data where valid, and removable through a clean hub URL. Legacy query keys such as tenant, tenant_id, managed_environment_id, tenant_scope, environment, and tableFilters are not accepted as valid Environment CTA filter state.

Technical Context

Language/Version: PHP 8.4.15, Laravel 12.52.0
Primary Dependencies: Filament 5.2.1, Livewire 4.1.4, Laravel Sail, Laravel Socialite, Laravel MCP
Storage: PostgreSQL; no schema changes for this spec
Testing: Pest 4.3.1 / PHPUnit 12.4.4; focused browser smoke where applicable
Validation Lanes: fast-feedback, confidence for existing related coverage, browser for focused rendered-state verification
Target Platform: Laravel admin application under apps/platform, local development through Sail, staging/production through Dokploy
Project Type: Web application, Laravel/Filament admin panel
Performance Goals: No material performance change. Environment filter resolution is one workspace-scoped Managed Environment lookup per relevant request/page mount and normal query narrowing thereafter.
Constraints: No migrations, seeders, new packages, env vars, queues, scheduler, or storage changes. No backward compatibility layer or dual-param support.
Scale/Scope: Cross-cutting runtime contract across critical workspace hubs and shared Environment-owned CTA/link helpers.

UI / Surface Guardrail Plan

  • Guardrail scope: Changed operator-facing surfaces for visible Environment filter state and CTA query contracts.
  • Native vs custom classification summary: Native Filament/Livewire pages/resources with a shared Blade partial or existing shared primitive for a small chip. No redesign.
  • Shared-family relevance: Navigation entry points, dashboard CTAs, scope signals, filter summaries, and clean clear links.
  • State layers in scope: URL query, page state, table/list query state, rendered page header/filter chip, shell context. Full persisted/session/deferred clear semantics are deferred to Spec 316.
  • Audience modes in scope: Operator-MSP and support-platform. Customer-read-only applies only where Customer Review Workspace currently exposes a customer-safe workspace surface.
  • Decision/diagnostic/raw hierarchy plan: Filter truth is default-visible; existing diagnostics/details remain on demand.
  • Raw/support gating plan: No raw evidence exposure changes.
  • One-primary-action / duplicate-truth control: The visible Environment filter chip is the single page-level truth for explicit Environment filter state; shell remains Workspace.
  • Handling modes by drift class or surface: Hard-stop for legacy alias acceptance in Environment CTA filter behavior; review-mandatory for any page-specific exception.
  • Repository-signal treatment: Contract tests and browser screenshots are required evidence.
  • Special surface test profiles: global-context-shell, monitoring-state-page, standard-native-filament.
  • Required tests or manual smoke: Functional-core URL/filter tests, state-contract tests, sidebar regression tests, and focused browser smoke.
  • Exception path and spread control: Any page discovered not to be workspace-owned or Environment-filterable must be documented as excluded instead of receiving a partial local exception.
  • Active feature PR close-out entry: Guardrail and Smoke Coverage.

Shared Pattern & System Fit

  • Cross-cutting feature marker: yes.
  • Systems touched: WorkspaceHubRegistry, WorkspaceSidebarNavigation, ManagedEnvironmentLinks, OperationRunLinks, CanonicalAdminTenantFilterState only if immediate clean-entry correctness requires it, Filament pages/resources for critical hubs, shared views under resources/views/filament, and related tests.
  • Shared abstractions reused: WorkspaceHubRegistry for known hub paths and clean hub URL generation; existing page/table query patterns; existing workspace and Managed Environment models/factories.
  • New abstraction introduced? why?: A narrow WorkspaceHubEnvironmentFilter resolver/helper is expected because multiple pages currently parse different keys and identifier types.
  • Why the existing abstraction was sufficient or insufficient: Spec 314 registry is sufficient for clean sidebar/global entry but intentionally treats Environment-like query params as forbidden for that clean-entry contract. Spec 315 needs a separate valid explicit CTA filter path.
  • Bounded deviation / spread control: The resolver reads only environment_id, validates only inside current Workspace, and does not become a shell/context manager or compatibility adapter.

OperationRun UX Impact

  • Touches OperationRun start/completion/link UX?: yes, link semantics only.
  • Central contract reused: Existing OperationRunLinks URL helper.
  • Delegated UX behaviors: Tenant/workspace-safe URL resolution for OperationRun links into workspace hubs.
  • Surface-owned behavior kept local: OperationRun start, completion, status, notifications, and artifact behavior remain unchanged.
  • Queued DB-notification policy: N/A.
  • Terminal notification path: N/A.
  • Exception path: none.

Provider Boundary & Portability Fit

  • Shared provider/platform boundary touched?: yes.
  • Provider-owned seams: Existing provider connection data and provider external tenant IDs stay provider-adjacent model data.
  • Platform-core seams: Workspace hub query contract, visible scope wording, Environment filter resolver, and CTA URL generation.
  • Neutral platform terms / contracts preserved: Workspace, Environment, Environment filter, environment_id.
  • Retained provider-specific semantics and why: Existing Microsoft/Intune provider connection internals remain as-is. They must not become the CTA filter identifier.
  • Bounded extraction or follow-up path: Broader tenant/environment naming cleanup is deferred to Spec 317.

Constitution Check

GATE: Must pass before implementation. Re-check after runtime changes.

  • Inventory-first: no Graph inventory semantics change.
  • Read/write separation: no Graph writes or destructive operations are added.
  • Graph contract path: no Graph calls are introduced.
  • Deterministic capabilities: existing capability checks remain; new tests must assert workspace/environment access boundaries.
  • RBAC-UX: workspace hub authorization remains workspace/capability-based. Cross-workspace Environment IDs are 404/safe no-access; member-missing-capability remains existing 403 behavior.
  • Workspace isolation: Environment filter resolution is explicitly scoped to current Workspace and must not switch workspace.
  • Tenant isolation: no cross-Workspace/Environment leakage; no provider external tenant ID lookup for CTA filter state.
  • Run observability: no new OperationRun lifecycle behavior. URL helpers only.
  • Test governance (TEST-GOV-001): lane, fixture cost, heavy-family browser coverage, and reviewer handoff are explicit in this plan and tasks.
  • Proportionality (PROP-001): new resolver/helper is justified by current-release cross-page drift and at least two concrete surfaces.
  • No premature abstraction (ABSTR-001): the resolver is bounded to one query key and does not become a generalized context framework.
  • Persisted truth (PERSIST-001): no persisted truth is added.
  • Behavioral state (STATE-001): no new state/status family is added.
  • UI semantics (UI-SEM-001): visible chip is direct domain-to-UI mapping, not a new taxonomy.
  • Shared pattern first (XCUT-001): extend Spec 314 hub registry and existing visible filter summaries before local patterns.
  • Provider boundary (PROV-001): platform CTA filter uses Managed Environment database ID, not provider external ID.
  • V1 explicitness / few layers: direct hard cutover, small helper, page-local data query application where appropriate.
  • Spec discipline / bloat check: explicit non-goals, follow-up specs 316/317/318, and no compatibility seam.
  • Filament-native UI (UI-FIL-001): use native Filament/Livewire and shared partials; no ad-hoc styling system.
  • UI/UX scope, truth, and naming: scope signals must distinguish Workspace shell from explicit Environment page filter.
  • UI naming: user-facing wording uses Environment, not Tenant, for the filter chip.

Test Governance Check

  • Test purpose / classification by changed surface: Unit/Feature for resolver and page contracts; Browser for integrated shell/header/chip URL flows.
  • Affected validation lanes: fast-feedback, confidence for existing related regressions, browser for focused smoke.
  • Why this lane mix is the narrowest sufficient proof: Unit/feature tests prove URL key, resolver, data scope, and clear-link behavior. Browser tests are required because the defect is also visible UI/shell truth.
  • Narrowest proving command(s):
    • cd apps/platform && ./vendor/bin/sail artisan test --filter=WorkspaceHubEnvironmentFilter
    • cd apps/platform && ./vendor/bin/sail artisan test --filter=EnvironmentCta
    • cd apps/platform && ./vendor/bin/sail artisan test --filter=WorkspaceHub
    • focused browser smoke command or manual browser verification documented in close-out
  • Fixture / helper / factory / seed / context cost risks: Existing Workspace, ManagedEnvironment, membership/capability, operation/review/evidence/finding/provider connection factories may need small focused setup. Avoid broad global seeders.
  • Expensive defaults or shared helper growth introduced?: no; browser smoke remains explicit.
  • Heavy-family additions, promotions, or visibility changes: Focused Spec 315 browser smoke only.
  • Surface-class relief / special coverage rule: Native Filament page/resource tests for pages/resources; browser smoke for rendered shell/context.
  • Closing validation and reviewer handoff: Reviewers must verify test commands, screenshot artifacts, no legacy alias acceptance, no compatibility adapter, and no scope drift from Spec 314.
  • Budget / baseline / trend follow-up: none expected.
  • Review-stop questions: Does any page still parse tenant or managed_environment_id as CTA filter? Does any CTA emit a legacy key? Does any filtered hub set shell Environment ownership? Does any clean sidebar link retain filter state?
  • Escalation path: Follow-up Spec 316 only if implementation reveals broader persisted clear-state behavior is required for correctness beyond clean URL entry.
  • Active feature PR close-out entry: Guardrail and Smoke Coverage.
  • Why no dedicated follow-up spec is needed: The canonical CTA filter contract is the dedicated follow-up to Spec 314. Clear internals, legacy cleanup, and durable browser guard infrastructure already have follow-up specs 316, 317, and 318.

Project Structure

Documentation (this feature)

specs/315-environment-cta-explicit-filter-contract/
|-- spec.md
|-- plan.md
|-- tasks.md
|-- checklists/
|   `-- requirements.md
`-- artifacts/
    `-- screenshots/        # created during implementation/browser verification if useful

No research.md, data-model.md, quickstart.md, or contracts/ artifact is required for preparation because this feature introduces no data model, external API contract, or new workflow API. Runtime implementation may add notes only if a classification/exclusion needs durable documentation inside this spec folder.

Source Code (repository root)

Likely runtime files to inspect or update during implementation:

apps/platform/app/Support/Navigation/WorkspaceHubRegistry.php
apps/platform/app/Support/Navigation/WorkspaceHubEnvironmentFilter.php
apps/platform/app/Support/Navigation/WorkspaceSidebarNavigation.php
apps/platform/app/Support/ManagedEnvironmentLinks.php
apps/platform/app/Support/Operations/OperationRunLinks.php
apps/platform/app/Support/Filament/CanonicalAdminTenantFilterState.php
apps/platform/app/Filament/Pages/Monitoring/Operations.php
apps/platform/app/Filament/Pages/Monitoring/EvidenceOverview.php
apps/platform/app/Filament/Pages/Governance/GovernanceInbox.php
apps/platform/app/Filament/Pages/Governance/DecisionRegister.php
apps/platform/app/Filament/Pages/Governance/FindingExceptionsQueue.php
apps/platform/app/Filament/Pages/Reviews/ReviewRegister.php
apps/platform/app/Filament/Pages/Reviews/CustomerReviewWorkspace.php
apps/platform/app/Filament/Resources/ProviderConnectionResource.php
apps/platform/resources/views/filament/
apps/platform/tests/Feature/
apps/platform/tests/Browser/

Potential classification-only inspection areas:

apps/platform/app/Filament/Pages/Monitoring/AuditLog.php
apps/platform/app/Filament/Resources/Alert*
apps/platform/app/Filament/Pages or Resources for Reports / Stored Reports
apps/platform/app/Filament/Pages or Resources for Support Requests

Structure Decision: Laravel/Filament platform app under apps/platform; new source files, if any, stay in existing app/Support and resources/views/filament locations. Tests stay in existing Pest feature/browser test directories.

Complexity Tracking

Violation Why Needed Simpler Alternative Rejected Because
New resolver/helper abstraction Environment CTA filtering spans many workspace hubs and currently has divergent key/identifier semantics Page-local parsing or dual-param adapters would preserve drift and violate the hard-cutover policy
Shared visible chip partial or primitive Filter state must be visibly consistent across hubs Duplicated per-page chip markup would increase scope wording drift

Phase 0: Discovery Completed During Preparation

Relevant repository facts discovered before authoring this plan:

  • Spec 313 is an audit-only baseline and explicitly recommends Spec 315 for Environment CTA explicit filter standardization.
  • Spec 314 has completed tasks and latest local history indicates the workspace hub navigation context contract was merged before this branch.
  • WorkspaceHubRegistry currently centralizes hub paths and clean URL behavior from Spec 314.
  • ManagedEnvironmentLinks and OperationRunLinks currently have Environment-owned link helpers that use legacy managed_environment_id semantics in some cases.
  • Operations currently has managed_environment_id, tenant_scope, and table filter state related to Environment prefiltering.
  • Governance Inbox and Decision Register currently parse managed_environment_id and tenant and render a visible ManagedEnvironment filter summary.
  • Finding Exceptions Queue currently uses tenant query state and managed_environment_id table filter naming.
  • Evidence Overview and Customer Review Workspace currently have Environment-like query/filter behavior that must hard-cut to environment_id.
  • Provider Connections currently uses managed_environment_id query behavior around provider/external identifiers; implementation must avoid provider external tenant ID as canonical CTA filter key.

Technical Approach

1. Canonical resolver

Add or reuse a narrow helper such as:

apps/platform/app/Support/Navigation/WorkspaceHubEnvironmentFilter.php

Responsibilities:

  • read only environment_id
  • validate scalar/integer-like value safely
  • resolve ManagedEnvironment by primary key inside the current Workspace
  • return null/no filter when environment_id is absent
  • abort with 404 or existing safe no-access when environment_id is present but invalid or cross-workspace
  • expose display name, ID, clear URL, and query application helpers
  • never read legacy params, remembered Environment, provider external tenant ID, slug, or Filament::getTenant()

2. Workspace hub registry alignment

Keep Spec 314 clean-entry behavior. Add a safe way for Environment-owned CTA URL builders and pages to generate or consume an explicit environment_id filter without weakening sidebar/global clean URL checks.

Review point:

  • environment_id may remain forbidden for clean sidebar/global entry while being allowed for explicit CTA filter entry through the resolver. The implementation must keep those two modes separate.

3. CTA URL hard cutover

Update Environment-owned link helpers and CTAs:

  • Environment Dashboard cards/header actions
  • Environment Governance Overview cards
  • Provider readiness/onboarding links
  • Required permissions and permission posture links
  • Recent Operations and OperationRun links
  • ManagedEnvironment link helpers
  • Review, evidence, support, report, provider connection helpers where they target workspace hubs

Output must use:

?environment_id={managed_environment_id}

and must not output:

tenant
tenant_id
managed_environment_id
tenant_scope
environment
tableFilters

4. Visible filter chip

Create or reuse a shared partial/primitive such as:

apps/platform/resources/views/filament/partials/workspace-hub-environment-filter-chip.blade.php

Required behavior:

  • render only for a valid environment_id filter
  • show Environment filter: {environment display name} or equivalent
  • include Clear filter
  • point clear link to the clean workspace hub URL
  • use calm enterprise styling consistent with existing Filament/Blade patterns
  • avoid Tenant, current tenant, or active shell Environment wording

5. Apply per workspace hub

For each critical hub:

  • read only the shared resolver
  • apply filter only if valid
  • render shared chip
  • update header/scope text to avoid misleading All environments
  • keep clean URL workspace-wide
  • keep shell Workspace-scoped
  • add/update tests proving canonical behavior and legacy param rejection

6. Classify conditional hubs

Audit Log, Alerts, Reports/Stored Reports, and Support Requests must be classified:

  • if workspace-owned and Environment-filterable: implement the same contract
  • if not Environment-filterable: do not pass Environment filters through CTAs and document exclusion in implementation close-out

Data Model

No data model changes.

No migrations, seeders, backfills, stored URL migrations, compatibility transforms, retention changes, queues, scheduler, or storage volume changes.

Security and RBAC

  • Existing page authorization remains the access gate.
  • Environment filter narrows visible data; it never grants access.
  • Cross-workspace Environment IDs must return 404 or safe no-access.
  • No shell context switch or implicit workspace switch is allowed.
  • No provider external tenant ID lookup is allowed for this CTA filter.
  • No secrets or Graph payload changes.

Deployment / Operations

  • No new environment variables.
  • No migrations.
  • No queues or scheduler changes.
  • No storage persistence or volume changes.
  • No package changes.
  • No Dokploy-specific deployment changes expected.
  • If implementation registers Filament assets, deployment must include cd apps/platform && php artisan filament:assets; no registered assets are expected for the planned shared chip.

Browser Verification Plan

Use the in-app browser or existing browser test tooling after runtime changes.

Critical hubs:

  • Operations
  • Governance Inbox
  • Decision Register
  • Finding Exceptions Queue
  • Provider Connections
  • Evidence
  • Reviews
  • Customer Reviews

Flows:

  1. Environment Dashboard CTA -> filtered workspace hub: URL has environment_id, no legacy params, workspace shell, visible chip, no misleading primary All environments, clear target is clean.
  2. Clean sidebar/global regression: URL has no environment_id or legacy params, no chip, workspace-wide state.
  3. Clear link smoke: clicking clear navigates to clean URL, chip disappears, workspace-wide state is visible.

Screenshots may be saved under:

specs/315-environment-cta-explicit-filter-contract/artifacts/screenshots/

Implementation Notes

  • Use direct hard cuts.
  • Do not add compatibility middleware or adapter layers.
  • Do not update tests by broad rebaseline; only change tests that asserted broken legacy behavior.
  • Avoid deep clear-filter work unless immediate clean URL correctness breaks; move deeper clearing to Spec 316.
  • Document any intentional page exclusions in the final implementation report.