TenantAtlas/specs/341-canonical-link-query-cleanup/spec.md
ahmido e324bd7bd6 feat: canonicalize admin scope links and queries (341) (#413)
## Summary
- remove remaining legacy scope query hint parsing from shared workspace and environment scoping seams so hubs only narrow via explicit `environment_id`
- align canonical link generation across workspace hubs, provider connections, audit log, alerts, and decision register flows
- add focused Spec 341 regression coverage for canonical link/query behavior and legacy alias rejection
- include the Spec 341 artifacts and move the review screenshots into `specs/341-canonical-link-query-cleanup/artifacts/screenshots/`
- ignore local `.playwright-mcp` browser tool output so it does not pollute future commits or pull requests

## Validation
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Navigation --filter=Spec341`
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Navigation/Spec341CanonicalLinkQueryCleanupTest.php tests/Feature/Navigation/WorkspaceHubEnvironmentFilterContractTest.php tests/Feature/ProviderConnections/ProviderConnectionsWorkspaceHubContractTest.php`
- `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent`
- `git diff --check`

## Notes
- Livewire v4 compliance unchanged
- Filament provider registration remains in `apps/platform/bootstrap/providers.php`
- no globally searchable resource behavior was changed in this slice
- no destructive action behavior was changed
- no new Filament assets; deploy `filament:assets` posture is unchanged

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #413
2026-05-31 22:46:39 +00:00

192 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Feature Specification: Spec 341 - Canonical Link / Query Cleanup
**Feature Branch**: `341-canonical-link-query-cleanup`
**Created**: 2026-05-31
**Status**: Draft
**Input**: Candidate `canonical-link-query-cleanup` from `docs/product/spec-candidates.md` + repo inspection of remaining legacy scope query hints in middleware/shell after Specs 338340 and Spec 339.
## Spec Candidate Check *(mandatory — SPEC-GATE-001)*
- **Problem**: The workspace/environment scope contract is now repo-real (Specs 314322, 338339), but some remaining URL/query seams still interpret legacy scope query keys (`tenant`, `managed_environment_id`, etc.) as “environment hints”. That reintroduces hidden scope, weakens trust for credential-adjacent and environment-bound surfaces, and makes deep links less predictable.
- **Today's failure**: Repo inspection shows remaining legacy query parsing in shared request-scoping seams (e.g. environment/tenant selection middleware and `OperateHubShell`) that can treat `?tenant=` or `?managed_environment_id=` as scope hints outside workspace hubs. This undermines the “explicit or route-owned” scope rule and increases the risk of accidental scope drift resurfacing.
- **User-visible improvement**: Operators can share and reload admin links confidently: workspace hubs are workspace-wide by default and only narrow via explicit `environment_id`; environment-bound pages are route-owned and do not accept legacy query aliases as authority; “clear filter” links return to clean canonical URLs; and guard tests block reintroducing legacy query scope behavior.
- **Smallest enterprise-capable version**: Inventory remaining legacy scope query parsing, remove it (or make it strictly ignore-only with safe fallback), align canonical link generation across hubs/shell/middleware seams, and add targeted tests to prevent regressions.
- **Explicit non-goals**:
- No new UI shell/sidebar/topbar redesign.
- No new routes or panels.
- No new persisted entities, enums, or abstraction frameworks.
- No RBAC model or capability registry changes.
- No provider/OAuth redesign (Spec 281 family).
- No Graph integration changes.
- **Permanent complexity imported**: A small, explicit inventory + a small set of guard/contract tests around canonical URL/query semantics. No new runtime taxonomy or UI framework.
- **Why now**: Specs 338340 and Spec 339 hardened scope/authority. The next risk is “scope drift through links and query parsing” reappearing as features continue. This cleanup locks the contract so future work cannot accidentally revive legacy hints.
- **Why not local**: Fixing a single pages link generator is insufficient because the drift sits in shared request-scoping seams. The smallest correct slice must cover the shared parsing/link seams and prove behavior with tests.
- **Approval class**: Core Enterprise (scope/authorization safety hardening).
- **Red flags triggered**: Cross-surface navigation semantics + shared middleware/shell seams. **Defense**: bounded to removing legacy query hint parsing and aligning canonical URL generation; no new frameworking/persistence; tests make the change reviewable.
- **Score**: Nutzen: 2 | Dringlichkeit: 2 | Scope: 1 | Komplexität: 1 | Produktnähe: 1 | Wiederverwendung: 2 | **Gesamt: 9/12**
- **Decision**: approve.
## Summary
Canonicalize admin navigation and request scope semantics so that:
- Workspace hubs remain workspace-wide unless explicitly filtered by `environment_id`.
- Environment-bound pages derive environment context only from route parameters (not legacy query aliases).
- Legacy scope query keys do not grant authority, do not silently narrow results, and do not reintroduce “hidden environment context”.
- Shared seams (`EnsureWorkspaceSelected`, `EnsureEnvironmentContextSelected`, `OperateHubShell`) do not parse legacy query scope hints for authority.
This is a hardening / cleanup slice: it removes legacy scope query hint parsing and aligns link/query semantics with the existing scope contract.
## Completed-Spec Guardrail Result
Related specs are context only and must not be rewritten:
- Workspace hub contracts and legacy alias guards: Specs 314322, 317, 338.
- Credential-adjacent authority hardening: Spec 339.
- Post-scope browser verification gate: Spec 340.
No `specs/341-*` package existed before this prep package was created.
## Spec Scope Fields *(mandatory)*
- **Scope**: canonical-view (navigation + link/query contract hygiene).
- **Primary routes / surfaces affected** (contract-level; exact list is inventory-driven):
- Workspace hubs that accept environment narrowing via `environment_id` (e.g. Governance Inbox, Decision Register, Review Register, Evidence Overview, Audit Log, Alerts, Provider Connections index, Customer Review Workspace).
- Environment-bound pages under `/admin/workspaces/{workspace}/environments/{environment}/...` that must not accept legacy query scope hints.
- Shared request-scoping seams that currently inspect legacy query keys:
- `apps/platform/app/Http/Middleware/EnsureWorkspaceSelected.php`
- `apps/platform/app/Support/Middleware/EnsureEnvironmentContextSelected.php`
- `apps/platform/app/Support/OperateHub/OperateHubShell.php`
- `apps/platform/app/Filament/Pages/EnvironmentRequiredPermissions.php` (query-hint cleanup only)
- **Data Ownership**: No schema changes. This slice changes URL/query handling and link generation only.
- **RBAC**:
- No new capabilities.
- Existing 404 vs 403 semantics remain authoritative.
- `environment_id` is a filter hint only; it must be validated against current workspace context + actor entitlement.
For canonical-view surfaces:
- **Default filter behavior when tenant-context is active**: Workspace hub clean URLs are workspace-wide and must not inherit remembered environment context, Filament tenant fallback, session table filters, or legacy query keys. If narrowed, the page must show an explicit `environment_id` filter state.
- **Explicit entitlement checks preventing cross-tenant leakage**: Any requested `environment_id` must resolve to an environment in the current workspace that the actor can access; foreign/out-of-scope IDs must fail as not found and must not leak existence.
## UI Surface Impact *(mandatory — UI-COV-001)*
Does this spec add, remove, rename, or materially change any reachable UI surface?
- [ ] No UI surface impact
- [x] Existing page changed
- [ ] New page/route added
- [x] Navigation changed
- [ ] Filament panel/provider surface changed
- [ ] New modal/drawer/wizard/action added
- [ ] New table/form/state added
- [ ] Customer-facing surface changed
- [ ] Dangerous action changed
- [ ] Status/evidence/review presentation changed
- [x] Workspace/environment context presentation changed
## UI/Productization Coverage *(UI-COV-001)*
- **Route/page/surface**: Cross-surface link + query semantics for workspace hubs and environment-bound pages (no new UI surface).
- **Current page archetype**: scope-contract / navigation semantics (global-context-shell + workspace hub filter contract).
- **Design depth**: Domain Pattern Surface (scope/authority hardening) — minimal visible UX change expected.
- **Repo-truth level**: repo-verified (existing hub filter contracts + shared middleware/shell code).
- **Existing pattern reused**: Spec 314/315/316 hub contracts + Spec 317 legacy alias cleanup + Spec 338 contract tests + existing navigation helpers.
- **New pattern required**: none (tighten and converge; do not invent a new link-normalization framework).
- **Screenshot required**: no (unless implementation introduces visible copy changes; then add one targeted screenshot in the implementation PR only).
- **Page audit required**: no (no new archetype; contract hardening only).
- **Customer-safe review required**: no (scope contract applies broadly; not a customer-safe productization slice).
- **Dangerous-action review required**: no (no action behavior change; link/query semantics only).
- **Coverage files to update (in implementation PR)**:
- [ ] `docs/ui-ux-enterprise-audit/route-inventory.md` (only if routes/nav entries change materially; not expected)
- [ ] `docs/ui-ux-enterprise-audit/design-coverage-matrix.md` (no new surface; expected `no`)
- [x] `N/A - no new reachable UI surface added; contract/URL semantics only`
## Cross-Cutting / Shared Pattern Reuse *(mandatory)*
- **Cross-cutting feature?**: yes.
- **Interaction class(es)**: navigation deep links, scope presentation, URL/query semantics, “clear filter” behavior.
- **Systems touched (expected)**:
- `apps/platform/app/Support/Navigation/WorkspaceHubNavigation.php`
- `apps/platform/app/Support/Navigation/WorkspaceHubEnvironmentFilter.php`
- `apps/platform/app/Filament/Concerns/UsesAdminEnvironmentFilterQueryParameter.php`
- `apps/platform/app/Http/Middleware/EnsureWorkspaceSelected.php`
- `apps/platform/app/Support/Middleware/EnsureEnvironmentContextSelected.php`
- `apps/platform/app/Support/OperateHub/OperateHubShell.php`
- `apps/platform/tests/Feature/Navigation/*`
- **Existing pattern(s) to extend**: Explicit `environment_id` filter only for workspace hubs; route-owned environment for environment-bound pages; deny-as-not-found for out-of-scope.
- **Allowed deviation and why**: none. This slice removes legacy behavior; it does not add a second link/query interpretation path.
- **Consistency impact**: One canonical environment filter key (`environment_id`) for hubs; no legacy query alias authority anywhere; all “clear filter” links return to clean canonical URLs.
- **Review focus**: “legacy query scope hints are not authority” + tests that make regressions obvious.
## OperationRun UX Impact
N/A — no `OperationRun` start, link, or lifecycle semantics are changed in this slice.
## Provider Boundary / Platform Core Check
- **Shared provider/platform boundary touched?**: no.
- **N/A**: This is platform scope/navigation hygiene. It must not interpret provider “tenant” identifiers as platform authority.
## Proportionality Review
N/A — no new persisted entities, abstraction frameworks, enums/status families, or taxonomy systems are introduced.
## Testing / Lane / Runtime Impact *(mandatory for runtime behavior changes)*
- **Test purpose / classification**: Feature (scope/link/query contract + regression guard).
- **Validation lane(s)**: fast-feedback (Feature). Browser smoke is optional and only justified if implementation changes visible navigation/URL behavior that cannot be proven reliably in Feature tests alone.
- **Planned validation commands**:
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Navigation --filter=Canonical`
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Workspaces --filter=Scope`
- `cd apps/platform && ./vendor/bin/sail pint --dirty`
## Acceptance Criteria
- **AC1**: Workspace hubs accept `environment_id` as the only environment-narrowing query key; legacy aliases (`tenant`, `tenant_id`, `managed_environment_id`, `environment`, `tenant_scope`, `tableFilters`) never establish filter authority.
- **AC2**: Environment-bound routes derive environment context only from route params and do not parse legacy query aliases as scope hints.
- **AC3**: “Clear filter” returns to the clean canonical hub URL and clears any persisted hub filter state as per existing contracts.
- **AC4**: Out-of-scope `environment_id` is rejected with deny-as-not-found semantics and does not leak environment existence.
- **AC5**: Targeted Feature tests fail loudly on regression and pass after implementation; no new heavy-governance or browser family is introduced without an explicit spec decision.
## User Scenarios & Testing *(mandatory)*
### User Story 1 — Share a canonical workspace hub link (Priority: P1)
As a workspace operator, I can share a link to a workspace hub that is either workspace-wide (clean URL) or explicitly narrowed to one environment using `environment_id`, and the page shows scope/filter state correctly after reload/back/forward.
**Independent Test**: A Feature test renders each critical hub with clean URL and with `?environment_id=<id>` and asserts filter state, clear-filter behavior, and no legacy query keys.
### User Story 2 — Legacy query aliases never become authority (Priority: P1)
As a security reviewer, I can trust that legacy query keys cannot establish environment context or widen results on environment-bound pages or shared scoping middleware.
**Independent Test**: Feature tests verify that `?tenant=` and `?managed_environment_id=` are ignored or rejected consistently and never treated as an environment hint for authority.
### User Story 3 — Guard tests prevent reintroduction (Priority: P2)
As a developer, I have regression guards that fail CI if new code introduces legacy query parsing or generates links containing legacy scope query keys.
**Independent Test**: A guard test scans/validates generated navigation URLs for forbidden query keys.
## Out of Scope
- Any feature work that changes business logic, capabilities, policies, or data model.
- Any broad UI redesign, new surfaces, or new navigation architecture.
- Any provider/OAuth or credential workflow changes.
- Any compatibility redirects or preserving legacy scope query keys “just in case”.
## Risks
- Some internal/shared surfaces may still rely on legacy query hints for environment selection. Implementation must replace that behavior with explicit route-owned context or explicit `environment_id` on hubs, and prove it with tests.
- Over-scoping this cleanup can become a refactor. Tasks must stay inventory-driven and bounded to canonical scope/query semantics only.
## Assumptions
- The workspace/environment scope contract tests and hub filter contracts remain authoritative.
- This is pre-production: removing legacy scope query keys is allowed and preferred over compatibility layers.
## Open Questions
None blocking. Any discovered “needed but unclear” legacy behavior must be recorded as a follow-up candidate rather than silently preserved.