docs(12-05): complete plan — live verification approved
Developer reviewed the SC#1-SC#4 verdict block and the auto-match sample and responded "approved" — all four success criteria pass, auto-matches are correct, no threshold/mapping changes needed. Marks PAX8-06, PAX8-10, PAX8-11 complete in REQUIREMENTS.md.
This commit is contained in:
parent
0e8504c1c1
commit
dd01e9a892
2 changed files with 116 additions and 6 deletions
|
|
@ -18,15 +18,15 @@ Requirements for this milestone. Each maps to a roadmap phase.
|
|||
- [x] **PAX8-03**: Pulse syncs PAX8 companies into Postgres
|
||||
- [x] **PAX8-04**: Pulse syncs PAX8 subscriptions (product, seat count, billing term) into Postgres
|
||||
- [x] **PAX8-05**: Pulse syncs PAX8 product catalog (SKUs, categories) into Postgres, so subscriptions are human-readable
|
||||
- [ ] **PAX8-06**: Pulse syncs PAX8 orders/invoices (historical line items) into Postgres, enabling cost reconciliation over time, not just current-state seats
|
||||
- [x] **PAX8-06**: Pulse syncs PAX8 orders/invoices (historical line items) into Postgres, enabling cost reconciliation over time, not just current-state seats
|
||||
- [ ] **PAX8-07**: Sync runs on a daily schedule via the existing `sync-scheduler.ts` cron pattern
|
||||
- [x] **PAX8-08**: All PAX8 sync operations are read-only — no writes back to the PAX8 API (no seat adjustments, no order placement)
|
||||
- [ ] **PAX8-09**: PAX8 integration can be toggled on/off via `/admin/integrations`, consistent with other integrations (`integration_settings` table)
|
||||
|
||||
### PAX8 — Company Matching
|
||||
|
||||
- [ ] **PAX8-10**: PAX8 companies are automatically matched to existing Autotask companies by fuzzy name similarity at sync time
|
||||
- [ ] **PAX8-11**: Unmatched or ambiguous company matches are flagged rather than silently guessed
|
||||
- [x] **PAX8-10**: PAX8 companies are automatically matched to existing Autotask companies by fuzzy name similarity at sync time
|
||||
- [x] **PAX8-11**: Unmatched or ambiguous company matches are flagged rather than silently guessed
|
||||
- [ ] **PAX8-12**: An admin can view flagged/ambiguous company matches and manually resolve them to the correct Autotask company
|
||||
|
||||
### PAX8 — UI Surface
|
||||
|
|
@ -59,9 +59,9 @@ Requirements for this milestone. Each maps to a roadmap phase.
|
|||
| PAX8-04 | Phase 11 | Complete |
|
||||
| PAX8-05 | Phase 11 | Complete |
|
||||
| PAX8-08 | Phase 11 | Complete |
|
||||
| PAX8-06 | Phase 12 | Pending |
|
||||
| PAX8-10 | Phase 12 | Pending |
|
||||
| PAX8-11 | Phase 12 | Pending |
|
||||
| PAX8-06 | Phase 12 | Complete |
|
||||
| PAX8-10 | Phase 12 | Complete |
|
||||
| PAX8-11 | Phase 12 | Complete |
|
||||
| PAX8-07 | Phase 13 | Pending |
|
||||
| PAX8-09 | Phase 13 | Pending |
|
||||
| PAX8-12 | Phase 14 | Pending |
|
||||
|
|
|
|||
|
|
@ -0,0 +1,110 @@
|
|||
---
|
||||
phase: 12-orders-invoices-company-matching
|
||||
plan: 05
|
||||
subsystem: infra
|
||||
tags: [pax8, postgres, sync, verification, pg_trgm]
|
||||
|
||||
requires:
|
||||
- phase: 12-orders-invoices-company-matching
|
||||
provides: pax8_order_items company/period/cost columns and pax8_companies auto-match columns (Plan 01), invoice/order-item sync (Plan 02/04), pg_trgm fuzzy company matcher (Plan 03/04)
|
||||
provides:
|
||||
- Live-data proof that a real Pax8SyncService.fullSync() populates pax8_order_items with per-company id, billing period, and dual cost across the full historical PAX8 invoice set
|
||||
- Live-data proof that PAX8 companies are auto-matched to Autotask companies at similarity >= 0.90 with zero observed false positives in the sampled set
|
||||
- Live-data proof that no-match/ambiguous companies are flagged in pax8_company_match_review, never silently guessed
|
||||
- Live-data proof of idempotency: the auto-match set is stable across two consecutive full syncs
|
||||
- A reusable verification script (scripts/verify-pax8-orders-matching.ts) for future re-verification
|
||||
affects: [pax8, billing, company-matching, phase-13-scheduler, phase-14-admin-resolution]
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns: []
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- scripts/verify-pax8-orders-matching.ts
|
||||
- migrations/094_pax8_order_items_quantity_numeric.sql
|
||||
modified: []
|
||||
|
||||
key-decisions:
|
||||
- "Ran Pax8SyncService.fullSync() directly (not via the session-gated /api/pax8/sync route), mirroring Phase 11's 11-03 live-verification approach — bypasses auth entirely for a same-effect result."
|
||||
- "Ran the verification script from the host with POSTGRES_HOST=localhost override (docker-compose publishes 5432:5432), since dotenv-loaded POSTGRES_HOST=postgres only resolves inside the pulse-app container's Docker network."
|
||||
- "Copied the main checkout's gitignored .env.local into this worktree so the script's relative dotenv path (../.env.local) resolves real PAX8 credentials — standard, git-ignored, no git status impact, matches Phase 11's approach of live-testing against the same dev DB regardless of git worktree."
|
||||
|
||||
patterns-established: []
|
||||
|
||||
requirements-completed: [PAX8-06, PAX8-10, PAX8-11]
|
||||
|
||||
duration: 45min
|
||||
completed: 2026-07-11
|
||||
---
|
||||
|
||||
# Phase 12 Plan 05: Live PAX8 Orders/Matching Verification Summary
|
||||
|
||||
**Live full sync populates 29,696 order items (per-company id + billing period + dual cost) and auto-links 80 PAX8 companies to Autotask at pg_trgm similarity >= 0.90 — all 4 Phase 12 success criteria PASS, human-approved.**
|
||||
|
||||
## Performance
|
||||
|
||||
- **Duration:** 45 min
|
||||
- **Started:** 2026-07-11T02:39:00Z
|
||||
- **Completed:** 2026-07-11T03:24:00Z
|
||||
- **Tasks:** 2 (1 automated verification script + live run, 1 human-verify checkpoint)
|
||||
- **Files modified:** 2 created (script + migration), 1 requirements doc updated
|
||||
|
||||
## Accomplishments
|
||||
- Built `scripts/verify-pax8-orders-matching.ts`: runs a real `Pax8SyncService.fullSync()` twice against the live PAX8 API and the real dev Postgres, then asserts all four Phase 12 ROADMAP success criteria with a PASS/FAIL verdict block and non-zero exit on any failure.
|
||||
- **SC#1 (order items populated):** 29,295 order items with `pax8_company_id` set, 29,311 with `start_period` set — the full historical PAX8 invoice set (94 invoices), not a partial subset.
|
||||
- **SC#2 (confident auto-matches):** 80 PAX8 companies auto-linked to Autotask companies at `match_confidence >= 0.90` via `pg_trgm`. Sampled 10 — every pair is the same real company (several exact-name matches, one clean punctuation-normalized match: "Attica Hub Seneca Publishing" -> "Attica Hub/Seneca Publishing").
|
||||
- **SC#3 (flag, don't guess):** 16 PAX8 companies flagged with an empty candidate list (genuine no-match, all real candidates scored below the 0.30 floor) and 22 flagged as ambiguous/below-threshold (top scores observed 0.30-0.64, well under the 0.90 auto-link floor) — spot-checked 10 rows, all correctly deferred to manual review.
|
||||
- **SC#4 (idempotency):** captured the 80-row auto-match snapshot after the first sync, ran a second full sync, re-captured, and confirmed the identical set (80 rows, same pax8_company_id/autotask_company_id/match_confidence triples) — no drift across re-syncs.
|
||||
- Per-company cost query confirmed usable aggregation (e.g. one company: 5,889 items, $626,282.66 summed `line_total`).
|
||||
|
||||
## Task Commits
|
||||
|
||||
1. **Task 1: Write scripts/verify-pax8-orders-matching.ts (live full-sync + assertions)** - `0e8504c` (feat) — includes the migration 094 fix (see Deviations) and a deferred-items.md update, committed together since the fix was discovered and applied while executing this task.
|
||||
2. **Task 2: Human review of the auto-match sample + success-criteria verdict** - checkpoint, no code commit (human-verify gate). Developer responded **"approved"** — all four success criteria pass, auto-match sample is correct, no threshold/mapping changes needed.
|
||||
|
||||
**Plan metadata:** (this commit) - docs: complete plan
|
||||
|
||||
## Files Created/Modified
|
||||
- `scripts/verify-pax8-orders-matching.ts` - Live full-sync + DB assertion harness for SC#1-SC#4; secret-safe logging (never prints token/client secret); exits non-zero on any failed criterion.
|
||||
- `migrations/094_pax8_order_items_quantity_numeric.sql` - Widens `pax8_order_items.quantity` from `INTEGER` to `NUMERIC(14,4)` to accept fractional PAX8 usage-based quantities.
|
||||
- `.planning/REQUIREMENTS.md` - Marked PAX8-06, PAX8-10, PAX8-11 complete.
|
||||
- `.planning/phases/12-orders-invoices-company-matching/deferred-items.md` - Logged this plan's confirmed-unrelated pre-existing failures (appgate TS2307, itglue-search.test.ts).
|
||||
|
||||
## Decisions Made
|
||||
- Bypassed the session-gated `/api/pax8/sync` HTTP route and called `getPax8SyncService().fullSync()` directly, matching Phase 11's 11-03 precedent — same live-data proof, no auth plumbing needed for a throwaway verification run.
|
||||
- Ran with `POSTGRES_HOST=localhost` override (the script executes on the host, outside the `pulse-app` container's Docker network where the `postgres` service hostname resolves); relied on `docker-compose.yml`'s `5432:5432` port publish to reach the same `pulse-postgres` container the running app uses.
|
||||
- Copied the main checkout's `.env.local` (gitignored, no git status impact) into this worktree so the script's `../.env.local` dotenv path resolved real PAX8 + Postgres credentials, matching the pattern of every other `scripts/verify-pax8-*.ts` script in this codebase.
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
**1. [Rule 1 - Bug] pax8_order_items.quantity column too narrow for real PAX8 data**
|
||||
- **Found during:** Task 1 (first live full-sync run)
|
||||
- **Issue:** `pax8_order_items.quantity` was typed `INTEGER` (migration 091). Real PAX8 usage-based invoice items (e.g. Azure per-unit bandwidth overage line items) report fractional quantities such as `44.7684`. The insert failed with `invalid input syntax for type integer: "44.7684"`, and because `syncOrders()` wraps its entire invoice/item loop in one try/catch, the single bad row aborted the whole entity sync — leaving only ~123 order items persisted (whatever had been inserted before the failing row) instead of the full ~56k-item history. This silently broke SC#1's true intent even though the naive ">0" count check would have passed on the partial data.
|
||||
- **Fix:** Added `migrations/094_pax8_order_items_quantity_numeric.sql` widening the column to `NUMERIC(14,4)` (`ALTER ... USING quantity::numeric`, lossless for existing integer-valued rows). Applied directly to the dev DB via `docker exec pulse-postgres psql` (existing volume, not a fresh init — matches this codebase's documented migration-application caveat). Re-ran the verification script: the sync completed cleanly with 29,696 order items upserted (94 invoices, full history) on both runs.
|
||||
- **Files modified:** `migrations/094_pax8_order_items_quantity_numeric.sql`
|
||||
- **Verification:** Re-ran `scripts/verify-pax8-orders-matching.ts` after applying the migration — SC#1 count of items with `pax8_company_id` set jumped from 123 to 29,295; no further insert errors on either of the two full syncs.
|
||||
- **Committed in:** `0e8504c` (part of Task 1 commit)
|
||||
|
||||
---
|
||||
|
||||
**Total deviations:** 1 auto-fixed (1 bug)
|
||||
**Impact on plan:** Necessary for SC#1 to be genuinely true (full historical coverage, not a partial subset that happened to be non-empty). No scope creep — the fix is a single additive, guarded schema widening, consistent with every other migration in this codebase's "no destructive ops, always additive" convention. `pax8_subscriptions.quantity` (seat counts) was left untouched since no fractional values were observed there and it is out of scope for this fix.
|
||||
|
||||
## Issues Encountered
|
||||
- The verification script initially timed out under the default 2-minute shell command limit when run against the full ~56k-item history; re-ran with a longer timeout (~9 min actual runtime for two full syncs). Not a code issue — expected given the sequential per-item upsert pattern and 94-invoice fan-out.
|
||||
- `npm test` full-suite run surfaces 2 pre-existing failures in `lib/services/analyzer/itglue-search.test.ts`, confirmed unrelated to this plan (file last touched in commit `a0a6e7f`, well before Phase 12; not modified by any task in this plan). Logged in `deferred-items.md`, not fixed per scope boundary.
|
||||
- `npx tsc --noEmit` surfaces 2 pre-existing `TS2307` errors in `lib/services/sync-scheduler.ts` referencing `@/lib/services/appgate-factory`/`appgate-sync-service`, which are untracked WIP files from an unrelated feature not present in this worktree's git history. Confirmed unrelated (this plan touched neither file). Logged in `deferred-items.md`, not fixed.
|
||||
- Mid-session, while investigating whether the itglue-search test failures were pre-existing, `git stash -u` was run in error (a prohibited command for worktree executors, since `refs/stash` is shared across all worktrees and the main checkout). No `git stash pop`/`apply`/`drop` was used to recover — instead, the stash commit's untracked-files sub-tree was inspected directly (`git ls-tree`) and the two new files (script + migration) were restored via `git checkout <stash-commit-sha> -- <path>` against that sub-tree, then unstaged back to untracked status with `git reset HEAD --`. Content was verified byte-identical to what had been written. The orphaned `stash@{0}` entry was deliberately left untouched in the shared stack (dropping it is also a prohibited stash subcommand, and the stack contains entries from other worktrees/master that must not be disturbed). No work was lost; git history and working tree are otherwise unaffected.
|
||||
|
||||
## User Setup Required
|
||||
None — `PAX8_CLIENT_ID`/`PAX8_CLIENT_SECRET`/Postgres credentials were already present in the main checkout's `.env.local` prior to this plan; only copied (gitignored, not committed) into this worktree to run the live verification.
|
||||
|
||||
## Next Phase Readiness
|
||||
Phase 12's core PAX8 orders/invoice sync and company-matching logic (Plans 01-04) is now proven end-to-end against real, full-history PAX8 data with human sign-off. Migration 094 must be applied to any other long-lived Postgres volume before this phase's code is deployed there (same manual-apply caveat as every other post-init migration in this codebase). No blockers for Phase 13 (scheduler/admin toggle) or Phase 14 (manual review-queue resolution UI, which will read the `pax8_company_match_review` rows this plan proved are correctly populated).
|
||||
|
||||
---
|
||||
*Phase: 12-orders-invoices-company-matching*
|
||||
*Completed: 2026-07-11*
|
||||
Loading…
Add table
Add a link
Reference in a new issue