TenantAtlas/specs/315-environment-cta-explicit-filter-contract/spec.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

41 KiB

Feature Specification: Environment CTA Explicit Filter Contract

Feature Branch: 315-environment-cta-explicit-filter-contract
Created: 2026-05-16
Status: Draft
Input: User supplied Spec 315 draft for a hard cutover from legacy Environment CTA query aliases to canonical environment_id filters on workspace hubs.

Spec Candidate Check (mandatory - SPEC-GATE-001)

  • Problem: Environment-owned CTAs into workspace hubs can currently pass Environment focus through inconsistent query keys, hidden table state, or shell context, so operators cannot reliably tell whether a workspace hub is showing all environments or one explicit Environment filter.
  • Today's failure: A user can enter a workspace hub from an Environment Dashboard and see data filtered by one Environment while the URL, header, shell, or filter UI implies workspace-wide scope. Legacy keys such as tenant, managed_environment_id, tenant_scope, and table filter structures also allow future drift.
  • User-visible improvement: Environment Dashboard and Environment-owned CTA entry to workspace hubs uses one visible, explicit Environment filter. The URL, header/filter chip, data scope, and clear link all agree.
  • Smallest enterprise-capable version: Introduce one request-scoped resolver for environment_id, one shared visible chip pattern, and update the in-scope workspace hubs and Environment-owned CTA URL builders to use that contract.
  • Explicit non-goals: No universal clear-filter internals, no broad legacy tenant naming cleanup, no compatibility aliases, no migrations, no new runtime feature, no redesign, and no conversion of workspace hubs into Environment pages.
  • Permanent complexity imported: One narrow Environment filter resolver/helper, one shared filter chip partial or existing shared primitive usage, focused feature/browser tests, and updated CTA URL helpers. No persisted entity, enum, status family, queue, scheduler, package, or environment variable is introduced.
  • Why now: Spec 314 completed the sidebar/global clean-entry half of the navigation contract. This spec closes the complementary Environment CTA path before more workspace hubs and CTAs accumulate incompatible scope semantics.
  • Why not local: The behavior spans Operations, Governance Inbox, Decision Register, Finding Exceptions, Provider Connections, Evidence, Reviews, Customer Reviews, and shared link helpers. Page-local fixes would recreate the exact drift this spec is intended to stop.
  • Approval class: Core Enterprise.
  • Red flags triggered: Cross-cutting navigation/filter behavior and a new resolver/helper. Defense: the helper is bounded to one canonical query key and at least two concrete current surfaces already require identical semantics.
  • Score: Nutzen: 2 | Dringlichkeit: 2 | Scope: 2 | Komplexitaet: 1 | Produktnaehe: 2 | Wiederverwendung: 2 | Gesamt: 11/12
  • Decision: approve.

Spec Scope Fields (mandatory)

  • Scope: canonical-view.
  • Primary Routes: Workspace hub routes for Operations, Governance Inbox, Decision Register, Finding Exceptions Queue, Provider Connections / Integrations, Evidence Overview, Reviews / Review Register, Customer Review Workspace, and any Audit Log, Alerts, Reports, Stored Reports, or Support Requests workspace hub route that is confirmed Environment-filterable during implementation.
  • Data Ownership: Workspace hubs remain workspace-owned pages. Managed Environment is a workspace-owned secondary scope used only as an explicit filter. Existing rows that belong to managed_environment_id or equivalent Environment scope are narrowed only when a valid environment_id filter is present.
  • RBAC: Existing workspace membership and page capability checks remain authoritative. Environment filter resolution must confirm the selected Environment belongs to the current Workspace and that the user is allowed to access it. Cross-workspace Environment IDs return 404 or the existing safe no-access convention without leaking existence.

For canonical-view specs:

  • Default filter behavior when tenant-context is active: Clean sidebar/global workspace hub entry is workspace-wide as defined by Spec 314. Active shell Environment, remembered Environment, Filament tenant fallback, session filters, or legacy query keys must not create an Environment filter on clean workspace hub entry.
  • Explicit entitlement checks preventing cross-tenant leakage: environment_id is resolved as ManagedEnvironment::query()->whereKey($environment_id)->where('workspace_id', $currentWorkspace->id). No lookup by provider external tenant ID, slug, remembered Environment, or Filament::getTenant() is valid for this contract.

Cross-Cutting / Shared Pattern Reuse (mandatory)

  • Cross-cutting feature?: yes.
  • Interaction class(es): navigation entry points, dashboard CTA links, header/scope wording, list/table filters, visible filter chips, clean clear links, and browser smoke verification.
  • Systems touched: WorkspaceHubRegistry, workspace sidebar/global navigation contracts from Spec 314, ManagedEnvironmentLinks, OperationRunLinks, workspace hub Filament pages/resources, page Blade views, and relevant tests.
  • Existing pattern(s) to extend: Spec 314 clean workspace hub registry and URL-cleaning behavior; current page-level visible filter summaries in Governance Inbox, Decision Register, and Customer Review Workspace; existing Filament page/table query patterns.
  • Shared contract / presenter / builder / renderer to reuse: Reuse WorkspaceHubRegistry for clean hub URL generation and add or reuse a narrow WorkspaceHubEnvironmentFilter resolver plus one shared visible filter chip partial/primitive.
  • Why the existing shared path is sufficient or insufficient: Spec 314 defines clean sidebar/global entry and forbidden query detection. It intentionally does not define valid Environment CTA filter resolution or visible filtered-state rendering, so a small complementary resolver is needed.
  • Allowed deviation and why: Page-specific data filtering may stay local where each hub uses different query/table/in-memory data sources, but every page must consume the same canonical filter state and shared visible chip wording.
  • Consistency impact: Query key, identifier type, workspace validation, chip wording, clear link target, shell behavior, and legacy key rejection must stay aligned across all in-scope hubs.
  • Review focus: Verify no page adds dual-param support, no CTA emits legacy params, and no workspace hub uses remembered Environment or Filament tenant state as data scope.

OperationRun UX Impact (mandatory)

  • Touches OperationRun start/completion/link UX?: yes, link semantics only.
  • Shared OperationRun UX contract/layer reused: Existing OperationRunLinks where it links Environment-owned surfaces to Operations or related workspace hubs.
  • Delegated start/completion UX behaviors: Tenant/workspace-safe URL resolution only. No queued toast, run start, completion, terminal notification, or artifact behavior changes.
  • Local surface-owned behavior that remains: Operation initiation inputs and run lifecycle behavior remain unchanged.
  • Queued DB-notification policy: N/A.
  • Terminal notification path: N/A.
  • Exception required?: none.

Provider Boundary / Platform Core Check (mandatory)

  • Shared provider/platform boundary touched?: yes.
  • Boundary classification: mixed.
  • Seams affected: Provider Connections filter query keys, provider-adjacent Environment identifiers, workspace hub URL generation, and operator-facing scope wording.
  • Neutral platform terms preserved or introduced: Workspace, Environment, Workspace hub, Environment filter, Clear filter, environment_id.
  • Provider-specific semantics retained and why: Existing provider connection records and Managed Environment backing data stay unchanged. Provider external tenant IDs remain model data but are not valid CTA filter identifiers.
  • Why this does not deepen provider coupling accidentally: The canonical CTA filter uses the platform Managed Environment database identifier and workspace ownership, not Microsoft tenant IDs or provider-specific slugs.
  • Follow-up path: Spec 317 will handle broader legacy Tenant / Environment cleanup.

UI / Surface Guardrail Impact (mandatory)

Surface / Change Operator-facing surface change? Native vs Custom Shared-Family Relevance State Layers Touched Exception Needed? Low-Impact / N/A Note
Environment Dashboard and Environment-owned CTA links yes Existing Filament/Blade actions and link helpers navigation, dashboard CTAs URL-query no Link target query key changes to environment_id
Workspace hub header/filter area yes Native Filament page plus shared Blade partial or existing primitive scope signals, filter summary page, URL-query, table/list state no Adds visible Environment filter chip when active
Operations yes Existing Filament page/table monitoring/state page URL-query, page state, table query no Filter chip and canonical query support only
Governance Inbox yes Existing Filament page/table governance queue URL-query, page state, table query no Hard-cut existing visible filter to environment_id
Decision Register yes Existing Filament page/table governance decision register URL-query, page state, table query no Clean and filtered URLs both supported
Finding Exceptions Queue yes Existing Filament page/table exception queue URL-query, page state, table query no Legacy tenant query no longer accepted
Provider Connections / Integrations yes Existing Filament resource/list provider connections URL-query, resource/list query no No provider external tenant ID as CTA filter key
Evidence Overview yes Existing Filament page/list evidence viewer URL-query, page state, in-memory/list filtering no Document if rows remain in-memory filtered
Reviews / Review Register yes Existing Filament page/table review workflow URL-query, table query no Explicit Environment chip and clean clear link
Customer Review Workspace yes Existing Filament page/table customer-safe review workspace URL-query, table query no Remains workspace-scoped, not Environment-owned
Audit Log / Alerts / Reports / Support Requests classification maybe Existing pages/resources audit/alerts/reports/support URL-query where supported no Only retrofit if confirmed workspace-owned and Environment-filterable

Decision-First Surface Role (mandatory when operator-facing surfaces are changed)

Surface Decision Role Human-in-the-loop Moment Immediately Visible for First Decision On-Demand Detail / Evidence Why This Is Primary or Why Not Workflow Alignment Attention-load Reduction
Workspace hubs opened from Environment CTA Secondary Context Decide whether the listed workspace data is intentionally narrowed to one Environment Workspace name, page title, Environment filter chip, clear filter link, filtered records Existing record details, table filters, and row actions The hub remains the decision/workspace surface; the chip makes the inherited Environment focus truthful Aligns Environment Dashboard drilldowns with workspace hub workflows Removes hidden scope reconstruction from URL/table/session state
Clean sidebar/global workspace hub entry Secondary Context Decide from workspace-wide data after navigating globally Workspace-wide header/scope, no Environment chip, clean URL Existing filters and details This is the neutral workspace-wide entry from Spec 314 Keeps sidebar/global behavior stable Prevents stale Environment context from surprising operators

Audience-Aware Disclosure (mandatory when operator-facing surfaces are changed)

Surface Audience Modes In Scope Decision-First Default-Visible Content Operator Diagnostics Support / Raw Evidence One Dominant Next Action Hidden / Gated By Default Duplicate-Truth Prevention
Workspace hub Environment filter chip operator-MSP, support-platform, customer-read-only where Customer Review Workspace applies Environment filter: {display name} and Clear filter Existing table/list filters and row details Existing support/raw views, if any Clear filter or inspect rows Raw query/table/session internals The chip is the single visible truth for explicit Environment filter state

UI/UX Surface Classification (mandatory when operator-facing surfaces are changed)

Surface Action Surface Class Surface Type Likely Next Operator Action Primary Inspect/Open Model Row Click Secondary Actions Placement Destructive Actions Placement Canonical Collection Route Canonical Detail Route Scope Signals Canonical Noun Critical Truth Visible by Default Exception Type / Justification
Operations List / Table / Bulk Monitoring/state page Inspect operation or clear Environment filter Existing operations inspect pattern existing Existing table/page actions Existing placement; no destructive changes /admin/workspaces/{workspace}/operations existing Workspace plus Environment filter chip when active Operations / Operation Whether results are workspace-wide or Environment-filtered none
Governance Inbox List / Table / Bulk Queue Inspect governance item or clear Environment filter Existing inbox inspect pattern existing Existing table/page actions Existing placement; no destructive changes existing workspace hub URL existing Workspace plus Environment filter chip when active Governance Inbox Filtered Environment state none
Decision Register List / Table / Bulk Register Inspect decision or clear Environment filter Existing decision inspect pattern existing Existing table/page actions Existing placement; no destructive changes existing workspace hub URL existing Workspace plus Environment filter chip when active Decision Register Filtered Environment state none
Finding Exceptions Queue List / Table / Bulk Queue Inspect exception or clear Environment filter Existing exception inspect pattern existing Existing table/page actions Existing placement; no destructive changes existing workspace hub URL existing Workspace plus Environment filter chip when active Finding Exceptions Filtered Environment state none
Provider Connections List / Table / Bulk Registry Inspect connection or clear Environment filter Existing provider connection inspect pattern existing Existing table/resource actions Existing placement; no destructive changes existing workspace hub URL existing Workspace plus Environment filter chip when active Provider Connections Filtered Environment state none
Evidence Overview List / Table / Bulk Evidence viewer Inspect evidence or clear Environment filter Existing evidence inspect pattern existing Existing page actions Existing placement; no destructive changes existing workspace hub URL existing Workspace plus Environment filter chip when active Evidence Filtered Environment state none
Reviews / Customer Reviews List / Table / Bulk Review workspace Inspect review or clear Environment filter Existing review inspect pattern existing Existing page/table actions Existing placement; no destructive changes existing workspace hub URL existing Workspace plus Environment filter chip when active Reviews Filtered Environment state none

Operator Surface Contract (mandatory when operator-facing surfaces are changed)

Surface Primary Persona Decision / Operator Action Supported Surface Type Primary Operator Question Default-visible Information Diagnostics-only Information Status Dimensions Used Mutation Scope Primary Actions Dangerous Actions
Environment-filtered workspace hub TenantPilot operator Confirm whether the workspace hub is intentionally narrowed to one Environment, inspect rows, or clear the filter Existing list/table/page Am I looking at workspace-wide data or one Environment? Workspace context, Environment filter chip, clear link, filtered rows Existing detailed diagnostics on each page Existing page-specific dimensions only TenantPilot UI navigation/filter state only Clear filter, inspect rows None added or changed

Proportionality Review (mandatory when structural complexity is introduced)

  • New source of truth?: no.
  • New persisted entity/table/artifact?: no.
  • New abstraction?: yes, a narrow request-scoped Environment filter resolver/helper is expected.
  • New enum/state/reason family?: no.
  • New cross-domain UI framework/taxonomy?: no.
  • Current operator problem: Operators cannot trust visible workspace hub scope after Environment-owned CTA entry because data filters, shell context, URL params, and visible labels can diverge.
  • Existing structure is insufficient because: WorkspaceHubRegistry protects clean sidebar/global entry, but it deliberately treats Environment-like query params as forbidden for clean entry and does not resolve a valid explicit Environment CTA filter. Page-local parsers currently disagree on key names, identifier type, and visible state.
  • Narrowest correct implementation: One resolver reads only environment_id, validates it inside the current Workspace, and exposes display/apply/clear-url helpers. One shared chip or existing shared primitive renders the visible filter. Page-specific query application remains local.
  • Ownership cost: Tests must cover resolver behavior, CTA URL generation, legacy param rejection, cross-workspace rejection, shell context, clear-link targets, and browser smoke flows. Reviewers must check every touched hub stays aligned.
  • Alternative intentionally rejected: Keeping page-local parsing and only renaming a few CTAs would preserve hidden drift. A compatibility adapter accepting old and new keys is explicitly rejected because this is a hard cutover.
  • Release truth: Current-release truth. This fixes observed system behavior after Spec 313/314, not speculative future architecture.

Compatibility posture

This feature assumes a pre-production environment.

Backward compatibility, legacy aliases, migration shims, historical fixtures, and compatibility-specific tests are out of scope. Canonical replacement is required.

Testing / Lane / Runtime Impact (mandatory for runtime behavior changes)

  • Test purpose / classification: Unit/Feature for resolver, URL helper, and page contracts; Browser for focused critical hub smoke flows.
  • Validation lane(s): fast-feedback for focused Pest tests; browser for Environment CTA, clean sidebar, and clear-link smoke verification.
  • Why this classification and these lanes are sufficient: The risk is request/URL state, workspace isolation, visible scope truth, and cross-page navigation. Focused feature tests prove deterministic contracts, while browser smoke verifies Filament/Livewire rendered state and shell behavior.
  • New or expanded test families: Add Spec 315 contract tests for CTA URL generation, workspace hub environment_id acceptance, legacy param rejection, cross-workspace rejection, shell context, primary-scope wording, clear links, sidebar regression, and Decision Register clean/filtered access.
  • Fixture / helper cost impact: Uses existing Workspace, Managed Environment, membership/capability, and page factories/helpers. New helpers must be opt-in and local to this feature.
  • Heavy-family visibility / justification: Browser smoke is required because the defect includes rendered shell/header/chip truth, not only query filtering. Keep it focused to the critical hub flows named in this spec.
  • Special surface test profile: global-context-shell and monitoring-state-page.
  • Standard-native relief or required special coverage: Standard Filament feature tests are sufficient for page loading, query state, filter chip text, and clear links; browser smoke handles integrated UI truth.
  • Reviewer handoff: Reviewers should verify the exact commands in tasks.md, ensure no legacy param acceptance remains for the explicit CTA filter contract, and confirm browser screenshots for critical hubs.
  • Budget / baseline / trend impact: No expected long-term budget increase beyond focused Spec 315 tests.
  • Escalation needed: none unless implementation discovers a hub requires broad persisted filter clearing; that belongs in Spec 316.
  • Active feature PR close-out entry: Guardrail and Smoke Coverage.
  • Planned validation commands: Focused Pest commands for Spec 315 feature tests, existing Spec 314 regression tests, and a focused browser smoke command or manual browser verification if browser automation cannot be made deterministic.

User Scenarios & Testing (mandatory)

User Story 1 - Environment CTA opens a visible filtered workspace hub (Priority: P1)

An operator starts on an Environment Dashboard and opens a workspace hub CTA. The destination remains a workspace hub, but the Environment focus is explicit in the URL, visible on the page, and applied to the data.

Why this priority: This is the primary drift that Spec 315 fixes.

Independent Test: Open each critical workspace hub with ?environment_id={environment.id} from an Environment-owned link and assert the page loads, shows the Environment filter chip, filters data to that Environment, and offers a clean clear link.

Acceptance Scenarios:

  1. Given Workspace A has Environment A and the user can access Workspace A, When the user clicks an Environment Dashboard CTA to Operations, Then the URL contains environment_id={Environment A database id}, the page shows Environment filter: {Environment A display name}, and operations are filtered to Environment A.
  2. Given a filtered workspace hub, When the user reviews the shell/context bar, Then the shell remains workspace-scoped and does not treat the hub as Environment-owned.
  3. Given a filtered workspace hub, When the user sees page scope text, Then the primary scope does not misleadingly claim All environments.

User Story 2 - Legacy Environment CTA params are ignored (Priority: P1)

An operator or stale link opens a workspace hub with legacy query keys. The page must not treat those keys as a valid Environment CTA filter.

Why this priority: The spec is a hard cutover. Accepting aliases would preserve hidden ambiguity.

Independent Test: For each critical hub, open URLs with tenant, tenant_id, managed_environment_id, tenant_scope, environment, and tableFilters without environment_id; assert no valid Environment chip appears and data remains workspace-wide or the app safely normalizes according to existing conventions.

Acceptance Scenarios:

  1. Given a workspace hub URL includes managed_environment_id=123 but no environment_id, When the page loads, Then no Environment filter chip is shown and the page does not filter as a valid Environment CTA filter.
  2. Given a workspace hub URL includes tenant=123 but no environment_id, When the page loads, Then the page remains workspace-wide or safely normalizes without using the legacy key.

User Story 3 - Cross-workspace Environment IDs are rejected (Priority: P1)

An operator opens Workspace A hub with an Environment ID from Workspace B. The app must not leak data or switch workspace context.

Why this priority: Workspace isolation is a core constitution requirement.

Independent Test: Seed Workspace A, Workspace B, Environment B, and accessible user context for Workspace A. Open Workspace A hub with ?environment_id={Environment B id} and assert 404 or the existing safe no-access behavior.

Acceptance Scenarios:

  1. Given Environment B belongs to Workspace B, When the user opens Workspace A Operations with environment_id={Environment B id}, Then the app returns 404 or safe no-access, does not show Environment B, and does not switch workspace.

User Story 4 - Clean sidebar/global entry remains workspace-wide (Priority: P2)

An operator moves from an Environment-owned surface to a workspace hub via sidebar or global navigation. The destination remains clean and workspace-wide.

Why this priority: This preserves Spec 314 while introducing Environment CTA filters.

Independent Test: After visiting an Environment Dashboard or filtered hub, open each critical hub through sidebar/global navigation and assert no environment_id or legacy Environment-like query params are present, no filter chip is visible, and workspace-wide data appears.

Acceptance Scenarios:

  1. Given the user recently opened Operations with environment_id, When the user clicks the sidebar Operations entry, Then the URL is the clean Operations hub URL and no Environment filter chip is visible.

User Story 5 - Clear filter returns to the clean hub URL (Priority: P2)

An operator on a filtered workspace hub can remove the Environment filter through a visible clear action.

Why this priority: Operators need an obvious exit from filtered state. Full persisted-state clear internals are intentionally deferred to Spec 316.

Independent Test: Open each critical hub with environment_id, assert the clear link exists, inspect the link target, click it in browser smoke, and assert the URL no longer contains environment_id or legacy params.

Acceptance Scenarios:

  1. Given Governance Inbox is filtered by Environment A, When the user clicks Clear filter, Then the browser navigates to the clean Governance Inbox URL without environment_id.

Requirements (mandatory)

Functional Requirements

  • FR-001: The only accepted Environment CTA filter query key for workspace hubs MUST be environment_id.
  • FR-002: Workspace hubs MUST NOT treat tenant, tenant_id, managed_environment_id, tenant_scope, environment, or tableFilters as valid Environment CTA filter state.
  • FR-003: environment_id MUST be the Managed Environment database identifier used by the current application model, not provider external tenant ID, slug, or display name.
  • FR-004: Environment filter resolution MUST query the current Workspace only and reject Environment IDs outside that Workspace with 404 or the existing safe no-access behavior.
  • FR-005: Environment filter resolution MUST NOT use Filament::getTenant(), remembered Environment state, session fallback, provider external tenant ID lookup, or workspace switching.
  • FR-006: Environment-owned CTAs that intentionally open workspace hubs with Environment focus MUST emit exactly environment_id={managed_environment_id} for that filter and MUST NOT emit legacy Environment filter params.
  • FR-007: A shared resolver/helper MUST expose the valid Environment filter state to pages in a way that supports hasFilter(), environment(), environmentId(), display name access, clear URL generation, and query application.
  • FR-008: Each in-scope workspace hub that accepts environment_id MUST render a visible Environment filter chip or summary when the filter is valid.
  • FR-009: The visible filter state MUST include the Environment display name and a clear link/action to the clean workspace hub URL.
  • FR-010: Filtered workspace hubs MUST NOT primarily show All environments wording while the explicit Environment filter is active.
  • FR-011: Filtered workspace hubs MUST keep shell/context ownership at the Workspace level and MUST NOT set active shell Environment ownership.
  • FR-012: If environment_id is valid, in-scope workspace hub data MUST be filtered to that Environment.
  • FR-013: If environment_id is absent, in-scope workspace hubs MUST be workspace-wide and MUST NOT apply remembered Environment state as a filter.
  • FR-014: Clean sidebar/global workspace hub URLs from Spec 314 MUST remain clean, workspace-wide, and free of Environment query params.
  • FR-015: The clear link target MUST remove environment_id, MUST emit no legacy Environment filter params, and MUST point to the clean workspace hub URL.
  • FR-016: Operations MUST support the canonical environment_id filter, visible chip, filtered data, clean clear link, and clean sidebar regression.
  • FR-017: Governance Inbox MUST hard-cut its existing Environment filter behavior to environment_id only.
  • FR-018: Decision Register MUST support both clean workspace URL and environment_id filtered URL without requiring the filter for access.
  • FR-019: Finding Exceptions Queue MUST replace legacy Environment CTA query behavior with environment_id only.
  • FR-020: Provider Connections / Integrations MUST use environment_id for explicit Environment CTA filters and MUST NOT use provider external tenant ID as the filter key.
  • FR-021: Evidence Overview MUST support canonical Environment filtering and document whether filtering is query-backed or in-memory.
  • FR-022: Reviews and Customer Review Workspace MUST support canonical Environment filtering while remaining workspace-scoped.
  • FR-023: Audit Log, Alerts, Reports, Stored Reports, and Support Requests MUST be classified during implementation; if Environment-filterable workspace hubs, they must follow this contract, otherwise Environment-owned CTAs must not pass an Environment filter to them.
  • FR-024: No compatibility layer, dual-param support, legacy alias support, stored URL migration, seeders, migrations, packages, environment variables, queues, scheduler changes, or storage changes may be introduced for this spec.

Non-Functional Requirements

  • NFR-001: The implementation MUST preserve workspace isolation and avoid leaking whether cross-workspace Environment IDs exist.
  • NFR-002: The implementation MUST use native Filament v5 / Livewire v4 patterns and existing shared primitives before local Blade/Tailwind customization.
  • NFR-003: Any new shared helper MUST stay narrow and replace duplicated page-local query parsing rather than becoming a broader context framework.
  • NFR-004: Browser verification MUST include Environment CTA flow, clean sidebar regression, and clear-link smoke for the critical hubs.

Key Entities (include if feature involves data)

  • Workspace: Primary operating scope and owner of workspace hubs.
  • ManagedEnvironment: Secondary operational scope inside a Workspace. Used by this spec only as an explicit filter when a valid environment_id is present.
  • WorkspaceHubEnvironmentFilter: Proposed request-scoped helper/resolver. It is not persisted and is not a new source of truth.
  • WorkspaceHubRegistry: Existing workspace hub registry/URL helper from Spec 314. It remains responsible for clean hub URL generation and must align with the explicit filter path.

In Scope

  • Canonical query key environment_id.
  • Workspace-scoped Managed Environment resolution.
  • Environment-owned CTA URL generation to workspace hubs.
  • Visible Environment filter chip/summary and clean clear link.
  • Data filtering for in-scope hubs when the filter is valid.
  • Legacy param rejection for Environment CTA filter behavior.
  • Regression coverage that sidebar/global entry remains clean and workspace-wide.
  • Focused browser verification and screenshots where useful.

Out of Scope

  • Full universal clear-filter internals across Livewire properties, Filament table filters, deferred filters, persisted/session filters, and back/forward state. This belongs to Spec 316.
  • Broad removal of all legacy tenant naming or old query handling unrelated to Environment CTA filters. This belongs to Spec 317.
  • Durable browser regression guard infrastructure. This belongs to Spec 318.
  • Redesigning workspace hubs.
  • Changing page product scope.
  • Making workspace hubs Environment-owned.
  • Making the sidebar dynamic based on active Environment.
  • New migrations, seeders, data backfills, packages, environment variables, queues, scheduler tasks, storage volumes, or production compatibility transforms.

Page-Specific Requirements

Operations

  • Accept only environment_id for explicit Environment CTA filter.
  • Show visible Environment filter chip.
  • Filter operations to the selected Environment.
  • Avoid managed_environment_id, tenant_scope, and misleading All environments wording while filtered.
  • Clear link points to the clean Operations URL.

Governance Inbox

  • Hard-cut existing Environment filtering to environment_id.
  • Remove tenant and managed_environment_id acceptance for this CTA filter contract.
  • Keep clean URL workspace-wide.

Decision Register

  • Keep clean workspace URL accessible.
  • Support filtered URL with visible chip.
  • Do not return 403 merely because the filter is absent.
  • Do not accept legacy Environment query aliases.

Finding Exceptions Queue

  • Accept only environment_id.
  • Do not use remembered Environment as data scope.
  • Show visible chip and clean clear link.
  • Keep clean URL workspace-wide.

Provider Connections / Integrations

  • Accept only environment_id for explicit Environment CTA filters.
  • Do not use provider external tenant ID as the CTA filter key.
  • Do not treat explicit Environment filtering as shell Environment ownership.
  • Keep sidebar/global behavior workspace-wide.

Evidence Overview

  • Accept only environment_id.
  • Show visible chip and clean clear link.
  • Filter rows/search/list consistently with the chip, whether query-backed or in-memory.

Reviews and Customer Review Workspace

  • Accept only environment_id.
  • Show visible chip and clear link.
  • Keep the customer-safe workspace experience workspace-scoped.
  • Do not convert Customer Review Workspace into an Environment page.

Audit Log / Alerts / Reports / Support Requests

  • Classify each during implementation.
  • If workspace-owned and Environment-filterable, implement environment_id, visible chip, and filtered data.
  • If not Environment-filterable, do not send Environment CTA filters to those pages and document the exclusion in the implementation close-out.

Required Tests

  • CTA URL Contract: Environment-owned CTAs to critical workspace hubs generate environment_id and never legacy keys.
  • Workspace Hub Accepts Canonical Filter: Critical hubs load with environment_id, show chip, filter data where seeded rows prove it, and expose clean clear link.
  • Legacy Params Do Not Apply Filter: Critical hubs ignore tenant, tenant_id, managed_environment_id, tenant_scope, environment, and tableFilters as explicit Environment filter state.
  • Invalid Environment ID Is Workspace-Scoped: Cross-workspace IDs return 404 or safe no-access without leakage or workspace switch.
  • Shell Does Not Become Environment-Owned: Filtered hubs keep Workspace shell context and show Environment only as page-level filter.
  • Filtered Header Does Not Say All Environments: At least Operations, Provider Connections, Customer Reviews, and Finding Exceptions Queue avoid misleading primary scope wording.
  • Clear Link Points To Clean Hub URL: Critical hubs link back to clean workspace hub URLs without environment_id or legacy params.
  • Sidebar Contract Still Holds: Spec 314 clean sidebar/global entry remains clean and workspace-wide.
  • Decision Register Filter Contract: Clean and filtered Decision Register URLs both work, with no legacy query accepted.

Browser Verification Required

Critical pages:

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

Required flows:

  1. Environment Dashboard CTA: Open Environment Dashboard, click CTA, verify environment_id, no legacy params, workspace shell, visible chip, no misleading All environments, clean clear target, and capture screenshot.
  2. Clean Sidebar Regression: From Environment context, click sidebar/global entry to the same hub, verify clean URL, no chip, workspace-wide state, and capture screenshot where useful.
  3. Clear Link Smoke: From filtered page, click clear link, verify clean URL, chip disappears, and page is workspace-wide. Do not assert full persisted-state cleanup beyond immediate visible correctness.

Suggested screenshots path:

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

Success Criteria (mandatory)

Measurable Outcomes

  • SC-001: Every in-scope Environment-owned CTA to a workspace hub emits environment_id and no legacy Environment filter query params.
  • SC-002: Every critical workspace hub accepts valid environment_id, shows a visible Environment filter chip, filters data to that Environment, and exposes a clean clear link.
  • SC-003: Every critical workspace hub ignores legacy Environment CTA params as filter state.
  • SC-004: Cross-workspace Environment IDs are rejected without data leakage.
  • SC-005: Clean sidebar/global workspace hub entry remains clean and workspace-wide after visiting filtered hubs.
  • SC-006: Required focused Pest tests and browser verification pass.

Assumptions

  • Spec 314 is complete and its clean sidebar/global workspace hub behavior is the baseline.
  • There is no production data or production environment to preserve, so hard cutover is acceptable.
  • Existing Workspace and Managed Environment models/factories can support the required tests without new persistence.
  • Existing authorization conventions already distinguish non-member/not-found and member-missing-capability behavior.

Risks

  • Persisted Filament filter state can reapply after clear: Spec 315 only requires clean hub URL behavior and immediate visible correctness; full clear semantics belong to Spec 316.
  • Provider Connections currently mixes external IDs and Managed Environment identifiers: Implementation must be explicit that CTA filtering uses database environment_id.
  • Large surface area: Tests and shared resolver/chip are mandatory to prevent another page-local drift cycle.

Required Final Implementation Report

When implementation is complete, report:

Spec 315 completed.

Changed behavior:
...

Canonical filter:
environment_id

Files changed:
...

Tests:
- command:
- result:

Browser verification:
...

Remaining follow-ups:
- 316:
- 317:
- 318:

No migrations were created.
No seeders were changed.
No packages, env vars, queues, scheduler, or storage changes were made.
No backwards compatibility layer was introduced.
No legacy query alias support was preserved for Environment CTA filtering.

Also include:

  • list of hubs supporting environment_id
  • list of CTAs updated
  • list of legacy params removed or ignored
  • pages intentionally excluded
  • clear-filter limitations deferred to Spec 316

Follow-Up Specs

  • 316 - Workspace Hub Clear Filter Contract: Full clearing across URL query, Livewire state, Filament table filters, deferred filters, persisted/session state, visible chips, reload safety, and back/forward stale state.
  • 317 - Legacy Tenant / Environment Context Cleanup: Remove/quarantine old tenant aliases, old managed_environment_id query handling where no longer valid, workspace hub Filament::getTenant() usage, remembered Environment as data boundary, stale tenant naming, and compatibility seams.
  • 318 - Browser Regression Coverage / No-Drift Guard: Durable browser/regression coverage for sidebar/global versus Environment CTA behavior.

Filament v5 Output Contract

  1. Livewire v4.0+ compliance: This spec targets the current app stack: Filament v5 with Livewire v4. No Livewire v3 APIs or assumptions are allowed.
  2. Provider registration location: No new Filament panel provider is expected. If implementation discovers provider registration work, Laravel 12 requires panel providers in apps/platform/bootstrap/providers.php, not bootstrap/app.php.
  3. Global search: No resource is made globally searchable by this spec. Existing globally searchable resources must still have Edit/View pages; Provider Connections currently remains disabled for global search unless a separate spec changes it.
  4. Destructive actions: No destructive actions are added or changed. If any implementation path touches a destructive action, it must use ->action(...), ->requiresConfirmation(), and existing authorization policy checks.
  5. Asset strategy: No new heavy assets are expected. If a shared Blade partial uses existing CSS/classes only, no asset deployment change is needed. If Filament assets are registered, deployment must include cd apps/platform && php artisan filament:assets.
  6. Testing plan: Cover Filament pages/resources as Livewire components or feature routes following current Pest conventions. Mutating actions are not part of this spec; URL, filter, shell, and visible-chip contracts are the required test surface.