## 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
35 KiB
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_idURLs 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_idfilter 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.idbefore 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,ManagedEnvironmentLinksfor 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-landingplus 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, andOperationRunLinks. - 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_idquery 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_idquery 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=BaselineComparecd apps/platform && ./vendor/bin/sail artisan test --filter=AdminSurfaceScopecd apps/platform && ./vendor/bin/sail artisan test --filter=WorkspaceHubRegistrycd apps/platform && ./vendor/bin/sail artisan test --filter=DecisionRegister- focused browser smoke for Spec 319 flows
cd apps/platform && ./vendor/bin/sail pint --testgit 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:
- 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_idquery. - 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."
- 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:
- 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.
- 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. - 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:
- Given the operator opens Decision Register from workspace sidebar, When the page loads, Then shell has Workspace only and no active Environment.
- 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. - 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, ortableFilters. - 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
WorkspaceHubRegistryor treated as a workspace hub. - FR-003: Baseline Compare MUST NOT use
WorkspaceHubEnvironmentFilterorenvironment_idas 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, ortableFilters. - 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:assetsrequirement 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.
BaselineCompareLandingis registered inAdminPanelProvideras a Filament Page and currently usesUsesAdminEnvironmentFilterQueryParameter.BaselineCompareLanding::canAccess()requiresresolveTenantContextForCurrentPanel()to produce aManagedEnvironment.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 resolvestenantandmanaged_environment_id, not canonicalenvironment_id.WorkspaceHubRegistrydoes not include Baseline Compare, which is correct and must remain true.- Direct clean and direct
environment_idBaseline 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_idfilter 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
- Baseline Compare is classified as Environment-owned.
- Baseline Compare is not in
WorkspaceHubRegistry. - Baseline Compare is not treated as a filtered workspace hub.
- Baseline Compare does not use
environment_idas primary access contract. - Baseline Compare does not use remembered Environment fallback.
Baseline Compare Route Contract
- Canonical Baseline Compare route includes Environment ownership.
- Environment belongs to Workspace.
- Cross-workspace Environment is rejected.
- Clean workspace-only Baseline Compare route is removed, invalid, or safe no-access.
- Workspace-style
?environment_id=...Baseline Compare route is removed, invalid, or non-canonical. - No legacy query params are accepted.
Shell / Breadcrumb / Copy
- Baseline Compare shell shows Workspace + Environment.
- Breadcrumbs indicate Environment ownership.
- Header/copy agrees with Environment ownership.
- "This environment" copy never appears without active Environment shell context.
- Reload keeps shell/copy aligned.
- Browser back/forward keeps shell/copy aligned.
Navigation / CTA
- Environment Dashboard Baseline Compare CTA uses Environment-owned route.
- CTA emits no
environment_id. - CTA emits no
tenant. - CTA emits no
tenant_id. - CTA emits no
managed_environment_id. - CTA emits no
tenant_scope. - CTA emits no
tableFilters. - Baseline Compare does not appear as workspace-wide sidebar hub.
Related Environment Pages
- Any touched Environment-owned page keeps Environment shell context.
- No touched Environment-owned page becomes workspace hub by accident.
- No touched Environment-owned page uses remembered Environment fallback.
Regression
- Decision Register remains workspace hub.
- Workspace hub clean entry from Spec 314 remains green.
- Environment CTA
environment_idcontract from Spec 315 remains green for workspace hubs. - Clear filter contract from Spec 316 remains green.
- Legacy Tenant cleanup from Spec 317 remains green.
- Spec 318 mismatch is resolved for Baseline Compare.
Tests / Browser
- Required tests added.
- Existing tests updated only if they asserted broken old behavior.
- No broad rebaseline.
- Focused browser verification completed.
- Screenshots saved where useful.
git diff --checkpasses.- Formatting passes.
Browser Verification Required
Flow A - Environment Dashboard CTA
- Open Environment Dashboard.
- Click Baseline Compare CTA/action.
- Verify URL is Environment-owned route.
- Verify URL has no
environment_idor legacy params. - Verify shell shows Workspace + Environment.
- Verify breadcrumb/header/copy align.
- Capture screenshot:
specs/319-environment-owned-surface-routing-shell-context-contract/artifacts/screenshots/environment-cta--baseline-compare.png
Flow B - Direct Clean Workspace URL
- Open old/clean workspace-only Baseline Compare URL if route exists.
- Verify page does not render as valid Baseline Compare.
- Verify no remembered Environment fallback.
- Capture screenshot or document route absence:
specs/319-environment-owned-surface-routing-shell-context-contract/artifacts/screenshots/direct-clean--baseline-compare--rejected.png
Flow C - Direct Workspace Filtered URL
- Open workspace-style Baseline Compare URL with
?environment_id=...if route exists. - Verify this is not canonical and does not render valid Baseline Compare.
- Capture screenshot or document route absence:
specs/319-environment-owned-surface-routing-shell-context-contract/artifacts/screenshots/direct-filtered--baseline-compare--rejected.png
Flow D - Reload
- Open canonical Environment Baseline Compare route.
- Reload.
- Verify shell remains Workspace + Environment.
- Verify no fallback/session mismatch.
- Capture screenshot:
specs/319-environment-owned-surface-routing-shell-context-contract/artifacts/screenshots/baseline-compare--after-reload.png
Flow E - Back/Forward
- Environment Dashboard -> Baseline Compare.
- Navigate to Decision Register.
- Browser back.
- Verify Baseline Compare has Environment shell.
- Browser forward.
- Verify Decision Register has Workspace shell and no active Environment unless filtered.
- Capture screenshots where useful:
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_idURLs. - 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-compareunless implementation analysis finds a closer established route name. ManagedEnvironmentroute 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_idwith 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:
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.