wulf-pulse/.planning/phases/24-aws-route-53-dns-sync-track-changes-crud-operations-full-aud/24-06-PLAN.md

16 KiB

phase plan type wave depends_on files_modified autonomous requirements must_haves
24-aws-route-53-dns-sync-track-changes-crud-operations-full-aud 06 execute 3
24-02
lib/services/sync-scheduler.ts
app/admin/sync/page.tsx
public/logos/route53.svg
true
SC-1
SC-6
truths artifacts key_links
SC-1/D-11: Two cron schedules exist — route53-incremental (every 15 minutes) and route53-full (daily) — both seeded into sync_schedules and editable from /admin, matching how every other integration's schedules are managed
SC-6/D-09: A Route 53 tile appears in the /admin/sync integration list with the same shape as the Veeam / Datto RMM / PAX8 tiles, linking to the dedicated /admin/sync/route53 detail page built in plan 24-07 — the existing per-integration pattern, not folded into an existing page
D-10: The scheduler dispatch branch for Route 53 checks isRoute53Configured() only — it does NOT consult integration_settings.disabled, so disabling Route 53 in /admin/integrations suppresses health display without stopping sync (PAX8 remains the sole blocking exception)
New schedules are seeded is_enabled: false, matching every other newly-introduced integration in this file, so nothing starts hitting AWS before an operator enables it
path provides contains
lib/services/sync-scheduler.ts route53-incremental and route53-full in the sync_type union, defaultSchedules, and the dispatch chain route53-incremental
path provides contains
app/admin/sync/page.tsx route53 entry in the INTEGRATIONS tile array id: 'route53'
path provides
public/logos/route53.svg Tile logo asset
from to via pattern
lib/services/sync-scheduler.ts lib/services/route53-sync-service.ts dynamic import of getRoute53SyncService inside the dispatch branch getRoute53SyncService
from to via pattern
app/admin/sync/page.tsx /admin/sync/route53 tile href /admin/sync/route53
Wire Route 53 into the two existing operator surfaces it must appear in: the node-cron sync scheduler (D-11's incremental + daily-full cadence) and the `/admin/sync` integration tile list.

Purpose: SC-1 (sync runs on a schedule) and SC-6 (integration appears in the existing sync admin UI/scheduler alongside the others). Output: modified lib/services/sync-scheduler.ts and app/admin/sync/page.tsx, plus a public/logos/route53.svg tile asset.

<execution_context> @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md </execution_context>

@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/24-aws-route-53-dns-sync-track-changes-crud-operations-full-aud/24-PATTERNS.md @.planning/phases/24-aws-route-53-dns-sync-track-changes-crud-operations-full-aud/24-CONTEXT.md @.planning/phases/24-aws-route-53-dns-sync-track-changes-crud-operations-full-aud/24-02-SUMMARY.md

lib/services/sync-scheduler.ts: line 25: sync_type: 'incremental' | 'full' | 'veeam-incremental' | ... | 'phishing-sweep'; line 147: sync_schedules table DDL — sync_type VARCHAR(30) NOT NULL (no CHECK constraint; 'route53-incremental' is 19 chars and fits) line 180: const defaultSchedules = [ ... ] // entries: { id, name, description, cron_expression, sync_type, is_enabled } line 312: for (const schedule of defaultSchedules) { INSERT INTO sync_schedules ... ON CONFLICT DO NOTHING } line 413+: dispatch chain — if (config.sync_type === 'veeam-incremental') { ... } else if (...) line 478-493: pax8-daily branch — the ONE branch that also checks integration_settings.disabled. Route 53 must NOT copy that gate (D-10). line 494-504: mimecast-sync branch — config-check-only pattern; this is the shape to copy.

app/admin/sync/page.tsx: lines 9-16: interface IntegrationCard { id, category, product, description, href, logo, color } lines 20-29: const INTEGRATIONS: IntegrationCard[] = [ ... 'pax8' entry is last ] lines 32-39: COLOR_MAP — available keys: red, green, blue, orange, purple, gray (no others) line 266: const colors = COLOR_MAP[intg.color]; // an unmapped color yields undefined

lib/services/route53-sync-service.ts: getRoute53SyncService(): Route53SyncService #fullSync(triggeredBy?): Promise #incrementalSync(triggeredBy?): Promise

lib/services/route53-factory.ts: isRoute53Configured(): boolean

Task 1: Add route53 sync types, schedules, and dispatch branches to the scheduler lib/services/sync-scheduler.ts - lib/services/sync-scheduler.ts (read the full file — the sync_type union at line 25, the sync_schedules DDL at line 147, defaultSchedules at lines 180-311, the seed loop at line 312, and the whole dispatch chain from line 413 onward) - lib/services/route53-sync-service.ts (getRoute53SyncService — plan 24-02) - lib/services/route53-factory.ts (isRoute53Configured — plan 24-01) - .planning/phases/24-aws-route-53-dns-sync-track-changes-crud-operations-full-aud/24-PATTERNS.md (sync-scheduler section — the exact branch shape, and the explicit instruction NOT to copy PAX8's disable gate) - .planning/phases/24-aws-route-53-dns-sync-track-changes-crud-operations-full-aud/24-CONTEXT.md (D-10, D-11) Modify `lib/services/sync-scheduler.ts` in three places.
  1. Line 25 sync_type union: append | 'route53-incremental' | 'route53-full' to the existing pipe-delimited string union. Do not reformat the rest of the line.

  2. defaultSchedules array (starts line 180): append two entries matching the exact object shape of the surrounding veeam-incremental / veeam-full entries at lines 198-221:

    • id route53-incremental, name Route 53 Incremental Sync, description Syncs AWS Route 53 hosted zones and records every 15 minutes, cron_expression */15 * * * *, sync_type route53-incremental, is_enabled false.
    • id route53-full, name Route 53 Full Sync, description Full AWS Route 53 zone and record reconciliation daily at 4:00 AM, cron_expression 0 4 * * *, sync_type route53-full, is_enabled false. is_enabled: false matches every newly-introduced integration in this file — an operator enables them from /admin after confirming credentials. The 0 4 * * * slot avoids the known collisions at 2:00 AM (veeam-full, qbo-sync-2am, mimecast-sync) and 3:00 AM (weekly-full's 0 3 * * 0). Enumerate every cron_expression in the live array before committing and pick the next free hour if 4:00 AM is now occupied. The seed loop at line 312 uses ON CONFLICT DO NOTHING, so existing deployments pick these up without overwriting operator-modified rows.
  3. Dispatch chain (line 413 onward): add two else if branches following the mimecast-sync config-check-only shape at lines 494-504, NOT the pax8-daily shape at lines 478-493:

    • else if (config.sync_type === 'route53-incremental') — dynamically await import('@/lib/services/route53-factory') for isRoute53Configured; if false, log [SCHEDULER] Skipping route53-incremental — Route 53 not configured and return; otherwise dynamically await import('@/lib/services/route53-sync-service') and call getRoute53SyncService().incrementalSync('scheduled').
    • else if (config.sync_type === 'route53-full') — same shape, calling fullSync('scheduled'). Use dynamic import() in both branches (as every other branch does) so the sync service module is not eager-loaded at scheduler-import time. The scheduler self-initializes as a side effect of its first server-side import, and a top-level import here would pull the AWS SDK into every server module graph.

CRITICAL (D-10): do NOT add an integration_settings.disabled query to either branch. PAX8 is the codebase's sole exception where disabling also blocks sync; CONTEXT.md D-10 states Route 53 explicitly does not join that list. Add a short comment above the first Route 53 branch recording this, so a future reader does not "fix" the apparent inconsistency with the PAX8 branch sitting a few lines above.

Confirm sync_schedules.sync_type at line 147 is VARCHAR(30) with no CHECK constraint before relying on the new values fitting — route53-incremental is 19 characters. npx tsc --noEmit --pretty && test $(grep -c "route53-incremental" lib/services/sync-scheduler.ts) -ge 3 && test $(grep -c "route53-full" lib/services/sync-scheduler.ts) -ge 3 && test $(grep -A12 "config.sync_type === 'route53" lib/services/sync-scheduler.ts | grep -c integration_settings) -eq 0 && echo PASS <acceptance_criteria> - route53-incremental and route53-full each appear at least 3 times in lib/services/sync-scheduler.ts (union, defaultSchedules, dispatch) - Both new defaultSchedules entries have is_enabled: false - Neither Route 53 dispatch branch body contains integration_settings (D-10) — grep over the 12 lines following each branch head returns 0 - Both dispatch branches use dynamic await import(...); no top-level route53 import exists: grep -c "^import.*route53" lib/services/sync-scheduler.ts returns 0 - The route53-full cron hour differs from every other daily cron_expression hour in defaultSchedules — verified by enumerating the array - A comment above the Route 53 branches records why the PAX8 disable gate is deliberately absent - npx tsc --noEmit --pretty exits 0 </acceptance_criteria> Scheduler knows both Route 53 sync types, seeds them disabled, dispatches via dynamic import, and never consults the disable toggle.

Task 2: Add the Route 53 tile to /admin/sync app/admin/sync/page.tsx, public/logos/route53.svg - app/admin/sync/page.tsx (read the full file — the IntegrationCard interface at lines 9-16, the INTEGRATIONS array at lines 20-29, COLOR_MAP at lines 32-39, and the tile render at line ~266 to confirm how `logo` is consumed) - public/logos/ directory listing (confirm the existing asset naming convention — every current asset is `.ico`) - .planning/phases/24-aws-route-53-dns-sync-track-changes-crud-operations-full-aud/24-PATTERNS.md (admin sync page section) Create `public/logos/route53.svg` — a small hand-authored SVG with `viewBox="0 0 32 32"`, `xmlns="http://www.w3.org/2000/svg"`, using the AWS orange `#FF9900`, containing a simple globe-or-DNS-node glyph built from primitive shapes only. It must contain no `