diff --git a/docs/implplan/SPRINT_20260306_003_FE_playwright_setup_reset_iteration_loop.md b/docs/implplan/SPRINT_20260306_003_FE_playwright_setup_reset_iteration_loop.md new file mode 100644 index 000000000..90b2f4e86 --- /dev/null +++ b/docs/implplan/SPRINT_20260306_003_FE_playwright_setup_reset_iteration_loop.md @@ -0,0 +1,93 @@ +# Sprint 20260306-003 - Web Playwright Setup/Reset Iteration Loop + +## Topic & Scope +- Run repeated Web QA/developer iterations against `https://stella-ops.local` using Playwright as the primary Tier 2 verification method. +- Treat an iteration as: verify runtime state, exercise real page flows/actions, capture defects, diagnose root cause, fix within Web scope, and retest with fresh evidence. +- Keep the work bounded to SPA/runtime-facing defects in the Web module; avoid repo-wide builds, mass tests, and unrelated cross-module churn. +- Working directory: `src/Web/StellaOps.Web`. +- Expected evidence: Playwright interaction logs/screenshots, targeted FE tests only when needed, updated UI docs if behavior changes, and sprint execution log entries. + +## Dependencies & Concurrency +- Runtime dependency: `https://stella-ops.local` must remain reachable with the current compose stack. +- Existing active work in `SPRINT_20260306_001` and `SPRINT_20260306_002` is treated as parallel ownership; avoid editing their in-flight search-context files unless a confirmed Web defect forces a coordinated fix. +- Cross-module edits are not planned for this sprint. If a root cause is confirmed outside `src/Web/StellaOps.Web`, record it and stop at triage inside this sprint. +- Safe parallelism: + - Route exploration, selector mapping, and evidence capture can proceed without touching source files. + - Code changes begin only after a defect is reproduced with fresh Playwright evidence. + +## Documentation Prerequisites +- `docs/qa/feature-checks/FLOW.md` +- `docs/code-of-conduct/TESTING_PRACTICES.md` +- `docs/code-of-conduct/CODE_OF_CONDUCT.md` +- `docs/modules/ui/architecture.md` +- `src/Web/StellaOps.Web/AGENTS.md` + +## Delivery Tracker + +### FE-QA-LOOP-001 - Establish bounded full-iteration workflow +Status: DOING +Dependency: none +Owners: QA, Developer (FE) +Task description: +- Define and execute a Web-only iteration loop that does not degrade into shallow page pings. +- Use Playwright to drive real route interactions, dialogs, filters, forms, keyboard flows, and negative/error paths where the route semantics support them. +- Record concrete issue evidence before any fixes are attempted. + +Completion criteria: +- [ ] Fresh Playwright evidence exists for a full route/action sweep against the running stack. +- [ ] The sweep records route-specific interactions, not only status codes or generic button clicks. +- [ ] Any discovered defects are carried into triage instead of skipped. + +### FE-QA-LOOP-002 - Reproduce and triage confirmed Web defects +Status: DOING +Dependency: FE-QA-LOOP-001 +Owners: QA, Developer (FE) +Task description: +- For each confirmed issue in scope, capture the failing user transaction, isolate the route/component/service boundary involved, and classify the defect. +- Keep a strict problems-first loop: do not move to a new defect until the current one is triaged to fix or blocked. + +Completion criteria: +- [ ] Each confirmed issue has reproducible steps and captured evidence. +- [ ] Root cause is documented with affected Web files or explicitly marked out-of-scope. +- [ ] Concurrency risks are noted when another agent owns overlapping files. + +### FE-QA-LOOP-003 - Implement scoped fixes and retest +Status: DOING +Dependency: FE-QA-LOOP-002 +Owners: Developer (FE), Test Automation +Task description: +- Apply minimal Web-only fixes for confirmed defects and add focused regression coverage where practical. +- Prefer targeted tests and route-specific Playwright replays over heavy workspace builds. + +Completion criteria: +- [ ] Each fix has fresh Playwright retest evidence. +- [ ] Any new FE automated coverage is targeted and memory-safe. +- [ ] Docs are updated when behavior or operator workflow changes. + +## Execution Log +| Date (UTC) | Update | Owner | +| --- | --- | --- | +| 2026-03-06 | Sprint created to isolate Web-only full QA/developer iterations on `https://stella-ops.local` using Playwright-first behavioral verification. | Project Manager | +| 2026-03-06 | FE-QA-LOOP-001 started. Preconditions read: QA FLOW, testing practices, code of conduct, UI architecture, and Web AGENTS. Runtime preflight confirms `stella-ops.local` is reachable and the compose stack is healthy. | QA | +| 2026-03-06 | Real authenticated Playwright probe found canonical-route drift hidden by prior shallow sweeps: `/security/advisories-vex`, `/ops/policy/overview`, and `/ops/policy/risk-budget` stayed on their requested URLs but rendered dashboard fallback content instead of the target surfaces. | QA | +| 2026-03-06 | Root cause confirmed in active router trees: `app.routes.ts` mounts `security-risk.routes.ts` (missing `advisories-vex`) and `ops.routes.ts` mounts `policy-governance.routes.ts` (missing `overview` and `risk-budget` aliases) while existing code/tests still deep-link to those canonical paths. | Developer (FE) | +| 2026-03-06 | Fixed canonical route gaps in `security-risk.routes.ts` and `policy-governance.routes.ts`, corrected stale governance card links to canonical policy paths, and tightened `prealpha-canonical-full-sweep.spec.ts` with route-specific heading/title assertions so dashboard fallback cannot pass silently. | Developer (FE) | +| 2026-03-06 | Verification: targeted Playwright regression slice passed locally (`npx playwright test tests/e2e/prealpha-canonical-full-sweep.spec.ts --grep \"advisories-vex|ops/policy$|ops/policy/overview|ops/policy/risk-budget\"` -> 4/4 pass). Angular console bundle rebuilt and synced into `compose_console-dist`; real authenticated Playwright probe against `https://stella-ops.local` confirmed all three routes now render their intended headings. | QA | +| 2026-03-06 | Live mission-board interaction probe found a second routing defect: the visible `View SBOM` action linked to `/security/sbom/lake`, which stayed on that URL but rendered dashboard fallback content. Direct navigation proved the intended surface is `/security/sbom-lake`. | QA | +| 2026-03-06 | Fixed the mission-board SBOM action to point at `/security/sbom-lake` and extended `prealpha-canonical-full-sweep.spec.ts` to cover both the canonical SBOM Lake route and the dashboard click-through path. | Developer (FE) | +| 2026-03-06 | Verification: targeted Playwright slice passed locally (`npx playwright test tests/e2e/prealpha-canonical-full-sweep.spec.ts --grep \"sbom-lake|mission board SBOM card opens SBOM Lake\"` -> 2/2 pass). Frontend bundle rebuilt, synced into `compose_console-dist`, and live authenticated Playwright confirmed the mission-board `View SBOM` action now lands on `https://stella-ops.local/security/sbom-lake` with `SBOM Lake` / `Attestation Coverage Metrics` visible. | QA | + +## Decisions & Risks +- Decision: this sprint stays inside `src/Web/StellaOps.Web` plus required sprint/doc updates only. +- Decision: Playwright is the primary behavioral verification tool; existing shallow sweep scripts are reference material, not acceptance evidence. +- Decision: avoid heavy solution-wide builds/tests due to memory constraints; use targeted FE checks only when a fix requires them. +- Decision: canonical route regressions must assert route-specific titles/headings, not only that the URL and shell remain visible. This aligns the implementation with `docs/modules/ui/v2-rewire/S00_route_deprecation_map.md`. +- Risk: concurrent agents are actively modifying search-related Web files. +- Mitigation: avoid those files unless a reproduced defect proves they are the root cause; record any overlap before editing. +- Risk: some visible failures may originate from backend APIs rather than Web code. +- Mitigation: capture the exact failing route/action and stop at triage if the root cause leaves Web scope. + +## Next Checkpoints +- 2026-03-06: Complete first fresh Playwright route/action sweep and defect list. +- 2026-03-06: Triage the first confirmed in-scope defect to root cause. +- 2026-03-07: Land first scoped fix with fresh retest evidence and a small commit. 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 0931c617b..1a39afaa5 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 @@ -243,7 +243,7 @@ interface MissionSummary {

SBOM Findings Snapshot

- View SBOM + View SBOM
@@ -1147,4 +1147,3 @@ export class DashboardV3Component { } - diff --git a/src/Web/StellaOps.Web/src/app/features/policy-governance/policy-governance.routes.ts b/src/Web/StellaOps.Web/src/app/features/policy-governance/policy-governance.routes.ts index c885806f1..8d3907f8c 100644 --- a/src/Web/StellaOps.Web/src/app/features/policy-governance/policy-governance.routes.ts +++ b/src/Web/StellaOps.Web/src/app/features/policy-governance/policy-governance.routes.ts @@ -17,11 +17,26 @@ export const policyGovernanceRoutes: Routes = [ loadComponent: () => import('./risk-budget-dashboard.component').then((m) => m.RiskBudgetDashboardComponent), }, + { + path: 'overview', + loadComponent: () => + import('./risk-budget-dashboard.component').then((m) => m.RiskBudgetDashboardComponent), + }, + { + path: 'risk-budget', + loadComponent: () => + import('./risk-budget-dashboard.component').then((m) => m.RiskBudgetDashboardComponent), + }, { path: 'budget', loadComponent: () => import('./risk-budget-dashboard.component').then((m) => m.RiskBudgetDashboardComponent), }, + { + path: 'risk-budget/config', + loadComponent: () => + import('./risk-budget-config.component').then((m) => m.RiskBudgetConfigComponent), + }, { path: 'budget/config', loadComponent: () => diff --git a/src/Web/StellaOps.Web/src/app/features/release-control/governance/release-control-governance-hub.component.ts b/src/Web/StellaOps.Web/src/app/features/release-control/governance/release-control-governance-hub.component.ts index 4b0f63133..b0199f07c 100644 --- a/src/Web/StellaOps.Web/src/app/features/release-control/governance/release-control-governance-hub.component.ts +++ b/src/Web/StellaOps.Web/src/app/features/release-control/governance/release-control-governance-hub.component.ts @@ -18,7 +18,7 @@ import { RouterLink } from '@angular/router';

Policy Baselines

Environment-scoped baseline definitions and lock rules.

- +

Governance Rules

Rule catalog for release control gate enforcement.

@@ -26,7 +26,7 @@ import { RouterLink } from '@angular/router';

Policy Simulation

Dry-run policy evaluations before production rollout.

- +

Exception Workflow

Exception requests, approvals, and expiry management.

diff --git a/src/Web/StellaOps.Web/src/app/routes/security-risk.routes.ts b/src/Web/StellaOps.Web/src/app/routes/security-risk.routes.ts index b6d63c2ae..aa92948a5 100644 --- a/src/Web/StellaOps.Web/src/app/routes/security-risk.routes.ts +++ b/src/Web/StellaOps.Web/src/app/routes/security-risk.routes.ts @@ -41,6 +41,15 @@ export const SECURITY_RISK_ROUTES: Routes = [ (m) => m.FindingDetailPageComponent ), }, + { + path: 'advisories-vex', + title: 'Advisories & VEX', + data: { breadcrumb: 'Advisories & VEX' }, + loadComponent: () => + import('../features/security/security-disposition-page.component').then( + (m) => m.SecurityDispositionPageComponent + ), + }, { path: 'supply-chain-data', title: 'Supply-Chain Data', diff --git a/src/Web/StellaOps.Web/tests/e2e/prealpha-canonical-full-sweep.spec.ts b/src/Web/StellaOps.Web/tests/e2e/prealpha-canonical-full-sweep.spec.ts index 2687d9ce8..b272a6b06 100644 --- a/src/Web/StellaOps.Web/tests/e2e/prealpha-canonical-full-sweep.spec.ts +++ b/src/Web/StellaOps.Web/tests/e2e/prealpha-canonical-full-sweep.spec.ts @@ -95,6 +95,7 @@ const canonicalRoutes = [ '/security/disposition', '/security/supply-chain-data', '/security/supply-chain-data/graph', + '/security/sbom-lake', '/security/reachability', '/security/reports', '/evidence', @@ -177,6 +178,29 @@ const canonicalRoutes = [ '/setup/topology/gate-profiles', ] as const; +const strictRouteExpectations: Partial> = { + '/security/advisories-vex': { + title: /Advisories/i, + texts: ['Security / Advisories & VEX', 'Providers'], + }, + '/security/sbom-lake': { + title: /SBOM Lake/i, + texts: ['SBOM Lake', 'Attestation Coverage Metrics'], + }, + '/ops/policy': { + title: /Policy/i, + texts: ['Policy Governance', 'Risk Budget Overview'], + }, + '/ops/policy/overview': { + title: /Policy/i, + texts: ['Policy Governance', 'Risk Budget Overview'], + }, + '/ops/policy/risk-budget': { + title: /Policy/i, + texts: ['Policy Governance', 'Risk Budget Overview'], + }, +}; + function collectNgErrors(page: Page): string[] { const errors: string[] = []; page.on('console', (msg) => { @@ -1210,6 +1234,13 @@ test.describe('Pre-alpha canonical full route sweep', () => { test(`route works: ${path}`, async ({ page }) => { const errors = collectNgErrors(page); await assertUsableRoute(page, path); + const expectation = strictRouteExpectations[path]; + if (expectation) { + await expect(page).toHaveTitle(expectation.title); + for (const text of expectation.texts) { + await expect(page.locator('#main-content')).toContainText(text); + } + } expect(errors, `Runtime errors on ${path}: ${errors.join('\n')}`).toEqual([]); }); } @@ -1240,6 +1271,16 @@ test.describe('Pre-alpha key end-user interactions', () => { await expect(page.locator('.topbar__primary-action')).toContainText('Export Report'); }); + test('mission board SBOM card opens SBOM Lake', async ({ page }) => { + await page.goto('/mission-control/board', { waitUntil: 'domcontentloaded' }); + await page.locator('#main-content a[href="/security/sbom-lake"]').first().click(); + + await expect(page).toHaveURL(/\/security\/sbom-lake$/); + await expect(page).toHaveTitle(/SBOM Lake/i); + await expect(page.locator('#main-content')).toContainText('SBOM Lake'); + await expect(page.locator('#main-content')).toContainText('Attestation Coverage Metrics'); + }); + test('sidebar root navigation works for all canonical workspaces', async ({ page }) => { await page.goto('/mission-control/board', { waitUntil: 'domcontentloaded' }); await page.locator('aside.sidebar a[href="/releases/overview"]').first().click();