docs(10): capture phase context

This commit is contained in:
lorentz 2026-07-10 12:18:05 -04:00
parent 25edff0462
commit 98aed06f8f
2 changed files with 238 additions and 0 deletions

View file

@ -0,0 +1,163 @@
# Phase 10: PAX8 Client & Auth Foundation - Context
**Gathered:** 2026-07-10
**Status:** Ready for planning
<domain>
## Phase Boundary
Pulse can authenticate to the PAX8 REST API (`api.pax8.com/v1`) via OAuth2
client-credentials, and the Postgres schema for all PAX8 entities exists —
companies, subscriptions, product catalog, orders/order-line-items, and a
company-match review queue. No sync logic, no company-matching logic, no
`/pax8` UI, and no scheduler wiring in this phase — those are Phase 11-14.
This phase proves the auth + schema foundation only.
</domain>
<decisions>
## Implementation Decisions
### Orders / Invoices Schema
- **D-01:** Two-table design — `pax8_orders` (header: order id, company,
order date, total, status) + `pax8_order_items` (line-item detail:
`order_id` FK, product/SKU, quantity, unit price, line total). Matches
PAX8's own order shape (an order has N line items) and avoids repeating
order-level fields on every line.
- **D-02:** No lookback-window bound. Sync (built in a later phase) pulls
full order history PAX8's API returns on first sync — no "earliest
synced" or window-config column needed in this phase's migration.
- **D-03:** Both `pax8_orders` and `pax8_order_items` carry a `raw_payload
JSONB` column alongside typed columns, as a safety net for PAX8 fields
not yet modeled. Mirrors the `itglue-search.ts` precedent of not
discarding API data even when only a subset is used today.
- **D-04:** Monetary amounts use `NUMERIC(12,2)` + a `currency CHAR(3)
DEFAULT 'USD'` column on both orders and line items — cheap insurance
against a future non-USD client without requiring a schema migration
later.
### Claude's Discretion
The user chose to discuss only Orders/Invoices granularity. The following
gray areas were surfaced but explicitly left to the planner/executor,
default to the closest existing codebase pattern:
- **Company match/review table shape** — model on `device_link_review`
(migration `080_device_xref_company_id.sql`): a conflicts/review queue
with candidate match(es), confidence, `resolved_at`/`resolved_by_user_id`/
`resolution_note`. Adapt field names for PAX8 companies →
Autotask companies instead of device → CI matching. This table is
created in this phase's migration but populated by Phase 12's matching
logic — schema should anticipate that consumer without over-designing it.
- **Product catalog scope** — no explicit decision; planner may choose
full-catalog sync or lazy/referenced-only population. This is a Phase
11 sync-service decision more than a Phase 10 schema decision — the
`pax8_products` table shape should accommodate either without requiring
a redesign (e.g., don't add a "referenced only" constraint at the schema
level).
- **Raw payload retention on other tables** — whether `pax8_companies`,
`pax8_subscriptions`, and `pax8_products` also get a `raw_payload`
JSONB column. Given D-03 established this pattern for orders, applying
it consistently across all four PAX8 tables is a reasonable default
unless the planner has a specific reason not to (e.g., a table is fully
and confidently typed).
</decisions>
<canonical_refs>
## Canonical References
**Downstream agents MUST read these before planning or implementing.**
### Project scope & requirements
- `.planning/PROJECT.md` — Current Milestone: v2.0 PAX8 Integration section;
states read-only scope, out-of-scope write access, and the SEED-003
future-consumer design note ("build the Postgres schema with that eventual
consumer in mind - clean, well-typed tables, avoid PAX8-API-shaped blobs")
- `.planning/REQUIREMENTS.md` — PAX8-01, PAX8-02 (this phase's requirement
IDs); also PAX8-03..14 for downstream-phase awareness of what this schema
must support later
- `.planning/seeds/SEED-002-pax8-integration.md` — original exploration:
auth breadcrumbs (client ID/secret already provisioned), pattern
breadcrumbs (`lib/services/<name>-client.ts` + `<name>-factory.ts`), entity
list (companies/subscriptions/catalog/orders), and the "fuzzy name match
at sync time, flag ambiguous for manual review" design fork
- `.planning/research/questions.md``RESEARCH-pax8-company-identifiers`:
open question on whether PAX8 exposes a stable identifier (domain,
external ID) beyond company name — worth checking PAX8's `companies`
endpoint response shape before Phase 12 commits fully to fuzzy name
matching. Not blocking for this phase (schema should keep the door open:
don't build the match table assuming name is the only comparable field).
### Existing patterns to follow
- `lib/services/msgraph-client.ts` + `lib/services/msgraph-factory.ts`
closest existing OAuth2 client-credentials pattern (token endpoint POST,
in-memory token + expiry cache, `is<Name>Configured()` + throw-if-missing).
PAX8's auth client should follow this shape, not the Veeam API-key shape.
- `lib/services/veeam-factory.ts` — canonical `is<Name>Configured()` +
singleton + throw-with-clear-message-if-missing-creds pattern (same shape
msgraph-factory.ts follows; both are valid reference points).
- `migrations/080_device_xref_company_id.sql` (`device_link_review` table) —
reference shape for the PAX8 company-match/review table (see Claude's
Discretion above).
- `migrations/081_integration_settings.sql` — integration toggle table;
PAX8's toggle row is added in Phase 13, not this phase — do not seed it
here.
- `CLAUDE.md` "External integrations" table — env var prefix convention
(`PAX8_*`); add PAX8 to this table and to `INTEGRATIONS.md` once the
client exists.
</canonical_refs>
<code_context>
## Existing Code Insights
### Reusable Assets
- `lib/services/msgraph-client.ts` — direct template for OAuth2
client-credentials token exchange (`getToken()` with expiry-aware
in-memory caching), including the exact `fetch` + `URLSearchParams` shape
to adapt for PAX8's token endpoint.
- `lib/services/msgraph-factory.ts` — direct template for
`isPax8Configured()` / `getPax8Client()` / `resetPax8Client()`.
### Established Patterns
- Factory + `is<Name>Configured()` + throw-if-missing-creds is used
uniformly across all ~15 existing integrations — no deviation expected
here.
- Migration numbering is sequential regardless of gaps; next number after
`090_ticket_reconcile_schedule.sql` is `091`.
- Match/review queues for ambiguous cross-system links use a dedicated
table (`device_link_review`) rather than a status column on the primary
entity table, keeping the "needs human review" concern separate from
the synced data itself.
### Integration Points
- New files: `lib/services/pax8-client.ts`, `lib/services/pax8-factory.ts`,
`lib/types/pax8.ts` (types barrel, per `lib/types/<domain>.ts` convention),
new migration `091_pax8_tables.sql` (or similar name).
- No route/nav/UI integration in this phase (`UI hint: no` per ROADMAP.md).
</code_context>
<specifics>
## Specific Ideas
No specific UI or behavioral references were given — this phase is schema
and client plumbing only. The concrete decisions are the four Orders/
Invoices schema points (D-01 through D-04) above.
</specifics>
<deferred>
## Deferred Ideas
None — discussion stayed within phase scope. (Company matching logic,
product catalog population strategy, the `/pax8` UI, and scheduler wiring
are already sequenced into Phases 11-14 per ROADMAP.md and REQUIREMENTS.md
— not deferred from this discussion, just out of this phase's boundary.)
</deferred>
---
*Phase: 10-pax8-client-auth-foundation*
*Context gathered: 2026-07-10*

View file

@ -0,0 +1,75 @@
# Phase 10: PAX8 Client & Auth Foundation - Discussion Log
> **Audit trail only.** Do not use as input to planning, research, or execution agents.
> Decisions are captured in CONTEXT.md — this log preserves the alternatives considered.
**Date:** 2026-07-10
**Phase:** 10-pax8-client-auth-foundation
**Areas discussed:** Orders/invoices granularity
---
## Gray areas presented (multiSelect)
| Area | Description | Selected |
|------|-------------|----------|
| Match/review table shape | Model on `device_link_review` (candidate array + confidences) vs. simpler single-best-guess + status enum | |
| Product catalog scope | Full PAX8 catalog vs. lazily populated from referenced subscriptions only | |
| Orders/invoices granularity | Line-item grain with header+items tables vs. flat single table | ✓ |
| Raw payload retention | JSONB safety-net column per row vs. fully typed columns only | |
**User selected:** Orders/invoices granularity only. The other three were left to the planner's discretion (see CONTEXT.md "Claude's Discretion").
---
## Orders/Invoices Granularity
### Q1 — Grain
| Option | Description | Selected |
|--------|-------------|----------|
| Two tables (header + line items) | `pax8_orders` (order id, company, order date, total, status) + `pax8_order_items` (order_id FK, product/SKU, quantity, unit price, line total). Matches PAX8's own order shape. | ✓ |
| One flat table | `pax8_order_items` only, order-level fields repeated per line. Simpler queries, no natural home for order status/totals. | |
**User's choice:** Two tables (header + line items)
### Q2 — Backfill window
| Option | Description | Selected |
|--------|-------------|----------|
| Full history, no bound | Pull everything on first sync, no lookback-window column needed. | ✓ |
| Bounded lookback window | e.g. last 24 months; requires a config value and sync logic to respect it. | |
**User's choice:** Full history, no bound
### Q3 — Raw JSON safety net
| Option | Description | Selected |
|--------|-------------|----------|
| Yes, keep raw_payload JSONB | Typed columns for used fields + raw_payload JSONB per row for anything not yet modeled. | ✓ |
| No, typed columns only | Fully committed to normalized columns per PROJECT.md's "avoid PAX8-API-shaped blobs" guidance. | |
**User's choice:** Yes, keep raw_payload JSONB
### Q4 — Currency handling
| Option | Description | Selected |
|--------|-------------|----------|
| NUMERIC amount + currency code column | `NUMERIC(12,2)` + `CHAR(3) DEFAULT 'USD'` on orders and line items. | ✓ |
| USD-only, no currency column | `NUMERIC(12,2)` only, assume USD everywhere. | |
**User's choice:** NUMERIC amount + currency code column
**Notes:** No additional follow-up questions requested — user moved straight to wrap-up after the 4 questions.
---
## Claude's Discretion
- Company match/review table shape — follow `device_link_review` (migration 080) as the closest precedent, adapted for PAX8 company → Autotask company matching.
- Product catalog scope (full vs. lazy/referenced-only) — deferred to Phase 11's sync-service design; schema should not preclude either approach.
- Raw payload retention on `pax8_companies` / `pax8_subscriptions` / `pax8_products` — reasonable to apply the same JSONB safety-net pattern established for orders (D-03), for consistency, unless the planner has a specific reason not to.
## Deferred Ideas
None — discussion stayed within Phase 10's boundary. Company matching logic (Phase 12), product catalog population (Phase 11), the `/pax8` UI (Phase 14), and scheduler wiring (Phase 13) are already sequenced elsewhere in ROADMAP.md/REQUIREMENTS.md.