Commit graph

928 commits

Author SHA1 Message Date
0acf1fa24f test(24-02): cover buildDriftHistoryRows update/delete/create/no-change cases
- 6 unit tests exercising the pure drift-classification wiring in Route53SyncService
- Confirms whole-recordset before/after payloads, not per-field deltas
- Confirms CRUD-originated changes still get tagged sync_detected_drift (sync cannot distinguish actor)
- Full suite green aside from 2 pre-existing itglue-search.test.ts failures logged in deferred-items.md (unrelated to this plan)
2026-08-05 20:23:16 -04:00
d8c0912f4b feat(24-02): add Route53SyncService — zones and records mirror sync
- fullSync/incrementalSync + getRoute53SyncService() singleton
- Paginated ListHostedZonesCommand + GetHostedZoneCommand (delegation set for D-12)
- Paginated ListResourceRecordSetsCommand per live zone
- Soft-delete reconciliation for zones and records (never hard-delete)
- sync_history bookkeeping with entity_type='route53', literal full/incremental sync_type
- Drift detection wired via buildDriftHistoryRows, writing sync_detected_drift history rows
- No integration_settings gating anywhere (D-10)
2026-08-05 20:22:34 -04:00
c18271dda9 test(24-02): add Route 53 record-key, normalization, and drift-classification helpers
- buildRecordKey, normalizeRecordSet, recordSetsEqual, classifyDrift, toHistoryPayload
- Pure, dependency-free module (no pg, no AWS client construction)
- 16 unit tests covering every behavior bullet from the plan
2026-08-05 20:21:05 -04:00
ecd4dabd67 docs(phase-24): update tracking after wave 1 (plan 24-01 complete)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-05 20:17:42 -04:00
2eb6c2884c chore: merge executor worktree (worktree-agent-aa690b9b15c5b0f8e) — plan 24-01 2026-08-05 20:16:56 -04:00
b81ad3ecc2 docs(24-01): resolve checkpoint — BWS key rename confirmed, DNS egress OK
Checkpoint task 3 resolved: BWS secret keys renamed in Bitwarden
(AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY confirmed present), DNS egress
to public resolvers confirmed OK, credentials confirmed reaching the
Node process. IAM scope left as an open operational item for the
developer to confirm via AWS console.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-05 20:16:45 -04:00
97ec5722b1 docs(24-01): create partial SUMMARY, checkpoint pending
Tasks 1-2 complete (AWS SDK + schema migration, types + credential factory).
Task 3 is a blocking human-verify checkpoint requiring BWS/AWS credential
confirmation and live docker/DNS-egress verification — not fabricated, not
run unilaterally. Execution stops here pending developer response.
2026-08-05 19:26:46 -04:00
fadae68fe4 docs(24-01): log pre-existing itglue-search test failures as out-of-scope
Unrelated to Route 53 factory/schema work; not fixed per scope boundary rule.
2026-08-05 19:25:39 -04:00
210f84d343 feat(24-01): implement Route 53 credential factory
- lib/services/route53-factory.ts: isRoute53Configured() / getRoute53Client()
  / resetRoute53Client(), following the veeam-factory.ts singleton shape
- No explicit credentials option passed to Route53Client — relies on the AWS
  SDK's default credential chain reading AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY
  from process.env, exactly how BWS injects them at the container entrypoint
- CLAUDE.md: document the AWS_* env-prefix exception in the integration table
- All 7 route53-factory.test.ts assertions pass; npx tsc --noEmit clean
2026-08-05 19:25:36 -04:00
4dd9d5dab8 test(24-01): add failing test for Route 53 credential factory
- lib/types/route53.ts: camelCase interfaces for zones/records/history/audit-log/sync-result
- lib/services/route53-factory.test.ts: isRoute53Configured() + getRoute53Client() behavior
  cases — fails RED, factory module does not exist yet
2026-08-05 19:24:28 -04:00
b9df27b656 feat(24-01): install AWS Route 53 SDK and create dedicated schema migration
- Add @aws-sdk/client-route-53 dependency (official aws-sdk-js-v3 package)
- Add migrations/102_route53_tables.sql: route53_zones, route53_records,
  route53_record_history (D-06 change ledger), route53_audit_log
  (D-03/D-07 attempt audit log with pending/committed/failed status)
- Seed integration_settings row for key='route53' (D-10, display-only toggle)
- Unbounded retention by design (D-08) — no purge job, no TTL, no DELETE
2026-08-05 19:23:43 -04:00
602c3bb7b5 docs(24): create phase plan 2026-08-05 19:18:13 -04:00
0eab4996e9 docs(24): add pattern map 2026-08-05 19:18:00 -04:00
998c4b1a36 docs(24): research phase domain for AWS Route 53 DNS sync 2026-08-05 19:17:59 -04:00
52affe348c docs(24): cite D-02/D-05/D-08/D-09 in plan must_haves 2026-08-05 19:17:35 -04:00
15e52280b5 docs(24): create phase plan — 7 plans in 4 waves for AWS Route 53 DNS sync 2026-08-05 19:09:15 -04:00
6ad27fcbb6 docs(phase-24): add validation strategy 2026-08-05 18:46:43 -04:00
e289f2d24f docs(state): record phase 24 context session 2026-08-05 18:31:11 -04:00
0805e387e3 docs(24): capture phase context 2026-08-05 18:31:06 -04:00
5f308f836f fix(phishing-recipient-seubert): scope campaign grouping Tier 3 to company, not reporting contact
Tier 3 is the only tier automatic (webhook-triggered) grouping ever
reaches, since grouping runs before message parsing. It was scoped to
reports.requester_contact_id, so the same campaign reported by
different employees at the same company never consolidated into one
campaign — each report's evidence/blast-radius view silently
under-reported the campaign's true recipients.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-22 12:34:33 -04:00
e9478101a3 docs(quick-260721-n49): fix classifier to resolve per-company Mimecast tenant instead of the global tenant 2026-07-21 16:48:11 -04:00
1cd0d4db48 chore: merge quick task worktree (worktree-agent-a498794d9a221b8b7) 2026-07-21 16:47:36 -04:00
9f12cd610a test(260721-n49): cover tenant-scoped vs global-fallback getBlastRadius branches
- Add mimecast_tenants routing branch to stageQueries and company_id to
  report fixtures
- New test: enabled mimecast_tenants row -> getBlastRadius called with
  { client, cacheScope: companyId } and the tenant SQL is issued
- New parameterized test: null companyId and companyId-with-no-enabled-row
  -> getBlastRadius called with no second argument (global fallback)
2026-07-21 16:45:44 -04:00
9f75f2160c fix(260721-n49): resolve per-company Mimecast tenant in gatherCampaignEvidence
- Thread company_id through reports query and CampaignReportSummary
- Mirror the route's Bug 2 (D-05) tenant-resolution block: query
  mimecast_tenants for an enabled row, build a tenant-scoped client via
  getMimecastClientForTenant, and pass { client, cacheScope } to
  getBlastRadius() when one exists
- Preserve global env fallback unchanged when no companyId or no
  enabled tenant row is present
2026-07-21 16:44:44 -04:00
f58856e103 docs(260721-n49): pre-dispatch plan for fix classifier Mimecast tenant resolution 2026-07-21 16:41:14 -04:00
3e9c7633d1 docs(quick-260721-mmf): fix Mimecast blast-radius query so it reflects the true blast radius across all recipients, not just the single reporter 2026-07-21 16:26:57 -04:00
534eda3c40 test(260721-mmf): assert tenant-wide fan-out and multi-recipient blast radius
- Updated the getHeldMessages window test to assert no `recipient` key and
  added an explicit no-`to` assertion for searchDeliveredMessages
- Added a new test driving searchDeliveredMessages with 3 distinct `to`
  addresses, asserting all appear in perRecipient and count toward
  matched/delivered
2026-07-21 16:25:39 -04:00
58202e0dee fix(260721-mmf): broaden Mimecast blast-radius fan-out to whole tenant
- searchDeliveredMessages now called with from+subject+start+end only
  (no `to`) so it returns every delivered/rejected message matching the
  campaign across all recipients, not just the reporter's mailbox
- getHeldMessages now called with start+end only (no `recipient`) —
  domainsMatch() post-filter is the sole scoping mechanism for held rows
- Updated inline comments to document the tenant-wide fan-out and the
  per-recipient merge behavior it now produces
2026-07-21 16:25:08 -04:00
f50b8a3a9b docs(260721-mmf): pre-dispatch plan for fix Mimecast blast-radius query scope 2026-07-21 16:19:31 -04:00
6eafaae40a docs(quick-260721-fy8): fix mimecast and qbo sync scheduler dispatch and reschedule mimecast cron
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 11:39:52 -04:00
db7db98374 chore: merge quick task worktree (worktree-agent-aed3ab3e5ca2a6fb2) 2026-07-21 11:38:55 -04:00
f447ac33c0 fix(quick-260721-fy8): reschedule mimecast-sync off the 2am cron collision
- Add migration 101: guarded UPDATE moving mimecast-sync from 0 2 * * * to
  45 4 * * * (minute 45 is unused by any other schedule row)
- Guarded on the stale cron value so it's a no-op if already moved and won't
  clobber an admin's manual schedule change
- Applied the same UPDATE directly to the live pulse-postgres container
  (migrations only auto-apply on first volume boot, per CLAUDE.md)
- Eliminates the 3-way 2 AM collision with qbo-sync-2am and veeam-full
2026-07-21 11:36:56 -04:00
9311f1044b fix(quick-260721-fy8): add mimecast-sync and qbo dispatch branches to scheduler
- Extend ScheduleConfig.sync_type union with 'mimecast-sync'
- Add mimecast-sync branch calling runMimecastIncrementalSync() behind
  isMimecastConfigured(), mirroring the engagement/zoom configured-gate pattern
- Add qbo branch calling getQboSyncService().incrementalSync('scheduled')
  behind an integration_settings disabled check, mirroring the pax8-daily
  disable-check pattern
- Both branches previously fell through to the generic Autotask fullSync()
  catch-all, which also contended for the SyncService singleton mutex
2026-07-21 11:36:16 -04:00
d41d6e7c0b docs(260721-fy8): pre-dispatch plan for fix mimecast and qbo sync scheduler dispatch and reschedule mimecast cron 2026-07-21 11:33:33 -04:00
a0d01cfe8b docs(v3.0): add LLM-context companion doc for phishing triage feature 2026-07-18 11:22:09 -04:00
f0c09927f4 docs(v3.0): generate milestone summary for onboarding 2026-07-18 11:20:31 -04:00
a65b29055c docs(quick-260718-9qg): complete QBO integration handoff quick task
Records the plan, execution summary, and STATE.md quick-task log entry
for the QBO_INTEGRATION_HANDOFF.md doc committed in ea8a36b.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W6RuWdiUiXrPK6FLBHjtpY
2026-07-18 07:08:29 -04:00
ea8a36b394 docs(quick-260718-9qg): add self-contained QBO integration handoff document
Documents Pulse's QuickBooks Online integration (OAuth2 authorization-code
flow, token storage/refresh, sandbox vs production API base URLs, scopes,
minor version, and gotchas learned from the AR reconciliation/soft-delete
work) so a new app's team can build their own QBO connection without
access to the Pulse codebase.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W6RuWdiUiXrPK6FLBHjtpY
2026-07-18 07:07:26 -04:00
672f17b7f9 chore: check in pending work — queue preferences, QBO AR diagnostics, mobile engagement fixes, ops scripts
Bundles several in-progress efforts that were sitting uncommitted:
- User queue-preferences (migration 087, API route, popover component)
- QBO invoice soft-delete (migration 088) and AR diagnostics route
- Dashboard/mobile engagement route and page adjustments
- Docker Compose log-rotation config
- One-off ticket/RMM investigation scripts (scripts/)
- Planning docs: phase verification/pattern notes, mobile shell design spec
- .gitignore: exclude local scratch financial/inventory data and Claude Code
  worktree/local-settings runtime state (never meant for version control)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W6RuWdiUiXrPK6FLBHjtpY
2026-07-18 06:34:57 -04:00
b638189cb0 docs: document get-hold-message-list endpoint and its held-queue filtering gotcha 2026-07-18 06:10:59 -04:00
5b09dbff96 fix: tighten Headers tab grid layout and wrap long values instead of overflowing the card 2026-07-18 06:04:37 -04:00
a30d72e9ba docs(quick-260718-7v8): Fix Mimecast blast-radius held-message false-positive fix 2026-07-18 05:48:23 -04:00
b7d6be47c6 chore: merge quick task worktree (worktree-agent-a45f7294477ed0d95) 2026-07-18 05:47:50 -04:00
67ee680105 test(quick-260718-7v8): cover held-message date-scoping and sender-relevance guard
- blast-radius: getHeldMessages called with same start/end window as
  searchDeliveredMessages
- blast-radius: unrelated-sender held row excluded from held/matched and
  perRecipient; matching-sender held row still counts and overrides
- client: getHeldMessages threads start/end into POST body data[0] when
  provided, omits them when not
2026-07-18 05:46:22 -04:00
94f7dad29c fix(quick-260718-7v8): date-scope held-message lookup + sender-relevance guard
- getHeldMessages() accepts optional start/end, threaded into data[0] as
  siblings of admin/searchBy (backward compatible when omitted; 403
  fallback body inherits them automatically via the existing spread)
- getBlastRadius() passes the same startStr/endStr window already
  computed for searchDeliveredMessages into getHeldMessages()
- Added domainsMatch() sender-relevance guard: held rows whose sender
  domain doesn't match input.sender (exact-or-proper-subdomain) are
  filtered out before counting/merging, so unrelated same-window holds
  never inflate held/matched or override a delivered recipient
2026-07-18 05:44:56 -04:00
36f4d418d0 docs(260718-7v8): pre-dispatch plan for Mimecast blast-radius held-message fix 2026-07-18 05:42:27 -04:00
4a77b0544c docs(quick-260717-v6c): Add Mark as accidental report action to phishing Action Area 2026-07-17 22:37:25 -04:00
565a0c1ee2 chore: merge quick task worktree (worktree-agent-a3ab410880ddc437a) 2026-07-17 22:36:29 -04:00
0d6cd25008 test(260717-v6c): mirror test coverage for accidental-report service functions
- remediation-service.test.ts: D-04 guard rejection, successful status flip
  + audit event, note-post-failure-still-commits path for
  markCampaignAccidentalReport
- triage-note-service.test.ts: noteType 18/publish 1 posting, per-ticket
  isolation, zero-reports path for generateAndPostAccidentalReportNote
2026-07-17 22:35:04 -04:00
74e43e23c4 feat(260717-v6c): add Mark as accidental report button, dialog, and timeline case
- ActionAreaCard: new GatedButton + confirm AlertDialog (optional reason),
  resolved/tooltip logic now covers accidental_report status, toast
  distinguishes full success from note-post failure
- TimelineCard: campaign_marked_accidental_report entry uses the
  blue/CheckCircle2 tint (distinct from slate/XCircle false-positive)
2026-07-17 22:34:09 -04:00