---
phase: 09-testing-qa
plan: 02
type: execute
wave: 2
depends_on: ["09-01"]
files_modified:
  - packages/portal/e2e/visual/snapshots.spec.ts
  - packages/portal/e2e/accessibility/a11y.spec.ts
  - packages/portal/e2e/lighthouse/lighthouserc.json
autonomous: true
requirements:
  - QA-02
  - QA-03
  - QA-04

must_haves:
  truths:
    - "Visual regression snapshots exist for all key pages at 3 viewports (desktop, tablet, mobile)"
    - "axe-core accessibility scan passes with zero critical violations on all key pages"
    - "Lighthouse scores meet >= 80 hard floor on login page (90 target)"
    - "Serious a11y violations are logged as warnings, not blockers"
  artifacts:
    - path: "packages/portal/e2e/visual/snapshots.spec.ts"
      provides: "Visual regression tests at 3 viewports"
      contains: "toHaveScreenshot"
    - path: "packages/portal/e2e/accessibility/a11y.spec.ts"
      provides: "axe-core accessibility scans on key pages"
      contains: "AxeBuilder"
    - path: "packages/portal/e2e/lighthouse/lighthouserc.json"
      provides: "Lighthouse CI config with score thresholds"
      contains: "minScore"
  key_links:
    - from: "packages/portal/e2e/accessibility/a11y.spec.ts"
      to: "packages/portal/e2e/fixtures.ts"
      via: "import axe fixture"
      pattern: "from.*fixtures"
    - from: "packages/portal/e2e/visual/snapshots.spec.ts"
      to: "packages/portal/playwright.config.ts"
      via: "visual-desktop/tablet/mobile projects"
      pattern: "toHaveScreenshot"
---

<objective>
Add visual regression testing at 3 viewports, axe-core accessibility scanning on all key pages, and Lighthouse CI performance/accessibility score gating.

Purpose: Catches CSS regressions that unit tests miss, ensures WCAG 2.1 AA compliance, and validates performance baselines before beta launch.

Output: Visual snapshot specs, accessibility scan specs, Lighthouse CI config, and baseline screenshots.
</objective>

<execution_context>
@/home/adelorenzo/.claude/get-shit-done/workflows/execute-plan.md
@/home/adelorenzo/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/phases/09-testing-qa/09-CONTEXT.md
@.planning/phases/09-testing-qa/09-RESEARCH.md
@.planning/phases/09-testing-qa/09-01-SUMMARY.md

Depends on Plan 01 for: playwright.config.ts (visual projects, a11y project), e2e/fixtures.ts (axe fixture), auth.setup.ts (storageState)
</context>

<tasks>

<task type="auto">
  <name>Task 1: Visual regression snapshots and axe-core accessibility tests</name>
  <files>
    packages/portal/e2e/visual/snapshots.spec.ts
    packages/portal/e2e/accessibility/a11y.spec.ts
  </files>
  <action>
1. Create `packages/portal/e2e/visual/snapshots.spec.ts`:
   - Import `{ test, expect }` from `../fixtures`
   - Use platform_admin storageState for authenticated pages
   - Key pages to snapshot (each as a separate test):
     a. Login page (no auth needed -- use empty storageState or navigate directly)
     b. Dashboard
     c. Agents list (/agents or /employees)
     d. Agents/new (3-card entry screen)
     e. Chat (empty state -- no conversation selected)
     f. Templates gallery (/agents/new then select templates option, or /templates)
   - Each test: goto page, wait for network idle or key element visible, call `await expect(page).toHaveScreenshot('page-name.png')`
   - The 3 viewport sizes are handled by the playwright.config.ts visual-desktop/visual-tablet/visual-mobile projects -- the spec runs once, projects provide viewport variation
   - For login page: navigate to /login without storageState
   - For authenticated pages: use default storageState (platform_admin)

2. Create `packages/portal/e2e/accessibility/a11y.spec.ts`:
   - Import `{ test, expect }` from `../fixtures` (gets axe fixture)
   - Use platform_admin storageState
   - Pages to scan: login, dashboard, agents list, agents/new, chat, templates, billing, users
   - For each page, create a test:
     ```
     test('page-name has no critical a11y violations', async ({ page, axe }) => {
       await page.goto('/path');
       await page.waitForLoadState('networkidle');
       const results = await axe().analyze();
       const critical = results.violations.filter(v => v.impact === 'critical');
       const serious = results.violations.filter(v => v.impact === 'serious');
       if (serious.length > 0) {
         console.warn(`Serious a11y violations on /path:`, serious.map(v => v.id));
       }
       expect(critical, `Critical a11y violations on /path`).toHaveLength(0);
     });
     ```
   - Add keyboard navigation test: "Tab through login form fields": goto /login, press Tab repeatedly, assert focus moves through Email -> Password -> Sign In button using `page.locator(':focus')`.
   - Add keyboard nav for chat: Tab to message input, type message, Enter to send.

3. Generate initial visual regression baselines:
   - Build the portal: `cd packages/portal && npm run build`
   - Copy static assets for standalone: `cp -r .next/static .next/standalone/.next/static && cp -r public .next/standalone/public`
   - Run with --update-snapshots: `npx playwright test e2e/visual/ --update-snapshots`
   - This creates baseline screenshots in the __snapshots__ directory
   - NOTE: If the full stack (gateway + DB) is not running, authenticated page snapshots may fail. In that case, generate baselines only for login page and document that full baselines require the running stack. The executor should start the stack via docker compose if possible.
  </action>
  <verify>
    <automated>cd /home/adelorenzo/repos/konstruct/packages/portal && npx playwright test e2e/accessibility/ --project=a11y --reporter=list 2>&1 | tail -20</automated>
  </verify>
  <done>Visual regression spec covers 6 key pages (runs at 3 viewports via projects), baseline screenshots generated. Accessibility spec scans 8+ pages with zero critical violations, serious violations logged as warnings. Keyboard navigation tested on login and chat.</done>
</task>

<task type="auto">
  <name>Task 2: Lighthouse CI configuration and score gating</name>
  <files>
    packages/portal/e2e/lighthouse/lighthouserc.json
  </files>
  <action>
1. Create `packages/portal/e2e/lighthouse/lighthouserc.json`:
   - Based on RESEARCH Pattern 5
   - collect.url: only "/login" page (authenticated pages redirect to login when Lighthouse runs unauthenticated -- see RESEARCH Pitfall 5)
   - collect.numberOfRuns: 1 (speed for CI)
   - collect.settings.preset: "desktop"
   - collect.settings.chromeFlags: "--no-sandbox --disable-dev-shm-usage"
   - assert.assertions:
     - categories:performance: ["error", {"minScore": 0.80}] (hard floor)
     - categories:accessibility: ["error", {"minScore": 0.80}]
     - categories:best-practices: ["error", {"minScore": 0.80}]
     - categories:seo: ["error", {"minScore": 0.80}]
   - upload.target: "filesystem"
   - upload.outputDir: ".lighthouseci"

2. Verify Lighthouse runs successfully:
   - Ensure portal is built and standalone server can start
   - Run: `cd packages/portal && npx lhci autorun --config=e2e/lighthouse/lighthouserc.json`
   - Verify scores are printed and assertions pass
   - If score is below 80 on any category, investigate and document (do NOT lower thresholds)

NOTE: Per RESEARCH Pitfall 5, only /login is tested with Lighthouse because authenticated pages redirect. The 90 target is aspirational -- the 80 hard floor is what CI enforces. Dashboard/chat performance should be validated manually or via Web Vitals in production.
  </action>
  <verify>
    <automated>cd /home/adelorenzo/repos/konstruct/packages/portal && test -f e2e/lighthouse/lighthouserc.json && cat e2e/lighthouse/lighthouserc.json | grep -q "minScore" && echo "PASS"</automated>
  </verify>
  <done>lighthouserc.json exists with score thresholds (80 hard floor, 90 aspirational). Lighthouse CI runs against /login and produces scores. All 4 categories (performance, accessibility, best practices, SEO) pass the 80 floor.</done>
</task>

</tasks>

<verification>
1. `cd packages/portal && npx playwright test e2e/visual/ --project=visual-desktop` -- visual regression passes (or creates baselines on first run)
2. `cd packages/portal && npx playwright test e2e/accessibility/ --project=a11y` -- zero critical violations
3. `cd packages/portal && npx lhci autorun --config=e2e/lighthouse/lighthouserc.json` -- all scores >= 80
4. Baseline screenshots committed to repo
</verification>

<success_criteria>
- Visual regression snapshots exist for 6 key pages at 3 viewports
- axe-core scans all key pages with zero critical a11y violations
- Serious a11y violations logged but not blocking
- Lighthouse CI passes with >= 80 on all 4 categories for /login
- Keyboard navigation tests pass for login form and chat input
</success_criteria>

<output>
After completion, create `.planning/phases/09-testing-qa/09-02-SUMMARY.md`
</output>
