Commit graph

918 commits

Author SHA1 Message Date
cf04f07c58 feat(quick-260717-a19): add idempotency guard + retry-parse on ticket.update
- parseAndStoreMessage (Defect 3): short-circuit with
  { stored: false, reason: 'already-parsed' } when a messages row already
  exists for the report, before any Autotask attachment fetch
- webhook-service (Defect 2): new retryPhishingParseOnUpdate wired into
  ticket.update fire-and-forget path; retries the missing-EML parse for a
  flagged, unparsed, auto_parse-gated report — no new cron/polling, reuses
  existing update traffic, safe to fire repeatedly thanks to the new
  idempotency guard
- Adjust eml-service test mock default so the new leading existence-check
  query doesn't short-circuit existing happy-path tests; add new test for
  the already-parsed short-circuit
2026-07-17 07:20:48 -04:00
204276c88a feat(quick-260717-a19): add 3 confirmed KnowBe4 domains to sim allowlist
- Extend knowbe4 vendor entry with customer-portal.info,
  cloud-service-care.com, bankonlinesupport.com (confirmed via shared
  URL fingerprint across Seubert tickets 699419/699421/699422/699433/
  699435/699456 on 2026-07-16/17)
- domainMatchesAllowlist and isKnownSimulationSender untouched
- Add tests for exact match, subdomain match, and suffix-spoof rejection
2026-07-17 07:19:18 -04:00
075c4bbd24 chore: remove REQUIREMENTS.md for v3.0 milestone
Archived to .planning/milestones/v3.0-REQUIREMENTS.md. Fresh REQUIREMENTS.md
gets created when the next milestone's requirements are defined.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W6RuWdiUiXrPK6FLBHjtpY
2026-07-17 06:58:12 -04:00
e5d42bda88 docs: update retrospective for v3.0 2026-07-17 06:57:43 -04:00
e9101b0d44 chore: archive v3.0 milestone files 2026-07-17 06:56:49 -04:00
8b04be160f docs: fix stale requirement checkboxes (CLASSIFY-01..06, REVIEW-03), record milestone-close deferred items 2026-07-17 06:48:16 -04:00
8610e7ab9b docs(22): add first-ever phase verification (human_needed — code passes, 5 UAT checks outstanding) 2026-07-17 06:47:18 -04:00
369ae31dba docs(23): mark phase complete — milestone v3.0 finished 2026-07-16 23:34:15 -04:00
2e55e5e91d docs(23): re-verify phase — AUTOGATE-03 gap closed, 18/18 passed 2026-07-16 23:33:07 -04:00
6e8c78b8d2 fix(23-06): capture actionId in auto-post audit payload, guard manual re-approval of acknowledge_user
23-06-REVIEW.md found two real defects in the just-merged idempotency fix:
- CR-01: autoPostAcknowledgment's audit payload omitted actionId, which the
  campaign-detail API requires to derive completedAt — every auto-posted
  acknowledge_user row rendered a null completion date in the Action Area UI.
- CR-02: the manual approve/remediate path had no server-side guard against
  re-approving acknowledge_user for a campaign that already got auto-posted —
  only a client-side UI check prevented the exact duplicate-note bug 23-06
  was chartered to close, reachable via a direct API call.

Fixes both: capture RETURNING id from the insert and include it in the audit
payload; add an existence check in approveRemediationActions that rejects
acknowledge_user when already posted for the campaign.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W6RuWdiUiXrPK6FLBHjtpY
2026-07-16 23:29:11 -04:00
9dfc9162df docs(23-06): add code review report 2026-07-16 23:29:03 -04:00
f44cdd2910 docs(phase-23): update tracking after wave 3 2026-07-16 23:21:24 -04:00
273ac9cb89 chore: merge executor worktree (worktree-agent-a172feca473a48833) 2026-07-16 23:20:29 -04:00
605e994937 docs(23-06): append self-check results to SUMMARY
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W6RuWdiUiXrPK6FLBHjtpY
2026-07-16 23:20:04 -04:00
3bd116ad5b docs(23-06): complete idempotent auto-post acknowledgment plan
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W6RuWdiUiXrPK6FLBHjtpY
2026-07-16 23:19:49 -04:00
13bf851ab5 fix(23-06): wire auto_report webhook branch to autoPostAcknowledgment
Replace the unguarded generateAndPostAcknowledgment(campaignId) direct
call in runGatedPhishingStages' auto_report branch with the idempotent
autoPostAcknowledgment(campaignId, 'system:auto_report'). Closes CR-01
(23-REVIEW.md) / Truth #18 (23-VERIFICATION.md): a repeat ticket-create
webhook joining an already-acknowledged USER_AWARENESS campaign now
finds the persisted remediation_actions row and skips the re-post
instead of re-sending the customer-visible thank-you note on every
additional report.
2026-07-16 23:18:47 -04:00
c79af9b448 feat(23-06): add idempotent autoPostAcknowledgment to remediation-service
- New autoPostAcknowledgment(campaignId, actor) mirrors the VERIFIED
  remediateApprovedActions shape: campaign row lock, idempotency check
  against an existing acknowledge_user remediation_actions row, insert +
  audit inside one transaction, note post after commit (non-fatal on
  failure)
- Closes CR-01/WR-01: gives the auto_report webhook path a persisted
  record so a repeat ticket-create webhook joining an already-acked
  campaign does not re-insert/re-audit/re-post
- Adds Test A/B/C in remediation-service.test.ts covering first-pass
  insert, idempotent skip, and non-fatal note-post failure
2026-07-16 23:17:28 -04:00
a918b72d04 docs(23): gap-closure plan for AUTOGATE-03 idempotency defect (CR-01) 2026-07-16 22:54:19 -04:00
cc87607a58 docs(23): add phase verification report (gaps found) 2026-07-16 21:17:14 -04:00
c243cbc41f docs(23): add code review report 2026-07-16 21:11:48 -04:00
dadac5180d docs(phase-23): update tracking after wave 2 2026-07-16 21:04:14 -04:00
05a31d6a65 chore: merge executor worktree (worktree-agent-afdf88c6384b105cc) 2026-07-16 21:02:50 -04:00
032160cfe9 chore: merge executor worktree (worktree-agent-ab40944a8ae803901) 2026-07-16 21:02:50 -04:00
7e33a8568b docs(23-04): complete admin phishing-automation page plan
- Adds 23-04-SUMMARY.md documenting Task 1 (page) and Task 2 (index tile)
- Records that Task 1's human-verify checkpoint was approved via a
  temporary production container rebuild rather than local npm run dev
2026-07-16 21:02:00 -04:00
160fe88253 feat(23-04): add admin index tile for Phishing Automation
- Adds a Phishing Automation tile to the Tools & Data section of /admin
- Links to /admin/phishing-automation, imports Bot icon from lucide-react
2026-07-16 21:01:08 -04:00
7869e0bcfd docs(23-05): complete gated automation pipeline plan
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W6RuWdiUiXrPK6FLBHjtpY
2026-07-16 19:47:21 -04:00
5dabaad722 feat(23-04): build /admin/phishing-automation page with per-stage toggles
- Company table with search + type filter (cloned from client-scope pattern)
- Three independent Switch toggles per row: auto-parse, auto-classify, auto-report
- toggle() PATCHes /api/admin/phishing-automation/{companyId} with all three current flags
- Helper caption clarifies stage dependency (informational, not enforced)
2026-07-16 19:46:16 -04:00
e1193bf476 feat(23-05): wire gated parse->classify->acknowledge chain into webhook
- triggerPhishingDetection now captures groupReportIntoCampaign's result and,
  when a campaignId exists, calls new runGatedPhishingStages
- runGatedPhishingStages reads the per-company automation gate and
  conditionally runs parseAndStoreMessage, classifyCampaign, and (only for
  USER_AWARENESS verdicts) generateAndPostAcknowledgment
- each stage isolated in its own try/catch (T-23-09); detection + grouping
  remain unconditional (D-07); auto_report never posts any other action
  (D-04, T-23-08)
2026-07-16 19:46:15 -04:00
e0f22f27c9 feat(23-05): implement getCompanyAutomationGate reader
- COALESCE(..., false) query keyed on company_id; absent row/null/NaN -> all-false
- never throws; type-check and vitest suite pass
2026-07-16 19:45:41 -04:00
0d7974cdd9 test(23-05): add failing tests for getCompanyAutomationGate reader
- covers absent-row, present-row mapping, null/NaN companyId short-circuit
2026-07-16 19:45:04 -04:00
b69558aae8 docs(phase-23): update tracking after wave 1 2026-07-16 19:42:35 -04:00
9aadbcdeea chore: merge executor worktree (worktree-agent-a5d0fe500dc50fe1e) 2026-07-16 19:40:47 -04:00
d9d8962ca7 chore: merge executor worktree (worktree-agent-a1093d44311d63302) 2026-07-16 19:40:47 -04:00
9f29424264 chore: merge executor worktree (worktree-agent-a099928868240d5fb) 2026-07-16 19:40:47 -04:00
fe2375e3a2 docs(23-01): complete classification disposition + acknowledgment plan
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W6RuWdiUiXrPK6FLBHjtpY
2026-07-16 19:40:21 -04:00
6224e44219 feat(23-01): wire acknowledge_user manual-path real note post into remediateApprovedActions
- remediateApprovedActions now captures the transaction's RemediateResult,
  then post-commit checks whether an approved acknowledge_user row was
  transitioned this pass (alreadyCompleted === false) and, if so, calls
  generateAndPostAcknowledgment(campaignId) exactly once
- Call happens outside the DB transaction (network I/O hazard) and is
  wrapped in its own try/catch that logs and swallows failures -- the DB
  transition has already committed
- Every other action type (block_sender, purge_message, warn_user,
  reset_password, isolate_endpoint, disable_forwarding_rule, quarantine)
  remains a simulated status-only transition, unchanged
- Updated top-of-file D-01 doc comment to record the narrow D-04 carve-out
- Tests: acknowledge_user IS posted once when remediated, NOT called for
  block_sender/warn_user-only remediation, NOT called on idempotent re-run
  of an already-completed acknowledge_user row, and a post rejection does
  not propagate out of remediateApprovedActions
2026-07-16 19:39:13 -04:00
42a6c05d6f docs(23-03): append self-check results to SUMMARY.md
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-16 19:38:43 -04:00
63d6e85f37 docs(23-03): complete migration 100 + phishing-automation API plan
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-16 19:38:29 -04:00
50e241592c feat(23-01): add generateAndPostAcknowledgment customer-visible note writer
- New generateAndPostAcknowledgment(campaignId) posts a short, appreciative
  thank-you note to every ticket linked to a campaign, using noteType 18
  (Client Portal Note, verified live against tenant's TicketNotes field
  metadata) so the note is customer-visible; publish stays 1 unchanged
- Body is a fixed template with zero evidence/URL/classification
  interpolation (T-23-01) -- not the evidence-dump formatTriageNote() template
- Mirrors generateAndPostTriageNote's per-ticket try/catch-in-loop error
  isolation and { noteText, tickets } return shape
- Tests: noteType 18 + publish 1 payload assertion, per-ticket failure
  isolation, and zero-linked-reports case
2026-07-16 19:37:56 -04:00
78b6b49c5b docs(23-03): backfill Phase 23 CLASSDISP-*/AUTOGATE-* requirement entries
- Adds Classification Disposition + Automation Gate subsection under v1
  Requirements (Phase 23 is active v3.0 roadmap work, not deferred)
- Adds 6 Traceability rows (CLASSDISP-01/02/03, AUTOGATE-01/02/03 -> Phase 23, Pending)
- Updates Coverage footer counts (32 -> 38 total)
- No existing v1/v2 entries or rows altered
2026-07-16 19:37:45 -04:00
50d0def2ae docs(23-02): append self-check result to SUMMARY
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W6RuWdiUiXrPK6FLBHjtpY
2026-07-16 19:37:07 -04:00
ea5047c80a feat(23-03): add admin phishing-automation GET/PATCH/DELETE routes
- GET /api/admin/phishing-automation: admin-gated list with COALESCE(...,false) gate defaults
- PATCH /api/admin/phishing-automation/[companyId]: upserts all three flags, actor+timestamp stamped
- DELETE /api/admin/phishing-automation/[companyId]: reverts company to all-OFF default
- Mirrors app/api/admin/company-scope/* route pattern
2026-07-16 19:37:06 -04:00
14ed8ca248 feat(23-01): add USER_AWARENESS verdict + acknowledge_user action mapping
- Add USER_AWARENESS to the Verdict union in campaign-classifier.ts
- mapVerdictToActions('USER_AWARENESS') returns ['acknowledge_user']; not added to DESTRUCTIVE_ACTIONS so requires_approval computes false
- classifyCampaign's simulation branch now assigns verdict = 'USER_AWARENESS' directly instead of falling through to evaluateSpamVsUnwanted
- deriveDefaultParams('acknowledge_user') returns {} (no operator-editable params)
- Widen TriageNoteEvidence.verdict to admit 'USER_AWARENESS' (pure type widen, no formatting change)
- Tests: classifier simulation fixtures now assert USER_AWARENESS/acknowledge_user/requiresApproval=false; new mapVerdictToActions/computeRequiresApproval/deriveDefaultParams cases
2026-07-16 19:36:53 -04:00
44bbe90ae0 docs(23-02): complete Review UI USER_AWARENESS + acknowledge_user support plan
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W6RuWdiUiXrPK6FLBHjtpY
2026-07-16 19:36:53 -04:00
db7d67cf25 feat(23-03): add phishing_automation_gate migration 100
- Opt-in per-company table (auto_parse/auto_classify/auto_report, all default false)
- PK company_id -> companies(id) ON DELETE CASCADE, updated_by/updated_at audit cols
- Applied to running dev Postgres (existing volume, not auto-run on init)
2026-07-16 19:36:14 -04:00
9ae834f5e6 feat(23-02): add USER_AWARENESS support to TimelineCard, prevent review-page crash
- Add USER_AWARENESS to classification-entry verdict union and VERDICT_TINT (emerald, matching ClassificationCard)
- Widen campaign_classified audit-case cast to include USER_AWARENESS
- Add defensive fallback on both .split(' ') tint sites so any unrecognized runtime verdict string can never crash the render (T-23-12) — phishing-timeline.ts emits verdict as an unvalidated plain string
2026-07-16 19:36:11 -04:00
547372e57a feat(23-02): add acknowledge_user manual action case to ActionAreaCard
- Add acknowledge_user: 'Acknowledge user' to ACTION_LABEL
- Add case 'acknowledge_user' to ActionParamsForm mirroring no_action (no-params form)
- Renders as a normal checkbox + Approve action for every company; no automation-gate logic added (D-04) — the auto-approval carve-out lives only in the webhook path (Plan 05)
2026-07-16 19:35:38 -04:00
af8a5bd438 feat(23-02): add USER_AWARENESS badge variant + acknowledge_user label to ClassificationCard
- Add USER_AWARENESS to ClassificationCardData verdict union
- Add emerald VERDICT_VARIANT_CLASS entry for USER_AWARENESS (distinct from UNWANTED amber and THREAT destructive)
- Add acknowledge_user: 'Acknowledge user' to ACTION_LABEL
2026-07-16 19:35:22 -04:00
7048bf693a docs(23): create phase plan 2026-07-16 19:31:58 -04:00
2f48dba6be docs(23): fix REQUIREMENTS.md section placement instruction in plan 23-03
Checker warning: creating a new '## v3 Requirements' top-level section would
misleadingly read as deferred/future work (that's what '## v2 Requirements'
means in this file). Phase 23 is active v3.0 roadmap work -- instruct adding
a subsection under the existing '## v1 Requirements' heading instead,
mirroring Phase 22's existing subsection.
2026-07-16 19:30:49 -04:00