chore: merge executor worktree (worktree-agent-a0e50d71b0b8ba829)
This commit is contained in:
commit
dea8df0c53
4 changed files with 140 additions and 1 deletions
106
.planning/phases/13-scheduler-admin-toggle/13-01-SUMMARY.md
Normal file
106
.planning/phases/13-scheduler-admin-toggle/13-01-SUMMARY.md
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
---
|
||||
phase: 13-scheduler-admin-toggle
|
||||
plan: 01
|
||||
subsystem: infra
|
||||
tags: [sync-scheduler, cron, postgres, pax8, integration-toggle]
|
||||
|
||||
# Dependency graph
|
||||
requires:
|
||||
- phase: 11-company-catalog-subscription-sync
|
||||
provides: Pax8SyncService.fullSync() companies/subscriptions/products orchestration
|
||||
- phase: 12-orders-invoices-company-matching
|
||||
provides: Pax8SyncService.fullSync() extended with orders/invoices + company matching
|
||||
provides:
|
||||
- Idempotent pax8-daily sync_schedules seed row (migration 096)
|
||||
- executeScheduledSync dual-guarded pax8-daily dispatch branch
|
||||
- CLAUDE.md precedent note for DB-toggle-gates-action behavior
|
||||
affects: [13-02-admin-toggle-route, 13-03-live-verification]
|
||||
|
||||
# Tech tracking
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns: [dual-guard scheduler branch (config + DB toggle), idempotent sync_schedules seed migration]
|
||||
|
||||
key-files:
|
||||
created: [migrations/096_pax8_daily_schedule.sql]
|
||||
modified: [lib/services/sync-scheduler.ts, CLAUDE.md]
|
||||
|
||||
key-decisions:
|
||||
- "pax8-daily seeded only via migration 096, never added to the in-code defaultSchedules array, per existing precedent for existing installs"
|
||||
- "PAX8 disabled-check is inline in the scheduler branch only (D-01) — no shared helper extracted, no changes to other integrations' branches"
|
||||
- "cron 0 4 * * * groups pax8-daily with the backend-reconciliation cluster (contract-services, tickets-reconcile) per D-04"
|
||||
|
||||
patterns-established:
|
||||
- "Dual-guard scheduler branch: isXConfigured() env check first, then integration_settings.disabled DB check, each with a distinct skip log, before calling the sync service"
|
||||
|
||||
requirements-completed: [PAX8-07, PAX8-09]
|
||||
|
||||
# Metrics
|
||||
duration: ~15min
|
||||
completed: 2026-07-11
|
||||
---
|
||||
|
||||
# Phase 13 Plan 01: Scheduler pax8-daily Wiring Summary
|
||||
|
||||
**Wired the existing `Pax8SyncService.fullSync()` into the daily cron scheduler via a new idempotent migration seed and a dual-guarded `pax8-daily` dispatch branch that respects both the env-config check and the `integration_settings` DB disable toggle.**
|
||||
|
||||
## Performance
|
||||
|
||||
- **Duration:** ~15 min
|
||||
- **Tasks:** 3
|
||||
- **Files modified:** 3 (1 created, 2 modified)
|
||||
|
||||
## Accomplishments
|
||||
- `migrations/096_pax8_daily_schedule.sql` idempotently seeds a `pax8-daily` row (cron `0 4 * * *`, `is_enabled=false`) using the `WHERE NOT EXISTS` style from migration 089; applied to the running dev DB and verified idempotent on a second run.
|
||||
- `executeScheduledSync` in `lib/services/sync-scheduler.ts` now dispatches `pax8-daily` to `getPax8SyncService().fullSync('scheduled')`, gated by two independent checks: `isPax8Configured()` (env vars) and `integration_settings.key='pax8'` disabled flag (DB toggle), each with a distinct skip log line.
|
||||
- `CLAUDE.md`'s "Operator config" section now documents PAX8 as the first integration where the DB toggle gates an action (scheduler skip + 403 on the manual route), not just health-check display.
|
||||
|
||||
## Task Commits
|
||||
|
||||
Each task was committed atomically:
|
||||
|
||||
1. **Task 1: Seed the pax8-daily schedule via migration 096** - `230296c` (feat)
|
||||
2. **Task 2: Add dual-guarded pax8-daily dispatch branch to executeScheduledSync** - `d07c0b7` (feat)
|
||||
3. **Task 3: Record the DB-toggle-gates-action precedent in CLAUDE.md** - `d5456bb` (docs)
|
||||
|
||||
_No plan metadata commit yet — orchestrator handles that after wave completion (worktree mode)._
|
||||
|
||||
## Files Created/Modified
|
||||
- `migrations/096_pax8_daily_schedule.sql` - Idempotent seed of the pax8-daily sync_schedules row
|
||||
- `lib/services/sync-scheduler.ts` - Extended `sync_type` union with `'pax8-daily'`; added dual-guarded dispatch branch after the appgate branch in `executeScheduledSync`
|
||||
- `CLAUDE.md` - Added a note in Operator config / Integration disable documenting PAX8 as the first DB-toggle-gates-action precedent
|
||||
|
||||
## Decisions Made
|
||||
- Followed the exact 089-style `WHERE NOT EXISTS` seed pattern (not 090's `ON CONFLICT`), per explicit CONTEXT.md/PATTERNS.md direction.
|
||||
- Kept the disabled-check PAX8-only and inline in the scheduler branch (D-01) rather than extracting a shared helper, since generalizing to all integrations was explicitly out of scope for this phase.
|
||||
- Placed the new branch immediately after the appgate branch to keep integration-toggle blocks together in the switch, per PATTERNS.md guidance.
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
None - plan executed exactly as written.
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
None. `npx tsc --noEmit --pretty` passed with no output after the sync-scheduler.ts edit. The migration was applied to the running `pulse-postgres` container using the default `pulse_user`/`pulse_autotask` credentials confirmed from `docker-compose.yml` (no `.env` overrides present).
|
||||
|
||||
## User Setup Required
|
||||
|
||||
None - no external service configuration required. The `pax8-daily` schedule ships `is_enabled=false`; an admin must opt in via the schedule editor separately, as documented in the plan.
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
- The `pax8-daily` schedule row exists and the scheduler branch is wired and type-checked; ready for the manual-trigger route toggle (D-02, likely 13-02) and live cron/disable-skip verification (13-03).
|
||||
- No changes were made to `app/api/pax8/sync/route.ts` or `lib/services/integration-health.ts` in this plan — those remain for subsequent plans in this phase per the plan's `files_modified` scope.
|
||||
|
||||
## Self-Check: PASSED
|
||||
|
||||
- FOUND: migrations/096_pax8_daily_schedule.sql
|
||||
- FOUND: .planning/phases/13-scheduler-admin-toggle/13-01-SUMMARY.md
|
||||
- FOUND commit: 230296c (Task 1)
|
||||
- FOUND commit: d07c0b7 (Task 2)
|
||||
- FOUND commit: d5456bb (Task 3)
|
||||
- FOUND commit: 73b33f7 (SUMMARY.md)
|
||||
|
||||
---
|
||||
*Phase: 13-scheduler-admin-toggle*
|
||||
*Completed: 2026-07-11*
|
||||
|
|
@ -137,6 +137,10 @@ Examples: `getAutotaskClient()`, `getMsgraphClient()`, `getDattoRmmClient()`,
|
|||
`disabled_at`, and an optional `disabled_reason`.
|
||||
In both cases live auth checks still run (logs surface the underlying
|
||||
state); the UI ignores the result for disabled integrations.
|
||||
- **PAX8 is the first exception**: disabling `key='pax8'` doesn't just
|
||||
suppress health-check display — the `pax8-daily` scheduler branch skips
|
||||
`fullSync()` and `POST /api/pax8/sync` returns 403. Every other
|
||||
integration's toggle today is display-only.
|
||||
|
||||
## Watch out for
|
||||
- A `.env` file is committed to the repo. Treat secrets as potentially real; don't
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ export interface ScheduleConfig {
|
|||
name: string;
|
||||
description: string;
|
||||
cron_expression: string;
|
||||
sync_type: 'incremental' | 'full' | 'veeam-incremental' | 'veeam-full' | 'veeam-rpo-check' | 'contract-services' | 'engagement-daily' | 'zoom-daily' | 'morning-summary' | 'ticket-digest-daily' | 'ticket-digest-weekly' | 'ticket-digest-monthly' | 'device-link-reconcile' | 'integration-health' | 'qbo' | 'appgate-sessions' | 'appgate-daily' | 'tickets-reconcile';
|
||||
sync_type: 'incremental' | 'full' | 'veeam-incremental' | 'veeam-full' | 'veeam-rpo-check' | 'contract-services' | 'engagement-daily' | 'zoom-daily' | 'morning-summary' | 'ticket-digest-daily' | 'ticket-digest-weekly' | 'ticket-digest-monthly' | 'device-link-reconcile' | 'integration-health' | 'qbo' | 'appgate-sessions' | 'appgate-daily' | 'tickets-reconcile' | 'pax8-daily';
|
||||
years_back?: number;
|
||||
is_enabled: boolean;
|
||||
last_run?: Date;
|
||||
|
|
@ -461,6 +461,22 @@ class SyncScheduler {
|
|||
console.log(
|
||||
`[SCHEDULER] tickets-reconcile: scanned=${result.scanned} updated=${result.updated} flippedComplete=${result.statusFlippedToComplete} softDeleted=${result.softDeleted} errors=${result.errors}`
|
||||
);
|
||||
} else if (config.sync_type === 'pax8-daily') {
|
||||
const { isPax8Configured } = await import('@/lib/services/pax8-factory');
|
||||
if (!isPax8Configured()) {
|
||||
console.log('[SCHEDULER] Skipping pax8-daily — PAX8 not configured');
|
||||
} else {
|
||||
const disabledRes = await postgresClient.query<{ disabled: boolean }>(
|
||||
`SELECT disabled FROM integration_settings WHERE key = 'pax8'`
|
||||
);
|
||||
const isDisabled = disabledRes.rows[0]?.disabled === true;
|
||||
if (isDisabled) {
|
||||
console.log('[SCHEDULER] Skipping pax8-daily — PAX8 disabled via /admin/integrations');
|
||||
} else {
|
||||
const { getPax8SyncService } = await import('@/lib/services/pax8-sync-service');
|
||||
await getPax8SyncService().fullSync('scheduled');
|
||||
}
|
||||
}
|
||||
} else if (config.sync_type === 'incremental') {
|
||||
await this.syncService.incrementalSync('scheduled');
|
||||
} else {
|
||||
|
|
|
|||
13
migrations/096_pax8_daily_schedule.sql
Normal file
13
migrations/096_pax8_daily_schedule.sql
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
-- Migration 096: Seed the pax8-daily sync schedule.
|
||||
--
|
||||
-- The sync_scheduler.createDefaultSchedules() path only seeds defaults on a
|
||||
-- virgin sync_schedules table; this migration covers existing installs.
|
||||
-- sync_schedules has no unique constraint on name, so guard with NOT EXISTS
|
||||
-- (same pattern as migration 089's appgate-sessions/appgate-daily seeds).
|
||||
|
||||
INSERT INTO sync_schedules (id, name, description, cron_expression, sync_type, is_enabled)
|
||||
SELECT 'pax8-daily',
|
||||
'PAX8 Daily Sync',
|
||||
'Full PAX8 sync — companies, subscriptions, products, orders, and company matching, daily at 4 AM.',
|
||||
'0 4 * * *', 'pax8-daily', false
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sync_schedules WHERE name = 'PAX8 Daily Sync');
|
||||
Loading…
Add table
Add a link
Reference in a new issue