## Summary\n- add completed Spec 319 artifacts for the environment-owned Baseline Compare routing contract\n- include browser-smoke screenshots and focused validation notes\n- keep the PR diff limited to Spec 319 artifacts because runtime is already present in platform-dev via #374\n\n## Testing\n- git diff --check\n- focused validation recorded in specs/319-environment-owned-surface-routing-shell-context-contract/tasks.md Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #377
490 lines
35 KiB
Markdown
490 lines
35 KiB
Markdown
# Feature Specification: Environment-Owned Surface Routing & Shell Context Contract
|
|
|
|
**Feature Branch**: `319-environment-owned-surface-routing-shell-context-contract`
|
|
**Created**: 2026-05-16
|
|
**Status**: Completed
|
|
**Input**: User supplied Spec 319 draft: Baseline Compare and similar Environment-owned pages must route through explicit Workspace + Environment ownership, with no legacy workspace-style access, no `environment_id` filter model, and no remembered Environment fallback.
|
|
|
|
## Spec Candidate Check *(mandatory - SPEC-GATE-001)*
|
|
|
|
- **Problem**: Baseline Compare behaves as an Environment-owned page but currently uses an unbound workspace-style Filament page URL, so route, shell, breadcrumb, copy, and access semantics can disagree.
|
|
- **Today's failure**: Operators can see "this environment" copy only because remembered Environment state happens to exist; after context clear, direct clean and direct `environment_id` URLs fail or behave inconsistently instead of being self-sufficient route-owned Environment pages.
|
|
- **User-visible improvement**: Baseline Compare is opened from an Environment route, reloads safely, shows Workspace + Environment shell context, rejects cross-workspace access, and no longer looks like a workspace hub or hidden filter state.
|
|
- **Smallest enterprise-capable version**: Hard-cut Baseline Compare to a canonical route-bound Environment page and update its entry points, classification, shell/breadcrumb/copy, and regression coverage. Inspect adjacent Environment-owned pages only for the same mismatch.
|
|
- **Explicit non-goals**: No workspace-wide Baseline Compare, no `environment_id` filter support for Baseline Compare, no compatibility redirects, no legacy query aliases, no Spec 320 workspace-owned analysis fixes, no Spec 321 Alerts/Audit filter decision, no durable browser infrastructure from Spec 322.
|
|
- **Permanent complexity imported**: No new persisted entities, tables, enum families, status families, broad registries, or cross-domain frameworks. Expected complexity is route/classification/link/test updates over existing Filament/Laravel structures.
|
|
- **Why now**: Spec 318 identified Baseline Compare as the highest-risk remaining shell/context mismatch after Specs 314-317 stabilized workspace hubs and legacy cleanup.
|
|
- **Why not local**: A copy-only or CTA-only fix would leave direct URL, reload, cross-workspace, shell, and remembered-fallback risks unresolved.
|
|
- **Approval class**: Cleanup / Consolidation.
|
|
- **Red flags triggered**: "Many surfaces, little user value" is avoided by making Baseline Compare required and keeping other pages inspect-only unless the same mismatch is proven. No new axis or framework is introduced.
|
|
- **Score**: Nutzen: 2 | Dringlichkeit: 2 | Scope: 2 | Komplexitat: 2 | Produktnahe: 2 | Wiederverwendung: 1 | **Gesamt: 11/12**
|
|
- **Decision**: approve.
|
|
|
|
## Spec Scope Fields *(mandatory)*
|
|
|
|
- **Scope**: workspace + environment route-owned admin surface.
|
|
- **Primary Routes**: Baseline Compare, canonical Environment Dashboard Baseline Compare CTA/action, relevant Environment sidebar/navigation entry if present.
|
|
- **Data Ownership**: Existing workspace-owned Baseline Profile/Snapshot truth and Environment-owned compare/assignment/result state only. No schema changes.
|
|
- **RBAC**: Workspace membership plus Environment entitlement. Non-member or cross-workspace Environment access returns 404/safe no-access. Member missing capability follows existing capability behavior.
|
|
|
|
For canonical-view specs:
|
|
|
|
- **Default filter behavior when tenant-context is active**: Not a canonical workspace view. Baseline Compare must not use active/remembered Environment as fallback.
|
|
- **Explicit entitlement checks preventing cross-tenant leakage**: Environment route resolution must validate `managed_environments.workspace_id === workspaces.id` before rendering or revealing Environment existence.
|
|
|
|
## Cross-Cutting / Shared Pattern Reuse *(mandatory)*
|
|
|
|
- **Cross-cutting feature?**: yes.
|
|
- **Interaction class(es)**: navigation entry points, shell/context bar, breadcrumbs, dashboard CTAs, route helpers, page copy, action links.
|
|
- **Systems touched**: `AdminSurfaceScope`, `WorkspaceHubRegistry`, `WorkspaceSidebarNavigation`, `WorkspaceScopedEnvironmentRoutes`, `ManagedEnvironmentLinks`, `OperateHubShell`, `BaselineCompareLanding`, Environment Dashboard summary/action builders, relevant widgets, routes, and tests.
|
|
- **Existing pattern(s) to extend**: Route-bound Environment pattern under `/admin/workspaces/{workspace}/environments/{environment}/...` and existing workspace/environment shell resolution.
|
|
- **Shared contract / presenter / builder / renderer to reuse**: Reuse `WorkspaceScopedEnvironmentRoutes`/route-bound Environment conventions where possible, `ManagedEnvironmentLinks` for canonical Environment URLs if a helper is needed, and existing Filament navigation/page patterns.
|
|
- **Why the existing shared path is sufficient or insufficient**: Existing route-bound Environment pages already produce correct shell behavior. Baseline Compare is the outlier because it remains a Filament page on `/admin/baseline-compare-landing` plus query/helper fallback.
|
|
- **Allowed deviation and why**: None for Baseline Compare. A redirect from old workspace-style URLs is disallowed unless implementation discovers an existing mandatory convention and covers it with tests.
|
|
- **Consistency impact**: Environment-owned pages must not behave as workspace hubs or filtered workspace hubs. Copy saying "this environment" is valid only with active Environment shell context.
|
|
- **Review focus**: Verify no compatibility path, query alias, remembered fallback, or workspace hub registry entry makes Baseline Compare render outside route-owned Environment context.
|
|
|
|
## OperationRun UX Impact
|
|
|
|
- **Touches OperationRun start/completion/link UX?**: yes, existing Baseline Compare "Compare now" action and run links must keep their current UX contract.
|
|
- **Shared OperationRun UX contract/layer reused**: Existing `OperationUxPresenter`, `OpsUxBrowserEvents`, and `OperationRunLinks`.
|
|
- **Delegated start/completion UX behaviors**: Existing queued toast, "Open operation" link, tenant/workspace-safe URL resolution, and run-enqueued browser event remain delegated to existing shared helpers.
|
|
- **Local surface-owned behavior that remains**: Baseline Compare page still owns initiation inputs and page state.
|
|
- **Queued DB-notification policy**: N/A - no new notification policy.
|
|
- **Terminal notification path**: Existing OperationRun lifecycle only.
|
|
- **Exception required?**: none.
|
|
|
|
## Provider Boundary / Platform Core Check
|
|
|
|
- **Shared provider/platform boundary touched?**: yes.
|
|
- **Boundary classification**: platform-core route/shell contract; provider-specific Tenant identity remains provider-owned only.
|
|
- **Seams affected**: route shape, navigation helpers, page classification, shell resolution, query key handling, and visible Workspace/Environment copy.
|
|
- **Neutral platform terms preserved or introduced**: `Workspace`, `Environment`, `Environment-owned page`, `Workspace hub`, `filtered workspace hub`, `Baseline Compare`.
|
|
- **Provider-specific semantics retained and why**: Existing Microsoft/Intune baseline compare domain behavior remains unchanged. Provider tenant IDs must not become route or fallback identifiers for this page.
|
|
- **Why this does not deepen provider coupling accidentally**: The canonical route uses platform Workspace + Managed Environment route identity, not Microsoft tenant IDs or Graph payload identity.
|
|
- **Follow-up path**: Specs 320, 321, and 322 handle adjacent workspace analysis, Alerts/Audit decisions, and durable browser guards.
|
|
|
|
## UI / Surface Guardrail Impact
|
|
|
|
| Surface / Change | Operator-facing surface change? | Native vs Custom | Shared-Family Relevance | State Layers Touched | Exception Needed? | Low-Impact / `N/A` Note |
|
|
|---|---|---|---|---|---|---|
|
|
| Baseline Compare | yes | Existing Filament Page + Blade view | navigation, shell, breadcrumb, action links | route, shell, page, copy | no | Route/shell hardening only; no redesign. |
|
|
| Environment Dashboard Baseline Compare CTA | yes | Existing Filament/Blade summary/action | dashboard CTA, navigation | URL generation | no | Must emit Environment-owned route, not `environment_id`. |
|
|
| Decision Register regression | yes | Existing Filament Page | workspace hub/filter contract | URL query, shell, chip | no | Regression only; behavior must not change. |
|
|
|
|
## Decision-First Surface Role
|
|
|
|
| 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 |
|
|
|---|---|---|---|---|---|---|---|
|
|
| Baseline Compare | Secondary Context | Operator decides whether the selected Environment has baseline assignment/result work to run or review | Environment identity, assignment state, compare posture, next action | run detail, findings, evidence gaps, compare matrix | It is Environment governance context, not workspace-wide triage | Opened from Environment Dashboard/governance posture | Removes hidden context reconstruction and broken direct links. |
|
|
| Environment Dashboard Baseline Compare CTA | Primary Decision Link | Operator follows Environment posture finding to compare posture | Baseline compare status/action | Baseline Compare page | Link must preserve Environment ownership | Environment Dashboard -> Environment-owned Baseline Compare | One click to the correct scoped page. |
|
|
| Decision Register | Primary Decision Surface | Operator reviews workspace governance decisions | workspace shell, optional Environment filter chip if filtered | finding/exception/evidence links | Regression only | Existing workspace hub | Prevents accidental shell/filter drift. |
|
|
|
|
## Audience-Aware Disclosure
|
|
|
|
| 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 |
|
|
|---|---|---|---|---|---|---|---|
|
|
| Baseline Compare | operator-MSP, support-platform | Environment, assignment state, compare posture, next action | coverage, evidence gaps, run links | raw Graph payloads remain out of scope/default-hidden | Assign baseline, compare now, or review results depending on state | raw payloads/debug details | Shell and copy must say the same Environment truth once. |
|
|
| Environment Dashboard CTA | operator-MSP | Baseline compare card/action | none | none | Open Baseline Compare | N/A | CTA URL itself carries Environment ownership. |
|
|
|
|
## UI/UX Surface Classification
|
|
|
|
| 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 |
|
|
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
| Baseline Compare | Workflow / Page | Environment-owned governance posture page | Open compare, compare now, view run/findings | Page-level Environment route | N/A | Header/actions or existing page sections | Existing Compare Now confirmation stays header/action | N/A | `/admin/workspaces/{workspace}/environments/{environment}/baseline-compare` | Workspace + Environment shell | Baseline Compare | selected Environment, baseline assignment/result state | none |
|
|
| Decision Register | List / Hub | Workspace hub / filtered workspace hub | Review decisions | existing page/table | existing behavior | existing behavior | existing behavior | `/admin/governance/decisions` | existing detail links | Workspace shell, optional filter chip | Decision register | workspace-wide or filtered decision state | regression only |
|
|
|
|
## Operator Surface Contract
|
|
|
|
| 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 |
|
|
|---|---|---|---|---|---|---|---|---|---|---|
|
|
| Baseline Compare | Workspace operator / manager | Decide whether to assign baseline, run compare, or inspect compare outcome for one Environment | Environment-owned posture page | What is this Environment's baseline compare posture? | Environment shell, assignment state, compare status, next action | evidence gaps, run detail links, compare matrix | assignment, compare state, data completeness, governance result | TenantPilot operation queue and Microsoft read/sync behavior as already implemented | Compare now, view findings/run, open matrix | Existing Compare Now is high-impact and must keep confirmation + authorization. |
|
|
|
|
## Proportionality Review *(mandatory when structural complexity is introduced)*
|
|
|
|
- **New source of truth?**: no.
|
|
- **New persisted entity/table/artifact?**: no.
|
|
- **New abstraction?**: no planned abstraction; reuse existing route/shell helpers.
|
|
- **New enum/state/reason family?**: no.
|
|
- **New cross-domain UI framework/taxonomy?**: no.
|
|
- **Current operator problem**: Environment-owned Baseline Compare is not route-owned and can show environment copy only because hidden context exists.
|
|
- **Existing structure is insufficient because**: The current unbound Filament page URL and `environment_id` query helper cannot reliably establish shell/context after environment context is cleared.
|
|
- **Narrowest correct implementation**: Put Baseline Compare on the existing Environment route family, update links/classification, and remove invalid workspace-style access.
|
|
- **Ownership cost**: Focused route/link/page tests and browser smoke for a high-risk page.
|
|
- **Alternative intentionally rejected**: Accept `environment_id` query or remembered session fallback for Baseline Compare; both preserve ambiguity and conflict with Specs 314-317.
|
|
- **Release truth**: Current-release cleanup before production.
|
|
|
|
### Compatibility posture
|
|
|
|
This feature assumes pre-production hard cutover.
|
|
|
|
Backward compatibility, legacy aliases, old workspace-style Baseline Compare routes, redirects, migration shims, and compatibility-specific tests are out of scope.
|
|
|
|
Canonical replacement is preferred over preservation.
|
|
|
|
## Testing / Lane / Runtime Impact *(mandatory for runtime behavior changes)*
|
|
|
|
- **Test purpose / classification**: Unit for classifier/registry helpers; Feature/Livewire for route/access/page rendering and CTA URL generation; Browser for shell/copy/reload/back-forward verification.
|
|
- **Validation lane(s)**: fast-feedback, confidence, browser.
|
|
- **Why this classification and these lanes are sufficient**: Unit/feature tests prove route and authorization contracts; browser smoke is required because the issue is visible shell/copy/navigation drift.
|
|
- **New or expanded test families**: Focused Spec 319 route/shell tests. Browser screenshots under this spec folder. No durable browser framework.
|
|
- **Fixture / helper cost impact**: Existing Workspace, ManagedEnvironment, membership/capability, baseline assignment/snapshot/run factories. Avoid new global seeders or broad helpers.
|
|
- **Heavy-family visibility / justification**: Browser smoke is explicit and limited to Baseline Compare plus Decision Register regression.
|
|
- **Special surface test profile**: global-context-shell.
|
|
- **Standard-native relief or required special coverage**: Special coverage required for route/shell/copy consistency.
|
|
- **Reviewer handoff**: Verify no app code can render Baseline Compare from clean workspace-only URL, filtered workspace-style URL, remembered Environment, or cross-workspace Environment.
|
|
- **Budget / baseline / trend impact**: none expected.
|
|
- **Escalation needed**: none for 319; adjacent surfaces deferred to 320/321/322.
|
|
- **Active feature PR close-out entry**: Guardrail and Smoke Coverage.
|
|
- **Planned validation commands**:
|
|
- `cd apps/platform && ./vendor/bin/sail artisan test --filter=BaselineCompare`
|
|
- `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=DecisionRegister`
|
|
- focused browser smoke for Spec 319 flows
|
|
- `cd apps/platform && ./vendor/bin/sail pint --test`
|
|
- `git diff --check`
|
|
|
|
## User Scenarios & Testing *(mandatory)*
|
|
|
|
### User Story 1 - Open Baseline Compare from Environment context (Priority: P1)
|
|
|
|
As an operator on an Environment Dashboard, I can open Baseline Compare through an Environment-owned URL and see Workspace + Environment shell context, so the page's "this environment" copy is truthful.
|
|
|
|
**Why this priority**: This fixes the highest-risk Spec 318 mismatch and makes the main Environment Dashboard CTA safe.
|
|
|
|
**Independent Test**: From an authorized Workspace A / Environment A context, click the Baseline Compare CTA and assert route, shell, header, copy, and data scope all point to Environment A.
|
|
|
|
**Acceptance Scenarios**:
|
|
|
|
1. **Given** Workspace A and Environment A, **When** the operator opens Baseline Compare from Environment Dashboard, **Then** the URL includes Workspace A and Environment A route identity and no `environment_id` query.
|
|
2. **Given** Environment A has no assigned baseline, **When** Baseline Compare renders, **Then** the shell shows Workspace A + Environment A and copy may say "this environment does not have an assigned baseline yet."
|
|
3. **Given** the operator reloads the page, **When** the browser refreshes, **Then** the same Workspace + Environment shell and copy remain aligned without session fallback.
|
|
|
|
---
|
|
|
|
### User Story 2 - Reject invalid workspace-style access (Priority: P1)
|
|
|
|
As a security reviewer, I need workspace-only and workspace-style filtered Baseline Compare URLs to fail safely, so hidden remembered Environment state cannot render Environment-specific pages.
|
|
|
|
**Why this priority**: The old URL model is the root of the shell/context ambiguity.
|
|
|
|
**Independent Test**: Clear remembered Environment context, open old clean and filtered Baseline Compare URLs, and assert the page does not render.
|
|
|
|
**Acceptance Scenarios**:
|
|
|
|
1. **Given** remembered Environment state exists, **When** it is cleared and the old clean Baseline Compare URL is opened, **Then** the page returns 404/safe no-access or no route and does not render Baseline Compare.
|
|
2. **Given** a workspace-style Baseline Compare URL includes `?environment_id=...`, **When** it is opened, **Then** it does not render as canonical Baseline Compare and does not use the query as a filter.
|
|
3. **Given** Workspace A and Environment B from Workspace B, **When** a URL combines them, **Then** the response is 404/safe no-access and does not reveal Environment B.
|
|
|
|
---
|
|
|
|
### User Story 3 - Preserve workspace hub behavior (Priority: P2)
|
|
|
|
As an operator using Decision Register and other workspace hubs, I need Spec 319 not to regress the clean workspace hub, filtered workspace hub, and clear-filter contracts from Specs 314-316.
|
|
|
|
**Why this priority**: Baseline Compare must move out of ambiguous workspace-style handling without destabilizing the workspace hub model.
|
|
|
|
**Independent Test**: Open Decision Register clean and filtered URLs and verify shell/filter behavior still matches Specs 314-316.
|
|
|
|
**Acceptance Scenarios**:
|
|
|
|
1. **Given** the operator opens Decision Register from workspace sidebar, **When** the page loads, **Then** shell has Workspace only and no active Environment.
|
|
2. **Given** the operator opens Decision Register with `environment_id`, **When** the page loads, **Then** it shows a visible Environment filter chip and shell remains Workspace only.
|
|
3. **Given** the operator clears the Decision Register filter, **When** the page reloads, **Then** URL and state return to the clean workspace hub.
|
|
|
|
### Edge Cases
|
|
|
|
- Environment route contains an Environment from another Workspace.
|
|
- Environment exists but the actor is not entitled to it.
|
|
- Workspace context is missing but the route contains a valid Environment.
|
|
- Environment has no baseline assignment.
|
|
- Environment has assignment but no usable active snapshot.
|
|
- Environment has a running, failed, partially succeeded, or stale compare run.
|
|
- Browser back/forward moves between Environment-owned Baseline Compare and workspace-owned Decision Register.
|
|
- A legacy query includes `tenant`, `tenant_id`, `managed_environment_id`, `tenant_scope`, `environment`, or `tableFilters`.
|
|
- Existing Compare Now action is rendered after the route change and still has confirmation, capability gating, and OperationRun UX.
|
|
|
|
## Requirements *(mandatory)*
|
|
|
|
**Constitution alignment (required):** This feature introduces no Graph calls, no new write behavior, and no new long-running/queued work. Existing Baseline Compare run behavior remains in place and must keep `OperationRun` observability, authorization, confirmation, and tests.
|
|
|
|
**Constitution alignment (PROP-001 / ABSTR-001 / PERSIST-001 / STATE-001 / BLOAT-001):** The spec introduces no new persistence, no new state family, no new broad abstraction, and no new taxonomy. It replaces ambiguous routing with existing Environment route ownership.
|
|
|
|
**Constitution alignment (XCUT-001):** Baseline Compare links and shell state reuse existing route, navigation, shell, and OperationRun helper paths rather than adding a parallel local context mechanism.
|
|
|
|
### Functional Requirements
|
|
|
|
- **FR-001**: Baseline Compare MUST be classified as an Environment-owned page.
|
|
- **FR-002**: Baseline Compare MUST NOT be listed in `WorkspaceHubRegistry` or treated as a workspace hub.
|
|
- **FR-003**: Baseline Compare MUST NOT use `WorkspaceHubEnvironmentFilter` or `environment_id` as its primary access model.
|
|
- **FR-004**: Baseline Compare MUST have a canonical Environment-owned route that includes Workspace and Environment identity.
|
|
- **FR-005**: The canonical route MUST validate that the Environment belongs to the Workspace before rendering.
|
|
- **FR-006**: Cross-workspace Environment route access MUST return 404/safe no-access without workspace switching or existence leakage.
|
|
- **FR-007**: Clean workspace-only Baseline Compare URL MUST NOT render the page.
|
|
- **FR-008**: Workspace-style Baseline Compare URL with `?environment_id=...` MUST NOT render as canonical Baseline Compare.
|
|
- **FR-009**: Baseline Compare MUST NOT resolve Environment from remembered Environment, last Environment, Filament tenant fallback, provider tenant ID, or session fallback.
|
|
- **FR-010**: Valid canonical Baseline Compare route MUST show Workspace + active Environment shell context.
|
|
- **FR-011**: Baseline Compare breadcrumbs/header/copy MUST indicate Environment ownership and align with shell context.
|
|
- **FR-012**: Copy saying "this environment" MUST NOT render without active Environment shell context.
|
|
- **FR-013**: Environment Dashboard Baseline Compare CTA/action MUST use the canonical Environment-owned route.
|
|
- **FR-014**: Environment Dashboard Baseline Compare CTA/action MUST emit no `environment_id`, `tenant`, `tenant_id`, `managed_environment_id`, `tenant_scope`, or `tableFilters`.
|
|
- **FR-015**: Environment sidebar/navigation entry for Baseline Compare, if present, MUST be visibly Environment-owned and route-bound.
|
|
- **FR-016**: Baseline Compare MUST not appear as a workspace-wide sidebar hub.
|
|
- **FR-017**: Existing Compare Now action MUST preserve `->action(...)`, `->requiresConfirmation()`, server-side authorization/capability enforcement, OperationRun start UX, and audit behavior.
|
|
- **FR-018**: Direct route/browser reload MUST keep shell and copy aligned without session fallback.
|
|
- **FR-019**: Browser back/forward between Baseline Compare and Decision Register MUST preserve Environment shell for Baseline Compare and workspace shell for Decision Register.
|
|
- **FR-020**: Decision Register MUST remain a workspace hub and optional filtered workspace hub.
|
|
- **FR-021**: Specs 314, 315, 316, and 317 regression contracts MUST remain green.
|
|
- **FR-022**: Required related Environment-owned pages MUST be inspected and fixed only if the same mismatch is proven: Required Permissions, Provider Readiness/Diagnostics, Inventory/Coverage, Backup/Restore Environment-owned views, Drift/Findings Environment-owned pages, and Environment-owned baseline drilldowns.
|
|
- **FR-023**: Workspace-owned baseline analysis/library surfaces such as Baselines and Baseline Snapshots MUST remain out of scope for Spec 319 unless repo analysis proves they are actually Environment-owned.
|
|
- **FR-024**: Alerts and Audit Log Environment filter decisions MUST remain out of scope for Spec 321.
|
|
- **FR-025**: No migrations, seeders, packages, env vars, queues, scheduler, storage, or deployment asset changes are allowed unless implementation proves the route fix is impossible without one and the spec is updated first.
|
|
|
|
### Non-Functional Requirements
|
|
|
|
- **NFR-001**: Access failures must use deny-as-not-found semantics for membership and cross-workspace isolation.
|
|
- **NFR-002**: Page render must not make Graph calls.
|
|
- **NFR-003**: Route/link changes must not add measurable query overhead beyond existing scoped model resolution.
|
|
- **NFR-004**: Tests must use the narrowest honest lane and keep browser coverage focused.
|
|
- **NFR-005**: Filament v5 / Livewire v4 compliance must be preserved.
|
|
- **NFR-006**: Laravel 12 panel provider registration remains in `apps/platform/bootstrap/providers.php`.
|
|
- **NFR-007**: No new Filament assets are planned; deployment `filament:assets` requirement does not change.
|
|
|
|
### Key Entities
|
|
|
|
- **Workspace**: Primary platform context and route owner.
|
|
- **ManagedEnvironment**: Secondary operational context inside a Workspace; route-bound for Environment-owned pages.
|
|
- **BaselineProfile**: Workspace-owned baseline definition.
|
|
- **BaselineTenantAssignment**: Existing Environment assignment to a baseline profile.
|
|
- **BaselineSnapshot**: Existing immutable baseline artifact.
|
|
- **OperationRun**: Existing observable run truth for Baseline Compare execution.
|
|
|
|
## Current Repo Findings To Preserve
|
|
|
|
- Spec 318 classified Baseline Compare as "Environment page implemented on unbound URL" with high risk.
|
|
- `BaselineCompareLanding` is registered in `AdminPanelProvider` as a Filament Page and currently uses `UsesAdminEnvironmentFilterQueryParameter`.
|
|
- `BaselineCompareLanding::canAccess()` requires `resolveTenantContextForCurrentPanel()` to produce a `ManagedEnvironment`.
|
|
- `BaselineCompareStats::forTenant()` makes Baseline Compare behavior Environment-specific.
|
|
- Environment Dashboard summary and widgets currently call `BaselineCompareLanding::getUrl(tenant: $tenant)`, which produces `/admin/baseline-compare-landing?environment_id=...`.
|
|
- `OperateHubShell::resolveQueryTenantHint()` currently resolves `tenant` and `managed_environment_id`, not canonical `environment_id`.
|
|
- `WorkspaceHubRegistry` does not include Baseline Compare, which is correct and must remain true.
|
|
- Direct clean and direct `environment_id` Baseline Compare URLs fail after context clear, proving the current URL is not self-sufficient.
|
|
|
|
## Out of Scope
|
|
|
|
- Decision Register behavior changes beyond regression coverage.
|
|
- Workspace hub behavior changes from Specs 314-316.
|
|
- `environment_id` filter support for Baseline Compare.
|
|
- Workspace-wide Baseline Compare.
|
|
- Spec 320 workspace-owned analysis page registration and shell cutover.
|
|
- Spec 321 Alerts/Audit Log Environment filter decision.
|
|
- Spec 322 durable browser no-drift regression infrastructure.
|
|
- Baseline feature redesign.
|
|
- Baseline assignment semantic changes except where route correctness requires link/access updates.
|
|
- Migrations, seeders, packages, env vars, queues, scheduler, storage, or deployment asset changes.
|
|
- Compatibility redirects, dual routes, legacy aliases, or remembered fallback support.
|
|
|
|
## Acceptance Criteria
|
|
|
|
### Baseline Compare Classification
|
|
|
|
- [x] Baseline Compare is classified as Environment-owned.
|
|
- [x] Baseline Compare is not in `WorkspaceHubRegistry`.
|
|
- [x] Baseline Compare is not treated as a filtered workspace hub.
|
|
- [x] Baseline Compare does not use `environment_id` as primary access contract.
|
|
- [x] Baseline Compare does not use remembered Environment fallback.
|
|
|
|
### Baseline Compare Route Contract
|
|
|
|
- [x] Canonical Baseline Compare route includes Environment ownership.
|
|
- [x] Environment belongs to Workspace.
|
|
- [x] Cross-workspace Environment is rejected.
|
|
- [x] Clean workspace-only Baseline Compare route is removed, invalid, or safe no-access.
|
|
- [x] Workspace-style `?environment_id=...` Baseline Compare route is removed, invalid, or non-canonical.
|
|
- [x] No legacy query params are accepted.
|
|
|
|
### Shell / Breadcrumb / Copy
|
|
|
|
- [x] Baseline Compare shell shows Workspace + Environment.
|
|
- [x] Breadcrumbs indicate Environment ownership.
|
|
- [x] Header/copy agrees with Environment ownership.
|
|
- [x] "This environment" copy never appears without active Environment shell context.
|
|
- [x] Reload keeps shell/copy aligned.
|
|
- [x] Browser back/forward keeps shell/copy aligned.
|
|
|
|
### Navigation / CTA
|
|
|
|
- [x] Environment Dashboard Baseline Compare CTA uses Environment-owned route.
|
|
- [x] CTA emits no `environment_id`.
|
|
- [x] CTA emits no `tenant`.
|
|
- [x] CTA emits no `tenant_id`.
|
|
- [x] CTA emits no `managed_environment_id`.
|
|
- [x] CTA emits no `tenant_scope`.
|
|
- [x] CTA emits no `tableFilters`.
|
|
- [x] Baseline Compare does not appear as workspace-wide sidebar hub.
|
|
|
|
### Related Environment Pages
|
|
|
|
- [x] Any touched Environment-owned page keeps Environment shell context.
|
|
- [x] No touched Environment-owned page becomes workspace hub by accident.
|
|
- [x] No touched Environment-owned page uses remembered Environment fallback.
|
|
|
|
### Regression
|
|
|
|
- [x] Decision Register remains workspace hub.
|
|
- [x] Workspace hub clean entry from Spec 314 remains green.
|
|
- [x] Environment CTA `environment_id` contract from Spec 315 remains green for workspace hubs.
|
|
- [x] Clear filter contract from Spec 316 remains green.
|
|
- [x] Legacy Tenant cleanup from Spec 317 remains green.
|
|
- [x] Spec 318 mismatch is resolved for Baseline Compare.
|
|
|
|
### Tests / Browser
|
|
|
|
- [x] Required tests added.
|
|
- [x] Existing tests updated only if they asserted broken old behavior.
|
|
- [x] No broad rebaseline.
|
|
- [x] Focused browser verification completed.
|
|
- [x] Screenshots saved where useful.
|
|
- [x] `git diff --check` passes.
|
|
- [x] Formatting passes.
|
|
|
|
## Browser Verification Required
|
|
|
|
### Flow A - Environment Dashboard CTA
|
|
|
|
1. Open Environment Dashboard.
|
|
2. Click Baseline Compare CTA/action.
|
|
3. Verify URL is Environment-owned route.
|
|
4. Verify URL has no `environment_id` or legacy params.
|
|
5. Verify shell shows Workspace + Environment.
|
|
6. Verify breadcrumb/header/copy align.
|
|
7. Capture screenshot:
|
|
|
|
```text
|
|
specs/319-environment-owned-surface-routing-shell-context-contract/artifacts/screenshots/environment-cta--baseline-compare.png
|
|
```
|
|
|
|
### Flow B - Direct Clean Workspace URL
|
|
|
|
1. Open old/clean workspace-only Baseline Compare URL if route exists.
|
|
2. Verify page does not render as valid Baseline Compare.
|
|
3. Verify no remembered Environment fallback.
|
|
4. Capture screenshot or document route absence:
|
|
|
|
```text
|
|
specs/319-environment-owned-surface-routing-shell-context-contract/artifacts/screenshots/direct-clean--baseline-compare--rejected.png
|
|
```
|
|
|
|
### Flow C - Direct Workspace Filtered URL
|
|
|
|
1. Open workspace-style Baseline Compare URL with `?environment_id=...` if route exists.
|
|
2. Verify this is not canonical and does not render valid Baseline Compare.
|
|
3. Capture screenshot or document route absence:
|
|
|
|
```text
|
|
specs/319-environment-owned-surface-routing-shell-context-contract/artifacts/screenshots/direct-filtered--baseline-compare--rejected.png
|
|
```
|
|
|
|
### Flow D - Reload
|
|
|
|
1. Open canonical Environment Baseline Compare route.
|
|
2. Reload.
|
|
3. Verify shell remains Workspace + Environment.
|
|
4. Verify no fallback/session mismatch.
|
|
5. Capture screenshot:
|
|
|
|
```text
|
|
specs/319-environment-owned-surface-routing-shell-context-contract/artifacts/screenshots/baseline-compare--after-reload.png
|
|
```
|
|
|
|
### Flow E - Back/Forward
|
|
|
|
1. Environment Dashboard -> Baseline Compare.
|
|
2. Navigate to Decision Register.
|
|
3. Browser back.
|
|
4. Verify Baseline Compare has Environment shell.
|
|
5. Browser forward.
|
|
6. Verify Decision Register has Workspace shell and no active Environment unless filtered.
|
|
7. Capture screenshots where useful:
|
|
|
|
```text
|
|
specs/319-environment-owned-surface-routing-shell-context-contract/artifacts/screenshots/baseline-compare--back-forward.png
|
|
specs/319-environment-owned-surface-routing-shell-context-contract/artifacts/screenshots/decision-register--regression.png
|
|
```
|
|
|
|
## Success Criteria *(mandatory)*
|
|
|
|
- **SC-001**: 100% of Baseline Compare entry points generated by Environment Dashboard and related Environment widgets use the canonical Environment-owned route.
|
|
- **SC-002**: 0 valid Baseline Compare renders occur from clean workspace-only URLs or workspace-style `environment_id` URLs.
|
|
- **SC-003**: Cross-workspace Environment route attempts return 404/safe no-access in focused tests.
|
|
- **SC-004**: Browser verification shows shell/copy consistency for CTA, reload, and back/forward flows.
|
|
- **SC-005**: Existing workspace hub regression tests for Decision Register and Specs 314-316 remain green.
|
|
|
|
## Assumptions
|
|
|
|
- The canonical route should follow the repo's existing Environment route family, preferably `/admin/workspaces/{workspace}/environments/{environment}/baseline-compare` unless implementation analysis finds a closer established route name.
|
|
- `ManagedEnvironment` route key remains the current slug route key used by other Environment routes.
|
|
- Existing Baseline Compare data, stats, Compare Now action, and OperationRun behavior are correct and need route/shell hardening only.
|
|
- There is no production data and no external consumer requiring old URLs.
|
|
|
|
## Risks
|
|
|
|
- Existing tests may assert remembered Environment fallback for Baseline Compare and must be updated because that behavior is now explicitly invalid.
|
|
- Filament Page registration may require an explicit route override instead of a simple trait; implementation must use the narrowest repo-consistent path.
|
|
- Environment navigation may currently mix workspace-owned baseline surfaces with Environment-owned Baseline Compare; implementation must not pull Baselines/Baseline Snapshots into this spec.
|
|
- Browser screenshots may require a running Sail/Vite/browser setup; if deterministic browser setup is blocked, document the blocker and capture available proof.
|
|
|
|
## Open Questions
|
|
|
|
None blocking implementation. If implementation discovers that Baseline Compare cannot be placed on the existing Environment route family without a new route helper or page registration pattern, update `plan.md` before coding.
|
|
|
|
## Follow-Up Spec Candidates
|
|
|
|
- **320 - Workspace-Owned Analysis Surface Registration & Shell Cutover**: Baselines, Baseline Snapshots, and workspace-owned analysis pages that inherit Environment shell context.
|
|
- **321 - Alerts / Audit Log Environment Filter Contract Decision**: decide whether to support `environment_id` with visible chip/clear or reject it.
|
|
- **322 - Browser No-Drift Regression Guard**: durable browser regression coverage for workspace hubs, Environment-owned pages, filtered hubs, clear/reload/back behavior, and no legacy alias resurrection.
|
|
|
|
## Required Final Report
|
|
|
|
Implementation close-out must report:
|
|
|
|
```text
|
|
Spec 319 completed.
|
|
|
|
Changed behavior:
|
|
...
|
|
|
|
Baseline Compare classification:
|
|
...
|
|
|
|
Canonical route:
|
|
...
|
|
|
|
Removed/invalidated routes:
|
|
...
|
|
|
|
Files changed:
|
|
...
|
|
|
|
Tests:
|
|
- command:
|
|
- result:
|
|
|
|
Browser verification:
|
|
...
|
|
|
|
Remaining follow-ups:
|
|
- 320:
|
|
- 321:
|
|
- 322:
|
|
|
|
No migrations were created.
|
|
No seeders were changed.
|
|
No packages, env vars, queues, scheduler, storage, or deployment asset changes were made.
|
|
No backwards compatibility layer was introduced.
|
|
No legacy query alias support was added.
|
|
```
|
|
|
|
Also include whether Baseline Compare was removed from workspace hub handling, whether Environment Dashboard CTA was updated, how cross-workspace Environment access is handled, whether clean workspace-only URL is removed or returns safe no-access, screenshot paths if generated, and known unrelated residual test failures.
|