---
phase: 14-pax8-ui-surface
plan: 06
type: execute
wave: 4
depends_on: ["14-05"]
files_modified: []
autonomous: false
requirements: [PAX8-12, PAX8-13, PAX8-14]
must_haves:
  truths:
    - "All four Phase 14 success criteria are confirmed working against the live dev DB by a human click-through"
    - "The view/resolve permission split is confirmed: a non-admin can view /pax8 but cannot resolve; an admin can resolve"
    - "A resolved match persists in both tables and survives a subsequent PAX8 sync"
  artifacts: []
  key_links: []
---

<objective>
Human verification of the complete `/pax8` surface end-to-end against the live dev database, plus the automated gate. This is the phase's acceptance gate before `/gsd:verify-work`.

Purpose: Phase 14 has no route/page automated test coverage (page components + `app/api/**` are outside vitest's `lib/**/*.test.ts` glob — matching the `device-link-conflicts` precedent, which also has zero automated tests). The one unit-tested piece is the resolver (Plan 02). Everything else is verified manually per the Validation Strategy. This plan runs the full suite, the type-check, and the documented click-through.
Output: confirmation of all four success criteria + the permission split + persistence across sync.
</objective>

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

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/14-pax8-ui-surface/14-VALIDATION.md
@.planning/phases/14-pax8-ui-surface/14-RESEARCH.md
@.planning/phases/14-pax8-ui-surface/14-01-SUMMARY.md
@.planning/phases/14-pax8-ui-surface/14-02-SUMMARY.md
@.planning/phases/14-pax8-ui-surface/14-03-SUMMARY.md
@.planning/phases/14-pax8-ui-surface/14-04-SUMMARY.md
@.planning/phases/14-pax8-ui-surface/14-05-SUMMARY.md
</context>

<tasks>

<task type="auto">
  <name>Task 1: Run the automated gates</name>
  <read_first>
    - .planning/phases/14-pax8-ui-surface/14-VALIDATION.md (Sampling Rate: tsc + npm test)
  </read_first>
  <files></files>
  <action>
    Run `npx tsc --noEmit --pretty` (the only automated gate covering the new app/pax8 + app/api/pax8 files) and `npm test` (full vitest suite — must stay green; it exercises the Plan 02 resolver test at `lib/services/pax8-company-match-resolver.test.ts` and must not regress the analyzer/rmm/b2 suites). Both must pass with zero errors before the human checkpoint. If either fails, stop and report the failure rather than proceeding to the checkpoint.
  </action>
  <verify>
    <automated>npx tsc --noEmit --pretty && npm test</automated>
  </verify>
  <acceptance_criteria>
    - `npx tsc --noEmit --pretty` exits 0 with no type errors
    - `npm test` exits 0; `pax8-company-match-resolver.test.ts` is included and green
  </acceptance_criteria>
  <done>Type-check and full test suite are green.</done>
</task>

<task type="checkpoint:human-verify" gate="blocking">
  <name>Task 2: Verify Phase 14 SC#1-4 + permission split against the running app</name>
  <action>
    After the automated gates pass, drive the running dev app through the steps in how-to-verify below and confirm every Phase 14 success criterion plus the D-07/D-08 view/resolve permission split and sync-persistence. Do not mark this done until a human types "approved".
  </action>
  <what-built>
    A new `/pax8` page (Companies + Needs Review tabs), four `/api/pax8/*` routes (company list, company drill-down, review queue, admin-gated resolve), an additively-extended `DetailModal` cost-breakdown view, a hardened `companies-list` route, and a top-level PAX8 nav entry. This checkpoint confirms all four Phase 14 success criteria and the D-07/D-08 permission split against the live dev DB (118 companies, 445 subscriptions, 38 open reviews).
  </what-built>
  <how-to-verify>
    1. Start/confirm the dev app (`npm run dev`, http://localhost:3100) and sign in.
    2. Confirm a top-level "PAX8" item appears in the desktop nav (and mobile Sheet); click it → lands on `/pax8`.
    3. SC#1 — Companies tab: confirm a DataTable of PAX8 companies renders with name, matched Autotask company (or an "Unmatched" badge), active subscription count, and city/country; sort by a column and run a name search — both update the list.
    4. SC#2 — cost breakdown: click a matched company row → the DetailModal opens with a "Subscriptions & Cost" table (product, qty, billing term, amount) above the identity fields, showing a summed total; verify the number of line items is not suspiciously fewer than the company's active-subscription count (Pitfall 2 windowing), and amounts look like billed totals not list-price×qty (Pitfall 3). Check the Raw tab shows the underlying JSON.
    5. SC#3 — Needs Review tab: switch tabs; confirm ~38 amber-bordered review cards appear, visually distinct from the main list, and the tab shows a count badge. Confirm a card with candidates shows "Link company" buttons AND a manual search; confirm a zero-candidate card shows "No suggested matches" with only the manual picker.
    6. SC#4 (resolve + persist) — as an ADMIN: resolve one ambiguous review via a candidate button and one zero-candidate review via manual search; confirm each card disappears with a "Linked to {name}" toast and the count badge decrements. In psql confirm both resolved companies now have `pax8_companies.match_method = 'manual'` + `autotask_company_id` set AND `pax8_company_match_review.resolved_at IS NOT NULL`. Trigger a PAX8 sync (`POST /api/pax8/sync` or the scheduler) and confirm the two resolutions are NOT overwritten (still `match_method='manual'`, review still resolved).
    7. Permission split (D-07/D-08) — as a NON-ADMIN authenticated user: confirm `/pax8` and both tabs load and the drill-down works, but attempting a resolve is rejected (403 surfaced as an error toast; the row does not resolve).
  </how-to-verify>
  <resume-signal>Type "approved" if all four success criteria + the permission split + sync-persistence hold, or describe any gap.</resume-signal>
</task>

</tasks>

<threat_model>
## Trust Boundaries

| Boundary | Description |
|----------|-------------|
| verification only | This plan makes no code changes; it exercises the boundaries established in Plans 01-05 |

## STRIDE Threat Register

| Threat ID | Category | Component | Disposition | Mitigation Plan |
|-----------|----------|-----------|-------------|-----------------|
| T-14-01 | Elevation of Privilege | resolve route (verification) | mitigate | Step 7 explicitly exercises the non-admin path to confirm the 403 gate holds in practice, not just in code |
| T-14-02 | Tampering | resolve persistence across sync | mitigate | Step 6 confirms both write signals (match_method='manual' + resolved review) survive a real re-sync — the load-bearing correctness check |
| T-14-SC | Tampering | npm/pip/cargo installs | accept | Zero new packages introduced across the phase |
</threat_model>

<verification>
- `npx tsc --noEmit --pretty && npm test` green
- Human checkpoint confirms all four success criteria, the permission split, and sync-persistence
</verification>

<success_criteria>
- Automated gates green
- Human confirms: SC#1 list, SC#2 cost breakdown, SC#3 distinct review section, SC#4 admin resolve + persist-through-sync, and the D-07/D-08 view/resolve split
</success_criteria>

<output>
Create `.planning/phases/14-pax8-ui-surface/14-06-SUMMARY.md` when done
</output>
