diff --git a/docs/implplan/SPRINT_20260307_015_FE_mission_board_environment_action_scope.md b/docs/implplan/SPRINT_20260307_015_FE_mission_board_environment_action_scope.md new file mode 100644 index 000000000..87b9b25d1 --- /dev/null +++ b/docs/implplan/SPRINT_20260307_015_FE_mission_board_environment_action_scope.md @@ -0,0 +1,86 @@ +# Sprint 20260307-015 - FE Mission Board Environment Action Scope + +## Topic & Scope +- Repair mission-board environment actions so they pass canonical environment and region scope into downstream topology and findings pages. +- Replace synthetic dashboard-only environment IDs in action links with route/query shapes the real pages actually understand. +- Add focused Angular coverage for the board action targets, then replay the affected actions with live Playwright from the authenticated mission board. +- Working directory: `src/Web/StellaOps.Web`. +- Expected evidence: focused Angular tests, live Playwright action checks on `https://stella-ops.local/mission-control/board`, and sprint execution log updates. + +## Dependencies & Concurrency +- Depends on the earlier mission-board route fixes plus the release-health shared environment work (`SPRINT_20260307_012` and `SPRINT_20260307_013`) because the target topology/findings surfaces already expect canonical environment scope. +- Safe parallelism: stay inside `src/Web/StellaOps.Web` plus sprint updates; do not touch unrelated navigation/settings/sidebar work already in progress from other agents. +- Scope is limited to mission-board environment action routing, not a full dashboard data-model rewrite. + +## Documentation Prerequisites +- `src/Web/StellaOps.Web/AGENTS.md` +- `src/Web/StellaOps.Web/src/app/features/dashboard-v3/dashboard-v3.component.ts` +- `src/Web/StellaOps.Web/src/app/features/topology/environment-posture-page.component.ts` +- `src/Web/StellaOps.Web/src/app/core/context/platform-context.store.ts` + +## Delivery Tracker + +### FE-DASH-001 - Reproduce mission-board environment action scope loss +Status: DONE +Dependency: none +Owners: QA +Task description: +- Replay mission-board actions from the live authenticated shell with Playwright. +- Confirm whether environment-specific actions preserve usable scope in downstream pages. + +Completion criteria: +- [x] Live Playwright captures concrete mission-board action targets. +- [x] The broken action is reduced to a specific route/query mismatch. + +### FE-DASH-002 - Replace synthetic environment action IDs with canonical scope +Status: DONE +Dependency: FE-DASH-001 +Owners: Developer +Task description: +- Update the mission-board environment card/table model so action links use canonical environment IDs plus region scope instead of composite placeholder IDs. +- Preserve current board rendering while making downstream routes receive valid scope values. + +Completion criteria: +- [x] Environment card `Detail` routes target the canonical topology posture path for the selected environment. +- [x] Environment card `Findings` routes preserve valid region/environment scope instead of `env=`. +- [x] Risk-table environment actions preserve the same canonical scope. + +### FE-DASH-003 - Add focused Angular coverage for board action targets +Status: DONE +Dependency: FE-DASH-002 +Owners: Test Automation +Task description: +- Add focused tests around the mission-board action helpers or rendered links. +- Verify the tests assert concrete route/query outputs for at least one environment card and one risk-table row. + +Completion criteria: +- [x] Focused Angular tests prove the board emits canonical topology/findings targets. +- [x] Tests fail before the fix and pass after it. + +### FE-DASH-004 - Replay the affected mission-board actions live +Status: DONE +Dependency: FE-DASH-003 +Owners: QA +Task description: +- Re-run the mission-board environment actions with live Playwright after the Web bundle is rebuilt. +- Confirm the downstream pages load with the intended environment scope instead of dropping to all-environment views. + +Completion criteria: +- [x] Live Playwright confirms the selected environment `Detail` action lands on the correct topology posture route. +- [x] Live Playwright confirms the selected environment `Findings` action preserves environment/region scope on the target page. + +## Execution Log +| Date (UTC) | Update | Owner | +| --- | --- | --- | +| 2026-03-07 | Sprint created after live Playwright on `/mission-control/board` showed mission-board environment actions using synthetic IDs such as `env=dev-eu-west`, causing the downstream findings page to fall back to `All regions / All environments`. | QA | +| 2026-03-07 | Rebound `DashboardV3Component` environment actions to canonical region/environment scope from the live Platform context store and added focused coverage in `src/app/core/testing/dashboard-v3.component.spec.ts`; `npx ng test --watch=false --include src/app/core/testing/dashboard-v3.component.spec.ts` passed. | Developer | +| 2026-03-07 | Replayed the first mission-board environment card `Detail` and `Findings` actions plus the first risk-table `Open` action with live Playwright. The targets now preserve `regions=&environments=` and the downstream pages render the expected scoped context (for example `US East / Development` and `US East / Staging`). | QA | + +## Decisions & Risks +- Decision: treat the defect as a board action-contract problem rather than patching the findings page to understand synthetic dashboard IDs, because the board is the component inventing the invalid token. +- Decision: source mission-board action identities from `PlatformContextStore.environments()` so downstream routes follow the same canonical environment catalog the rest of the shell uses. +- Risk: `DashboardV3Component` still uses placeholder data, so the fix must stay scoped to action semantics and avoid broadening into a data-source rewrite during this iteration. + +## Next Checkpoints +- 2026-03-07: land the mission-board action-scope fix and focused tests. +- 2026-03-07: replay the affected board actions with live Playwright and continue the authenticated page/action sweep. diff --git a/src/Web/StellaOps.Web/src/app/core/testing/dashboard-v3.component.spec.ts b/src/Web/StellaOps.Web/src/app/core/testing/dashboard-v3.component.spec.ts new file mode 100644 index 000000000..5dccada90 --- /dev/null +++ b/src/Web/StellaOps.Web/src/app/core/testing/dashboard-v3.component.spec.ts @@ -0,0 +1,104 @@ +import { TestBed } from '@angular/core/testing'; +import { provideRouter } from '@angular/router'; + +import { PlatformContextStore } from '../context/platform-context.store'; +import { DashboardV3Component } from '../../features/dashboard-v3/dashboard-v3.component'; + +describe('DashboardV3Component', () => { + it('builds canonical topology posture targets for environment cards', () => { + TestBed.configureTestingModule({ + imports: [DashboardV3Component], + providers: [ + provideRouter([]), + { + provide: PlatformContextStore, + useValue: { + initialize: () => undefined, + regions: () => [ + { regionId: 'eu-west', displayName: 'EU West' }, + { regionId: 'us-east', displayName: 'US East' }, + ], + environments: () => [ + { + environmentId: 'dev', + regionId: 'eu-west', + environmentType: 'development', + displayName: 'Development EU West', + }, + { + environmentId: 'prod-us-east', + regionId: 'us-east', + environmentType: 'production', + displayName: 'Production US East', + }, + ], + }, + }, + ], + }); + + const fixture = TestBed.createComponent(DashboardV3Component); + const component = fixture.componentInstance; + const environment = component.filteredEnvironments()[0]; + + expect(component.environmentPostureRoute(environment)).toEqual([ + '/setup/topology/environments', + 'dev', + 'posture', + ]); + expect(component.environmentScopeQuery(environment)).toEqual({ + region: 'eu-west', + regions: 'eu-west', + environment: 'dev', + environments: 'dev', + }); + }); + + it('builds canonical findings scope for downstream pages instead of synthetic dashboard ids', () => { + TestBed.configureTestingModule({ + imports: [DashboardV3Component], + providers: [ + provideRouter([]), + { + provide: PlatformContextStore, + useValue: { + initialize: () => undefined, + regions: () => [ + { regionId: 'eu-west', displayName: 'EU West' }, + { regionId: 'us-east', displayName: 'US East' }, + ], + environments: () => [ + { + environmentId: 'dev', + regionId: 'eu-west', + environmentType: 'development', + displayName: 'Development EU West', + }, + { + environmentId: 'prod', + regionId: 'us-east', + environmentType: 'production', + displayName: 'Production US East', + }, + ], + }, + }, + ], + }); + + const fixture = TestBed.createComponent(DashboardV3Component); + const component = fixture.componentInstance; + const environment = component.filteredEnvironments().find((candidate) => candidate.regionId === 'us-east'); + + expect(environment).toBeDefined(); + expect(component.environmentScopeQuery(environment!)).toEqual({ + region: 'us-east', + regions: 'us-east', + environment: 'prod', + environments: 'prod', + }); + expect(component.environmentScopeQuery(environment!)).not.toEqual( + jasmine.objectContaining({ env: 'prod-us-east' }), + ); + }); +}); diff --git a/src/Web/StellaOps.Web/src/app/features/dashboard-v3/dashboard-v3.component.ts b/src/Web/StellaOps.Web/src/app/features/dashboard-v3/dashboard-v3.component.ts index e80c2f67c..ae6fac111 100644 --- a/src/Web/StellaOps.Web/src/app/features/dashboard-v3/dashboard-v3.component.ts +++ b/src/Web/StellaOps.Web/src/app/features/dashboard-v3/dashboard-v3.component.ts @@ -9,14 +9,21 @@ import { Component, ChangeDetectionStrategy, - signal, computed, + inject, + signal, } from '@angular/core'; import { TitleCasePipe, UpperCasePipe } from '@angular/common'; import { RouterLink } from '@angular/router'; +import { + PlatformContextEnvironment, + PlatformContextStore, +} from '../../core/context/platform-context.store'; interface EnvironmentCard { id: string; + environmentId: string; + regionId: string; name: string; region: string; deployStatus: 'healthy' | 'degraded' | 'blocked' | 'unknown'; @@ -66,9 +73,9 @@ interface MissionSummary { (change)="onRegionChange($event)" > - - - + @for (region of availableRegions(); track region.value) { + + } @@ -174,10 +181,20 @@ interface MissionSummary {