docs(15-03): add plan execution summary
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012wWroM6FXkQJiH3JgYcony
This commit is contained in:
parent
b199d9991c
commit
63b4aabc5e
1 changed files with 95 additions and 0 deletions
|
|
@ -0,0 +1,95 @@
|
|||
---
|
||||
phase: 15-data-model-detection-ticket-evidence
|
||||
plan: 03
|
||||
subsystem: services
|
||||
tags: [phishing-triage, webhook, cron, scheduler, detection]
|
||||
|
||||
# Dependency graph
|
||||
requires:
|
||||
- phase: 15-02
|
||||
provides: "detectPhishingTicket(ticket) shared detection core (phishing-detector.ts)"
|
||||
provides:
|
||||
- "lib/services/phishing-sweep-service.ts — sweepPhishingTickets() bounded cron reconciliation"
|
||||
- "webhook-service.ts triggerPhishingDetection() — fire-and-forget detection on ticket.created"
|
||||
- "sync-scheduler.ts phishing-sweep sync_type + dispatch branch + defaultSchedules entry"
|
||||
- "migrations/098_phishing_sweep_schedule.sql — phishing-sweep schedule seed for existing installs"
|
||||
affects: [16-message-parsing, 17-mimecast-blast-radius]
|
||||
|
||||
# Tech tracking
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns:
|
||||
- "Fire-and-forget webhook hook-in mirroring triggerWorkflowEngine (.catch(err => console.error(...)), never awaited in the request path"
|
||||
- "Bounded cron sweep mirroring reconcileStaleTickets (LIMIT + recent-activity window + per-row try/catch, never rethrow)"
|
||||
- "Dynamic await import() in every sync-scheduler dispatch branch (no eager worker import per CLAUDE.md)"
|
||||
|
||||
key-files:
|
||||
created: [lib/services/phishing-sweep-service.ts, migrations/098_phishing_sweep_schedule.sql]
|
||||
modified: [lib/services/webhook-service.ts, lib/services/sync-scheduler.ts]
|
||||
|
||||
key-decisions:
|
||||
- "Reused ticket-reconciliation-service.ts's structure verbatim for the sweep (SELECT-with-LIMIT, per-row try/catch, createSyncLogger, aggregate result object) rather than inventing a new shape"
|
||||
- "triggerPhishingDetection reads payload.entity.createdByContactID (not creatorContactID, which does not exist) into created_by_contact_id — verified by grep since a typo would compile cleanly (payload.entity is Record<string, any>) but silently degrade EVID-01 requester capture at runtime"
|
||||
- "phishing-sweep schedule is registered disabled-by-default (is_enabled: false), matching the tickets-reconcile precedent — an admin must opt in via /admin"
|
||||
|
||||
requirements-completed: [DETECT-01, DETECT-02]
|
||||
|
||||
# Metrics
|
||||
duration: 9min
|
||||
completed: 2026-07-15
|
||||
---
|
||||
|
||||
# Phase 15 Plan 03: Webhook + Cron Sweep Wiring Summary
|
||||
|
||||
**Wired the Plan 02 `detectPhishingTicket` core into both of Pulse's established scan triggers — a fire-and-forget hook on the ticket.created webhook and a bounded daily cron sweep (`sweepPhishingTickets`, LIMIT 500 / 7-day window) — plus migration 098 to seed the disabled-by-default `phishing-sweep` schedule row for existing installs.**
|
||||
|
||||
## Performance
|
||||
|
||||
- **Duration:** 9 min
|
||||
- **Started:** 2026-07-15T11:49:00Z
|
||||
- **Completed:** 2026-07-15T11:58:00Z
|
||||
- **Tasks:** 3 completed
|
||||
- **Files modified:** 4 (2 created, 2 modified)
|
||||
|
||||
## Accomplishments
|
||||
- Created `lib/services/phishing-sweep-service.ts` exporting `sweepPhishingTickets()`: queries non-deleted tickets with `last_activity_date` in the last 7 days (bounded `LIMIT 500`), calls the shared `detectPhishingTicket` per row inside a try/catch (no rethrow — one bad ticket never aborts the sweep), and returns a `{ scanned, flagged, skippedUnchanged, errors }` aggregate, logged via `createSyncLogger`.
|
||||
- Wired `webhook-service.ts`: added a `triggerPhishingDetection` private method that builds a `DetectableTicket` from `payload.entity` (preferring the inline entity, falling back to `payload.entityId` with null fields), reading the correct `createdByContactID` Autotask field into `created_by_contact_id`. Called it as a second fire-and-forget alongside the existing `triggerWorkflowEngine` call in the `ticket.created` handler, without awaiting it in the request path.
|
||||
- Registered the sweep in `sync-scheduler.ts`: extended the `sync_type` union with `'phishing-sweep'`, added a `defaultSchedules` entry (`is_enabled: false`, daily `0 5 * * *` cron), and added a dispatch branch that dynamically imports and calls `sweepPhishingTickets`, logging the scanned/flagged/skippedUnchanged/errors summary.
|
||||
- Created `migrations/098_phishing_sweep_schedule.sql`, seeding the `phishing-sweep` row via `ON CONFLICT (id) DO NOTHING` (idempotent, disabled by default) for existing installs whose `sync_schedules` table predates this migration.
|
||||
|
||||
## Task Commits
|
||||
|
||||
1. **Task 1: phishing-sweep-service.ts** - `dbd2ebe` (feat)
|
||||
2. **Task 2: webhook hook-in** - `194b58b` (feat)
|
||||
3. **Task 3: scheduler branch + migration 098** - `b199d99` (feat)
|
||||
|
||||
**Plan metadata:** (this SUMMARY.md commit)
|
||||
|
||||
## Files Created/Modified
|
||||
- `lib/services/phishing-sweep-service.ts` - Bounded reconciliation sweep calling the shared `detectPhishingTicket` core (no duplicated match/hash logic)
|
||||
- `lib/services/webhook-service.ts` - Added `detectPhishingTicket` import, `triggerPhishingDetection` method, and a fire-and-forget call in the `ticket.created` handler alongside the existing workflow-engine trigger
|
||||
- `lib/services/sync-scheduler.ts` - Extended `sync_type` union, added `phishing-sweep` `defaultSchedules` entry, added a dispatch branch dynamically importing `sweepPhishingTickets`
|
||||
- `migrations/098_phishing_sweep_schedule.sql` - Seeds the `phishing-sweep` schedule row for existing installs (idempotent, disabled by default)
|
||||
|
||||
## Decisions Made
|
||||
- Mirrored `ticket-reconciliation-service.ts`'s structure for the sweep verbatim (module constants for window/limit, `createSyncLogger`, per-row try/catch with `logger.warn` and no rethrow) rather than introducing a different shape, since the plan explicitly called this out as the closest analog.
|
||||
- Used the exact `createdByContactID` field name confirmed by the plan's interface notes and `lib/utils/entity-mapper.ts:211`; verified via grep that no `creatorContactID` typo was introduced (a typo would compile cleanly since `AutotaskWebhookPayload.entity` is `Record<string, any>`, but would silently produce `undefined` at runtime).
|
||||
- Kept the `phishing-sweep` schedule disabled by default (`is_enabled: false`), matching the `tickets-reconcile` precedent, so the sweep does nothing until an admin explicitly enables it via `/admin`.
|
||||
|
||||
## Deviations from Plan
|
||||
None — plan executed exactly as written. All three tasks matched their `<action>` specs, and every acceptance criterion (grep checks + `npx tsc --noEmit --pretty`) passed on first attempt.
|
||||
|
||||
## Issues Encountered
|
||||
None.
|
||||
|
||||
## User Setup Required
|
||||
None for this plan. The `phishing-sweep` schedule is seeded disabled; an admin can enable it later at `/admin` once ready to run reconciliation sweeps in production. No new env vars or credentials introduced (reuses existing `AUTOTASK_*` config via the Plan 02 detector).
|
||||
|
||||
## Next Phase Readiness
|
||||
- Both DETECT-01 scan triggers (webhook + cron) are now wired to the same shared `detectPhishingTicket` core, so DETECT-02 idempotency holds identically on either path.
|
||||
- The `phishing-sweep` schedule exists in the `defaultSchedules` seed path (fresh installs) and migration 098 (existing installs), both idempotent and disabled by default.
|
||||
- No blockers for Phase 16 (message parsing) or Phase 17 (Mimecast blast-radius), which build on this detection/evidence foundation.
|
||||
|
||||
---
|
||||
*Phase: 15-data-model-detection-ticket-evidence*
|
||||
*Completed: 2026-07-15*
|
||||
Loading…
Add table
Add a link
Reference in a new issue