---
milestone: v2.0
audited: 2026-07-11T11:55:44Z
status: gaps_found
scores:
  requirements: 9/14
  phases: 3/5
  integration: 8/8
  flows: 1/1
gaps:
  requirements:
    - id: "PAX8-07"
      status: "unsatisfied"
      phase: "Phase 13"
      claimed_by_plans: []
      completed_by_plans: []
      verification_status: "missing"
      evidence: "Phase 13 (Scheduler & Admin Toggle) has not been discussed, planned, or executed. No PLAN.md/SUMMARY.md exist."
    - id: "PAX8-09"
      status: "unsatisfied"
      phase: "Phase 13"
      claimed_by_plans: []
      completed_by_plans: []
      verification_status: "missing"
      evidence: "Same as PAX8-07 — Phase 13 not yet started."
    - id: "PAX8-12"
      status: "unsatisfied"
      phase: "Phase 14"
      claimed_by_plans: []
      completed_by_plans: []
      verification_status: "missing"
      evidence: "Phase 14 (/pax8 UI Surface) has not been discussed, planned, or executed."
    - id: "PAX8-13"
      status: "unsatisfied"
      phase: "Phase 14"
      claimed_by_plans: []
      completed_by_plans: []
      verification_status: "missing"
      evidence: "Same as PAX8-12 — Phase 14 not yet started."
    - id: "PAX8-14"
      status: "unsatisfied"
      phase: "Phase 14"
      claimed_by_plans: []
      completed_by_plans: []
      verification_status: "missing"
      evidence: "Same as PAX8-12 — Phase 14 not yet started."
  integration:
    - finding: "GET /api/pax8/sync status endpoint under-reports"
      severity: "warning"
      affected_requirements: ["PAX8-06", "PAX8-10", "PAX8-11"]
      evidence: "app/api/pax8/sync/route.ts GET handler's counts query only selects pax8_companies/pax8_subscriptions/pax8_products (written in Phase 11). Phase 12 added pax8_orders, pax8_order_items, and pax8_company_match_review and wired their sync into the same fullSync() this route triggers, but never extended the status query. Data lands correctly in Postgres (proven directly in 12-05-SUMMARY.md), but an admin polling this endpoint today has zero visibility into whether PAX8-06/10/11 actually ran."
  flows: []
tech_debt:
  - phase: 11-company-catalog-subscription-sync
    items:
      - "Pre-existing tsc errors in lib/services/sync-scheduler.ts:446,450 (missing appgate-factory/appgate-sync-service modules) — untracked WIP unrelated to PAX8, reproduces before and after this phase's changes"
  - phase: 12-orders-invoices-company-matching
    items:
      - "Same pre-existing sync-scheduler.ts TS2307 errors reproduce unchanged across all 5 plans"
      - "2 pre-existing itglue-search.test.ts failures (unrelated file, last touched in commit a0a6e7f well before Phase 12)"
      - "GET /api/pax8/sync status endpoint not extended for orders/company-matches (see integration gap above)"
      - "IN-03 (accepted, informational): Pax8InvoiceItem.forCompanyId vs .companyId ambiguity — only companyId is read; live-verified correct for sampled data"
nyquist:
  compliant_phases: ["10-pax8-client-auth-foundation", "12-orders-invoices-company-matching"]
  partial_phases: []
  missing_phases: ["11-company-catalog-subscription-sync"]
  overall: "partial"
---

# Milestone v2.0 — PAX8 Integration — Audit Report

## Scope

5 phases planned for this milestone. **3 executed and verified** (Phases 10, 11, 12). **2 not yet started** (Phase 13: Scheduler & Admin Toggle; Phase 14: /pax8 UI Surface).

This audit reflects the milestone's current state, not a false claim of completion — the `gaps_found` status below is expected given 2 of 5 phases remain unbuilt, not a defect in the work that has shipped.

## Phase Verification Summary

| Phase | Status | Requirements | Notes |
|-------|--------|--------------|-------|
| 10. PAX8 Client & Auth Foundation | ✓ passed | PAX8-01, PAX8-02 | — |
| 11. Company, Catalog & Subscription Sync | ✓ passed | PAX8-03, PAX8-04, PAX8-05, PAX8-08 | No VALIDATION.md (Nyquist missing) |
| 12. Orders/Invoices & Company Matching | ✓ passed | PAX8-06, PAX8-10, PAX8-11 | Code-reviewed (2 critical + 3 warning fixed), UAT 7/7, security 9/9 closed, Nyquist compliant |
| 13. Scheduler & Admin Toggle | not started | PAX8-07, PAX8-09 | — |
| 14. /pax8 UI Surface | not started | PAX8-12, PAX8-13, PAX8-14 | — |

## Requirements Coverage (3-Source Cross-Reference)

| REQ-ID | Description | Phase | VERIFICATION | SUMMARY frontmatter | REQUIREMENTS.md | Final Status |
|--------|-------------|-------|--------------|---------------------|------------------|---------------|
| PAX8-01 | OAuth2 client-credentials auth | 10 | passed | listed | [x] | **satisfied** |
| PAX8-02 | isPax8Configured() helper | 10 | passed | listed | [x] | **satisfied** |
| PAX8-03 | Sync companies | 11 | passed | listed | [x] | **satisfied** |
| PAX8-04 | Sync subscriptions | 11 | passed | listed | [x] | **satisfied** |
| PAX8-05 | Sync product catalog | 11 | passed | listed | [x] | **satisfied** |
| PAX8-08 | Read-only sync operations | 11 | passed | listed | [x] | **satisfied** |
| PAX8-06 | Sync orders/invoices (historical) | 12 | passed | listed | [x] | **satisfied** |
| PAX8-10 | Fuzzy company auto-match | 12 | passed | listed | [x] | **satisfied** |
| PAX8-11 | Flag unmatched/ambiguous, never guess | 12 | passed | listed | [x] | **satisfied** |
| PAX8-07 | Daily sync via cron | 13 | missing | absent | [ ] | **unsatisfied** (phase not started) |
| PAX8-09 | Toggle via /admin/integrations | 13 | missing | absent | [ ] | **unsatisfied** (phase not started) |
| PAX8-12 | Manual match resolution UI | 14 | missing | absent | [ ] | **unsatisfied** (phase not started) |
| PAX8-13 | /pax8 page: companies/subscriptions/cost | 14 | missing | absent | [ ] | **unsatisfied** (phase not started) |
| PAX8-14 | /pax8 page surfaces flagged matches | 14 | missing | absent | [ ] | **unsatisfied** (phase not started) |

No orphaned requirements — every REQ-ID in the traceability table maps to a real phase; the 5 unsatisfied ones are correctly tracked as "Pending" and their phases genuinely haven't run yet.

## Cross-Phase Integration (Phases 10–12 only, per scope)

**8/8 expected connections wired. 0 orphaned exports. 0 hard breaks.**

Full `fullSync()` dependency chain traced and verified against live data (12-05-SUMMARY.md: 94 invoices, 29,696 order items, 80 auto-matches, 38 flagged, idempotent across 2 runs):
`syncCompanies` → `syncSubscriptions` (+ referencedProductIds) → `syncProducts` → `syncOrders` → `syncCompanyMatches`

Schema cross-check: every INSERT column list in `pax8-sync-service.ts` verified against migrations 091/092/093/094/095 — no positional param/column mismatches found. `pax8_company_match_review` upsert's `ON CONFLICT` clause matches the exact partial unique index in migration 091.

### Integration Finding (Warning, not a blocker)

**`GET /api/pax8/sync` status endpoint under-reports (affects PAX8-06, PAX8-10, PAX8-11 visibility)**

The status endpoint's `counts` query (written in Phase 11) only selects `pax8_companies`/`pax8_subscriptions`/`pax8_products`. Phase 12 added `pax8_orders`, `pax8_order_items`, and `pax8_company_match_review`, wired their sync into the same `fullSync()` this route triggers, but never extended the status query to report them. Data lands correctly (proven by direct DB assertions), but today's only read surface for PAX8 sync status has zero visibility into orders/invoices/matching activity. Not a blocker — recommend fixing before or during Phase 14, since the `/pax8` UI page will likely want this same status data.

## Nyquist Compliance

| Phase | VALIDATION.md | Compliant | Action |
|-------|---------------|-----------|--------|
| 10 | exists | true | — |
| 11 | **missing** | — | `/gsd:validate-phase 11` |
| 12 | exists | true | — |

## Tech Debt (accumulated, non-blocking)

**Phase 11 & 12 (recurring):**
- Pre-existing `lib/services/sync-scheduler.ts:446,450` TS2307 errors (missing `appgate-factory`/`appgate-sync-service` modules) — untracked WIP unrelated to PAX8, confirmed reproducing unchanged across both phases' plans
- 2 pre-existing `lib/services/analyzer/itglue-search.test.ts` failures (unrelated file, last touched well before Phase 12)

**Phase 12:**
- `GET /api/pax8/sync` status endpoint gap (see Integration Finding above)
- IN-03 (accepted, informational, from 12-REVIEW.md): `Pax8InvoiceItem.forCompanyId` vs `.companyId` field ambiguity — only `companyId` is read; live-verified correct for the sampled data

## Verdict

`gaps_found` — driven entirely by 2 of 5 milestone phases not yet being built (Phase 13, Phase 14). Everything that HAS shipped (Phases 10–12) is fully wired, verified, security-audited, and Nyquist-covered except for one missing VALIDATION.md (Phase 11) and one non-blocking status-endpoint gap.
