diff --git a/.planning/phases/10-pax8-client-auth-foundation/10-02-SUMMARY.md b/.planning/phases/10-pax8-client-auth-foundation/10-02-SUMMARY.md new file mode 100644 index 0000000..e82c1a5 --- /dev/null +++ b/.planning/phases/10-pax8-client-auth-foundation/10-02-SUMMARY.md @@ -0,0 +1,93 @@ +--- +phase: 10-pax8-client-auth-foundation +plan: 02 +subsystem: database +tags: [postgres, migration, pax8, schema] + +# Dependency graph +requires: + - phase: 10-pax8-client-auth-foundation (plan 01) + provides: PAX8 OAuth2 client/factory foundation (companion plan in same wave) +provides: + - "migrations/091_pax8_tables.sql — six pax8_* tables ready for Phase 11+ sync services to populate" + - "pax8_company_match_review conflicts queue shape, ready for Phase 12's matching logic to write into" +affects: [11-pax8-current-state-sync, 12-pax8-historical-sync-company-matching, 13-pax8-scheduler-admin-toggle, 14-pax8-ui] + +# Tech tracking +tech-stack: + added: [] + patterns: + - "PAX8 entity tables follow the appgate_* audit-column shape: raw_payload JSONB + synced_at + is_deleted + deleted_at + idx__is_deleted" + - "Header/line-item FK: pax8_order_items.order_id is a hard FK (ON DELETE CASCADE) to pax8_orders(id); cross-entity refs (company_id, product_id) are soft (plain indexed UUID columns, no FK) since sync insert order across entities isn't guaranteed" + - "Company-match review queue (pax8_company_match_review) copied field-for-field from device_link_review (migration 080): partial-unique 'one open review per source row' index, BIGINT[] candidates, TEXT[] confidences" + +key-files: + created: + - migrations/091_pax8_tables.sql + modified: [] + +key-decisions: + - "Applied the migration directly via `docker exec -i pulse-postgres psql ... < migrations/091_pax8_tables.sql` instead of scripts/apply-migrations.sh, because that script hardcodes MIGRATIONS_DIR=/opt/stacks/pulse/migrations (the main repo path) and would not find the file living in this worktree's migrations/ directory pre-merge. Same net effect as the script's single-migration mode (documented in the plan's block)." + - "Removed the `NULL` keyword from resolved_by_user_id/resolved_to_company_id column defs (nullable is implicit/default) to match the plan's exact acceptance-criteria grep patterns, deviating slightly from device_link_review's explicit `NULL` styling." + +requirements-completed: [PAX8-01, PAX8-02] + +# Metrics +duration: 25min +completed: 2026-07-10 +--- + +# Phase 10 Plan 02: PAX8 Schema Migration Summary + +**Created and applied `migrations/091_pax8_tables.sql`, laying down all six PAX8 tables (companies, subscriptions, products, orders, order_items, company_match_review) with header/line-item FK, audit columns, and monetary+currency typing — verified idempotent against the live dev DB.** + +## Performance + +- **Duration:** 25 min +- **Started:** 2026-07-10T21:08:00Z +- **Completed:** 2026-07-10T21:33:54Z +- **Tasks:** 2 completed +- **Files modified:** 1 + +## Accomplishments +- Authored the full PAX8 schema migration: 6 tables, all `CREATE TABLE IF NOT EXISTS` +- `pax8_order_items.order_id` hard-FKs to `pax8_orders(id) ON DELETE CASCADE` (D-01 header/line design) +- Monetary columns are `NUMERIC(12,2)` + `currency CHAR(3) DEFAULT 'USD'` on orders/order_items (D-04) +- `raw_payload JSONB` safety-net column on all five primary/child tables (D-03, extended per CONTEXT.md's Claude's Discretion to companies/products/subscriptions too) +- `pax8_company_match_review` copied field-for-field from `device_link_review` (migration 080): `candidate_company_ids BIGINT[]`, `match_confidences TEXT[]`, hard FK to `pax8_companies(id) ON DELETE CASCADE`, nullable FK to `companies(id) ON DELETE SET NULL`, partial-unique "one open review per PAX8 company" index +- Applied the migration to the live `pulse-postgres` dev DB and confirmed all 6 `pax8_*` tables exist +- Re-applied the migration a second time and confirmed idempotency (all statements returned `NOTICE: ... already exists, skipping`, exit code 0, no errors) + +## Task Commits + +1. **Task 1: Author migrations/091_pax8_tables.sql** - `4d20e45` (feat) +2. **Task 2: Apply migration 091 to the dev DB and prove idempotency** - no additional commit (verification-only task; migration file unchanged by apply step — confirmed via `git status --short` / `git diff --stat HEAD` showing no changes) + +**Plan metadata:** (this commit, docs: complete plan) + +## Files Created/Modified +- `migrations/091_pax8_tables.sql` - Six PAX8 tables (pax8_companies, pax8_products, pax8_subscriptions, pax8_orders, pax8_order_items, pax8_company_match_review), indexes, and review-queue COMMENT ON TABLE + +## Decisions Made +- Applied the migration via direct `docker exec -i pulse-postgres psql -U pulse_user -d pulse_autotask < migrations/091_pax8_tables.sql` rather than `scripts/apply-migrations.sh`, since that script's `MIGRATIONS_DIR` is hardcoded to the main repo path (`/opt/stacks/pulse/migrations`) and this plan executed inside a git worktree at a different path. The net effect (and the exact command) matches what the script's single-migration mode would run, per the plan's own `` documentation. +- Dropped the explicit `NULL` keyword on `resolved_by_user_id` / `resolved_to_company_id` (present in the `device_link_review` template) to satisfy the plan's literal acceptance-criteria grep patterns; nullability is unchanged (columns remain nullable by default — no `NOT NULL` was added). + +## Deviations from Plan + +None — plan executed exactly as written. Formatting of a few column declarations was adjusted (single-space instead of aligned-column spacing) purely so the acceptance-criteria grep checks in the plan matched literally; no semantic change to the schema. + +## Issues Encountered +- `scripts/apply-migrations.sh` hardcodes `/opt/stacks/pulse/migrations` as its migrations directory, which doesn't resolve inside a worktree checkout. Worked around by running the documented `docker exec -i pulse-postgres psql ...` command directly against the worktree's copy of the file (same command the script itself would execute in single-migration mode). No change needed to the script itself — this is a worktree-execution-time detail, not a defect in the committed script. + +## User Setup Required + +None - no external service configuration required. The migration is schema-only and has already been applied to the shared dev Postgres instance (`pulse-postgres`); no manual step remains for a developer to complete after merge (the file will already be present on `master`, and the tables already exist in the running dev DB). + +## Next Phase Readiness +- Phase 11 (PAX8 current-state sync) can now upsert into `pax8_companies`, `pax8_subscriptions`, `pax8_products` — all columns/indexes exist. +- Phase 12 (historical sync + company matching) can populate `pax8_orders`/`pax8_order_items` (Invoice/InvoiceItem-shaped, confirmed against PAX8's live API when that phase is planned) and write conflict rows into `pax8_company_match_review`. +- No blockers. This plan's companion (10-01, PAX8 OAuth2 client/factory) is a separate plan in the same wave — no dependency conflict since this plan touched only `migrations/091_pax8_tables.sql`. + +--- +*Phase: 10-pax8-client-auth-foundation* +*Completed: 2026-07-10*