---
phase: 14
slug: pax8-ui-surface
status: approved
nyquist_compliant: true
wave_0_complete: true
created: 2026-07-11
---

# Phase 14 — Validation Strategy

> Per-phase validation contract for feedback sampling during execution.

---

## Test Infrastructure

| Property | Value |
|----------|-------|
| **Framework** | vitest 4.1.5 |
| **Config file** | `vitest.config.ts` — `test.include: ['lib/**/*.test.ts']` (page components and `app/api/**` route handlers are structurally excluded from `npm test` today) |
| **Quick run command** | `npx tsc --noEmit --pretty` (the only automated gate that actually covers `app/pax8/**` and `app/api/pax8/**`) |
| **Full suite command** | `npm test` (covers `lib/services/analyzer/**`, `lib/services/rmm/**`, `lib/services/b2/**` only — won't exercise this phase's new files unless logic is extracted into `lib/services/`) |
| **Estimated runtime** | ~2 seconds (`npm test`) |

---

## Sampling Rate

- **After every task commit:** Run `npx tsc --noEmit --pretty`
- **After every plan wave:** Run `npm test`
- **Before `/gsd:verify-work`:** Full suite must be green + manual click-through of both tabs (list, drill-down, resolve-with-candidate, resolve-via-manual-search, empty-state for zero-candidate row)
- **Max feedback latency:** ~2 seconds

---

## Per-Task Verification Map

| Task ID | Plan | Wave | Requirement | Threat Ref | Secure Behavior | Test Type | Automated Command | File Exists | Status |
|---------|------|------|-------------|------------|-----------------|-----------|-------------------|-------------|--------|
| 14-01-01 | 01 | 1 | PAX8-13 | — | Company list renders with subscription/cost data | manual | `npx tsc --noEmit --pretty` (type-check only) | ❌ W0 | ⬜ pending |
| 14-02-01 | 02 | 2 | PAX8-12, PAX8-14 | T-14-01, T-14-02 | Resolve action writes both `pax8_companies` and `pax8_company_match_review`, admin-gated, validates target company exists | unit (if resolve logic extracted to `lib/services/`) or manual | `npx vitest run lib/services/pax8-company-match-resolver.test.ts` (if extracted) | ❌ W0 | ⬜ pending |

*Status: ⬜ pending · ✅ green · ❌ red · ⚠️ flaky*

---

## Wave 0 Requirements

- [x] Decided: extract resolve logic into `lib/services/pax8-company-match-resolver.ts` for unit-test coverage (Plan 02, Task 2) rather than accept manual-only verification
- [x] Extracted: `lib/services/pax8-company-match-resolver.test.ts` — mocks `postgresClient.query`, following `pax8-company-matcher.test.ts`'s existing convention (`vi.mock('@/lib/services/postgres-client', ...)`)
- [x] No new test framework/config needed — vitest is already configured project-wide

---

## Manual-Only Verifications

| Behavior | Requirement | Why Manual | Test Instructions |
|----------|-------------|------------|-------------------|
| `/pax8` company list renders with cost breakdown | PAX8-13 | Page components are outside vitest's `lib/**/*.test.ts` glob; no test file planned, matching `device-link-conflicts` precedent | Open `/pax8` as an authenticated non-admin user; confirm company rows render with subscription/cost data via DetailModal drill-down |
| Review section shows flagged/ambiguous matches distinctly | PAX8-14 | Same as above — UI rendering, not covered by current test glob | Open the "Needs Review" tab; confirm the 38 known open review rows appear, distinct from the main company list |
| Resolve persists and survives future syncs | PAX8-12 | End-to-end DB + sync-service interaction; covered functionally by unit test only if resolve logic is extracted (see Wave 0) | Resolve a flagged company, confirm `pax8_companies.autotask_company_id` + `pax8_company_match_review.resolved_at` are set, then trigger a manual PAX8 sync and confirm the resolution is not overwritten (`resolved_at IS NOT NULL` rows are excluded from re-scoring per Phase 12's D-05) |
| View/resolve permission split | D-07/D-08 | Requires two distinct authenticated sessions (regular user vs admin) to exercise both branches | As a non-admin authenticated user, confirm `/pax8` view loads but the resolve action is rejected (403/hidden); as admin, confirm resolve succeeds |

---

## Validation Sign-Off

- [x] All tasks have `<automated>` verify or Wave 0 dependencies
- [x] Sampling continuity: no 3 consecutive tasks without automated verify
- [x] Wave 0 covers all MISSING references
- [x] No watch-mode flags
- [x] Feedback latency < 5s
- [x] `nyquist_compliant: true` set in frontmatter

**Approval:** approved — Wave 0 resolved via Plan 02 (resolver extracted + unit-tested)
