diff --git a/.planning/phases/14-pax8-ui-surface/14-VALIDATION.md b/.planning/phases/14-pax8-ui-surface/14-VALIDATION.md new file mode 100644 index 0000000..f9d8695 --- /dev/null +++ b/.planning/phases/14-pax8-ui-surface/14-VALIDATION.md @@ -0,0 +1,76 @@ +--- +phase: 14 +slug: pax8-ui-surface +status: draft +nyquist_compliant: false +wave_0_complete: false +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 + +- [ ] Decide whether to extract resolve/cost-breakdown SQL into `lib/services/` for unit-test coverage, or accept manual-only verification — matches `device-link-conflicts`'s existing precedent, which also has zero automated tests today +- [ ] If extracted: `lib/services/pax8-company-match-resolver.test.ts` — mock `postgresClient.query`, following `pax8-company-matcher.test.ts`'s existing convention (`vi.mock('@/lib/services/postgres-client', ...)`) +- [ ] 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 + +- [ ] All tasks have `` verify or Wave 0 dependencies +- [ ] Sampling continuity: no 3 consecutive tasks without automated verify +- [ ] Wave 0 covers all MISSING references +- [ ] No watch-mode flags +- [ ] Feedback latency < 5s +- [ ] `nyquist_compliant: true` set in frontmatter + +**Approval:** pending