From 98c7153a10af9b8159c3c4508f078e582b40687d Mon Sep 17 00:00:00 2001 From: lorentz Date: Thu, 16 Jul 2026 18:37:04 -0400 Subject: [PATCH] docs(23): capture phase context --- .../23-CONTEXT.md | 246 ++++++++++++++++++ .../23-DISCUSSION-LOG.md | 98 +++++++ 2 files changed, 344 insertions(+) create mode 100644 .planning/phases/23-classification-disposition-per-client-automation-gate/23-CONTEXT.md create mode 100644 .planning/phases/23-classification-disposition-per-client-automation-gate/23-DISCUSSION-LOG.md diff --git a/.planning/phases/23-classification-disposition-per-client-automation-gate/23-CONTEXT.md b/.planning/phases/23-classification-disposition-per-client-automation-gate/23-CONTEXT.md new file mode 100644 index 0000000..52a81ac --- /dev/null +++ b/.planning/phases/23-classification-disposition-per-client-automation-gate/23-CONTEXT.md @@ -0,0 +1,246 @@ +# Phase 23: Classification Disposition + Per-Client Automation Gate - Context + +**Gathered:** 2026-07-16 +**Status:** Ready for planning + + +## Phase Boundary + +Two related capabilities in the phishing triage pipeline: + +1. **A dedicated "confirmed simulated phish" verdict.** The classifier + (`campaign-classifier.ts`) already detects known phishing-simulation + vendors (KnowBe4/Breach Secure Now) via sender-domain allowlist and + explicitly skips the THREAT tier for them — but the result still lands in + the generic `UNWANTED` bucket (the same disposition as run-of-the-mill + marketing spam), with the recommended action `warn_user`. This phase adds + a distinct 4th verdict with a new, distinct recommended action. + +2. **A per-client automation gate.** With the Autotask webhook now fixed + (delivering ticket create/update events again after ~3 months dead), the + pipeline's parse/classify/report stages *could* run automatically per + report. Today they're always manual (an explicit design limitation from + Phase 18 — `parseAndStoreMessage` and `classifyCampaign` are never called + by the automatic webhook/cron path). This phase adds an admin page letting + an admin choose, per Autotask company, which of these stages run + automatically instead of requiring the existing manual triggers. + +Does NOT cover: changing the classifier's detection logic itself (the +KnowBe4/BSN sender-domain allowlist already exists and works — this phase +only changes what verdict/action it maps to), changing Phase 20's remediation +approval model for real threats (quarantine/block/etc. always require manual +approval, regardless of this phase's automation gate), or re-litigating the +webhook fix (already shipped as quick task 260716-n46 in this same session). + + + + +## Implementation Decisions + +### New Verdict: User Acknowledgment +- **D-01:** Add a 4th `Verdict` value (working name `USER_AWARENESS` — exact + string is Claude's discretion, pick something that reads clearly in + `StatusBadge`/reporting) for campaigns where the classifier's own reasoning + already shows a phishing-simulation-vendor sender-domain match. This is + additive to the existing `SPAM | UNWANTED | THREAT` union, not a + replacement. +- **D-02:** This verdict maps to a NEW recommended action, `acknowledge_user` + (not `warn_user`) — a professional message thanking the employee for + reporting it and reinforcing that their vigilance helps keep the company + secure. Exact copy is Claude's discretion; tone should be genuinely + appreciative, not templated-sounding. +- **D-03:** `acknowledge_user` delivers as a **customer-visible (Publish=true) + Autotask ticket note** — not an internal-only note like Phase 21's existing + triage-note, and not a separate email. Reuses the existing `TicketNotes` + write path from `triage-note-service.ts`, with the visibility flag flipped + for this specific action type so the reporting employee actually sees it + (via their normal ticket notification/portal). +- **D-04:** `acknowledge_user` is exempt from Phase 20's proposed-only / + manual-approval model — for companies with the automation gate's + "report-to-ticket" stage enabled, it posts automatically without an + operator clicking Approve first. This is a deliberate, narrow carve-out: + it applies ONLY to `acknowledge_user` (a thank-you message, not a security + action). Every other recommended action (quarantine, block sender, + warn_user, etc.) always requires manual approval regardless of the + automation gate, unchanged from Phase 20. + +### Automation Gate: Granularity and Defaults +- **D-05:** Three independent per-company toggles, not one master switch: + **parse**, **classify**, **report-to-ticket**. A company can, e.g., have + auto-parse+classify enabled but auto-report disabled (an operator reviews + before anything reaches the client's ticket). +- **D-06:** Default state for a company with no explicit row is **all three + OFF** (opt-in, not opt-out). A client only gets automatic pipeline + execution once someone deliberately enables it for them — mirrors Phase + 20's "proposed-only by default" safety posture. This also means: even with + the webhook now delivering ticket-create events for every company, nothing + changes in current pipeline behavior until an admin opts a company in via + this new page. +- **D-07 (derived, not separately asked):** Detection + Tier-3 grouping + (already automatic via the webhook, unconditionally, for every company — + fixed in quick task 260716-n46) are UNAFFECTED by this gate. The gate only + ever controls the three previously-manual-only stages (parse, classify, + report). Detection/grouping are non-destructive (just flag + bucket into a + campaign) and were never gated behind per-company consent even before this + phase — no reason to add friction there. + +### Automation Gate: Admin Surface +- **D-08:** New dedicated page at `/admin/phishing-automation`, `admin` role + (not super-admin-restricted) — consistent with other `/admin/*` pages. + Structurally mirrors the existing `/admin/client-scope` page: a searchable/ + filterable company table (reuse `components/admin/DataTable.tsx`), one row + per company, with the three stage toggles as `Switch` components per row + (see Reusable Assets below for the exact analog). + +### Claude's Discretion +- Exact new verdict string value (`USER_AWARENESS` vs. alternatives) and its + `StatusBadge` color/variant. +- Exact `acknowledge_user` message copy (tone direction given: professional, + genuinely appreciative — see D-02). +- Schema shape for the automation-gate settings (new table keyed by + `company_id` with three boolean columns vs. some other normalization) — + whatever is cleanest given `company_scope`'s existing precedent (migration + 082: absent row = default, explicit row = override). +- Whether the automatic parse/classify/report pipeline runs synchronously + inline in the webhook handler (mirroring today's automatic detect+group + call in `triggerPhishingDetection()`) or is queued for a worker to pick up + — an implementation detail, not a user-facing behavior choice, as long as + it eventually runs without a human clicking a button. +- Audit/logging of automation-gate toggle changes (who/when) — reasonable to + add given `integration_settings`' existing `disabled_by`/`disabled_at` + pattern, but not something the user was asked about explicitly. + + + + +## Canonical References + +**Downstream agents MUST read these before planning or implementing.** + +### Existing pipeline stages this phase gates +- `lib/services/campaign-classifier.ts` — `Verdict` type (`SPAM | UNWANTED | + THREAT`), `mapVerdictToActions()`, and the existing KnowBe4/Breach Secure + Now sender-domain-allowlist detection that already skips the THREAT tier + (search for "phishing-simulation vendor allowlist" in its reasoning + strings) — this phase changes what verdict/action that detection maps to, + not the detection itself. +- `lib/services/phishing-eml-service.ts` — `parseAndStoreMessage()`, the + "parse" stage. +- `lib/services/campaign-grouping-service.ts` — module doc-comment's + documented D-07 limitation: `parseAndStoreMessage` and classification are + "not wired into the automatic webhook/cron path" — this phase is what + wires them in, per-company, behind the new gate. +- `lib/services/webhook-service.ts` — `triggerPhishingDetection()`, the + existing automatic detect+Tier-3-group call fired on every `Ticket` webhook + create event (fixed this session — see quick task + `.planning/quick/260716-n46-fix-mimecast-blast-radius-date-window-fu/`). + The new automatic parse/classify/report calls should follow this same + call site's shape (read the ticket row back from Postgres, run the next + stage, never block the webhook response). +- `lib/services/triage-note-service.ts` — `generateAndPostTriageNote()`, the + existing internal-note writer this phase's `acknowledge_user` action reuses + with a flipped visibility flag; also the "report-to-ticket" automation + stage's existing manual equivalent. +- `app/api/phishing/campaigns/[id]/classify/route.ts` — the only existing + trigger of `classifyCampaign`; the new automatic "classify" stage should + call the same underlying service function this route calls, not duplicate + classification logic. + +### Automation-gate UI/API precedent +- `app/admin/client-scope/page.tsx` — the exact UI pattern to mirror: company + table with search/type-filter + `Switch` per row, backed by + `app/api/admin/company-scope/route.ts`. +- `migrations/082_company_scope.sql` — the opt-out-table schema precedent + (`company_id` + boolean, absent row = default true, explicit row = + override) to adapt for this phase's opt-IN, three-boolean shape. +- `migrations/081_*.sql` (`integration_settings` table) — the + `disabled_by`/`disabled_at`/`disabled_reason` audit-column precedent, if + audit logging is added per Claude's Discretion above. + +### Phase 20 safety model this phase narrowly carves out of +- `.planning/phases/20-remediation-approval-audit-safety/` (SUMMARY/PLAN + files) — the "proposed-only, human-approved" remediation model. + `acknowledge_user` is the ONLY action exempted from manual approval by this + phase (D-04); every other action type is unaffected. + +### Prior phase context carried forward +- `.planning/phases/22-approval-ui-livelink-addressable-campaign-review-and-approve/22-CONTEXT.md` + — D-06's `hasPermission()` client-side gating pattern (reuse for the new + admin page's action buttons if any client-side gating is needed beyond the + page-level `admin` role check). + + + + +## Existing Code Insights + +### Reusable Assets +- `app/admin/client-scope/page.tsx` + `app/api/admin/company-scope/route.ts` + — direct structural template for the new automation-gate page (table + + search/filter + per-row toggle(s), just 3 Switches instead of 1). +- `components/admin/DataTable.tsx` — table wrapper already used by + `client-scope` and other admin pages. +- `lib/services/triage-note-service.ts` — reuse its `TicketNotes` write call + shape for `acknowledge_user`, just with the publish/visibility flag + flipped. +- `lib/permissions.ts` / `requirePermission()` — existing admin-role gate + pattern for the new page's API routes. + +### Established Patterns +- Opt-in/opt-out settings-table pattern already exists twice + (`company_scope` for analytics, `integration_settings` for integration + toggles) — this phase's automation-gate table is a third instance of the + same pattern, just with 3 booleans instead of 1 and opt-in instead of + opt-out defaults. +- "Absent row = default behavior, explicit row = override" is the + established convention for these settings tables — follow it rather than + requiring a row to exist for every company up front. + +### Integration Points +- New automatic parse/classify/report calls plug into + `webhookService`'s existing `triggerPhishingDetection()` call site (or a + sibling method called from the same place) — after detection+grouping + succeeds, check the grouped campaign's company against the new gate table + before calling `parseAndStoreMessage`/`classifyCampaign`/ + `generateAndPostTriageNote`. +- `ClassificationCard` (Phase 22) needs to render the new verdict with its + own `StatusBadge` variant/color, distinct from `UNWANTED`'s existing + styling. +- `ActionAreaCard` (Phase 22) needs to handle `acknowledge_user` as a + recommended-action checkbox like today's other action types, for the + manual path (companies without report-to-ticket automation enabled still + see it as a normal proposed action an operator approves). + + + + +## Specific Ideas + +- The `acknowledge_user` message should read as a genuine thank-you, not a + templated compliance notice — explicit framing from discussion: "a + professional message letting them know we appreciate their attention in + keeping the company secure." +- This phase's motivating example: ticket 699415 (Seubert & Associates, + "Phishing Report - direccion@it-support.care - Attention Please"), + correctly classified `UNWANTED` with the BSN-allowlist reasoning already + present, confidence display bug already fixed separately (quick task + 260716-pgr) — but the disposition itself is what this phase corrects. + + + + +## Deferred Ideas + +None — discussion stayed within phase scope. (The webhook fix and the +confidence display bug that surfaced during the same investigation were +already resolved as separate quick tasks, not folded into this phase.) + +### Reviewed Todos (not folded) +None — no pending todos matched this phase. + + + +--- + +*Phase: 23-classification-disposition-per-client-automation-gate* +*Context gathered: 2026-07-16* diff --git a/.planning/phases/23-classification-disposition-per-client-automation-gate/23-DISCUSSION-LOG.md b/.planning/phases/23-classification-disposition-per-client-automation-gate/23-DISCUSSION-LOG.md new file mode 100644 index 0000000..7cca88c --- /dev/null +++ b/.planning/phases/23-classification-disposition-per-client-automation-gate/23-DISCUSSION-LOG.md @@ -0,0 +1,98 @@ +# Phase 23: Classification Disposition + Per-Client Automation Gate - Discussion Log + +> **Audit trail only.** Do not use as input to planning, research, or execution agents. +> Decisions are captured in CONTEXT.md — this log preserves the alternatives considered. + +**Date:** 2026-07-16 +**Phase:** 23-classification-disposition-per-client-automation-gate +**Areas discussed:** Gate granularity, Default state, Verdict behavior, Gate page location, Acknowledge-user delivery path, Auto-approval carve-out + +--- + +## Gate granularity + +| Option | Description | Selected | +|--------|-------------|----------| +| One master toggle per company | Single on/off switch controlling parse+classify+report together | | +| Separate toggles per stage | Three independent switches (parse / classify / report-to-ticket) per company | ✓ | + +**User's choice:** Separate toggles per stage. +**Notes:** More flexible than a single switch — a company can have auto-parse+classify without auto-report, keeping a human review point before anything reaches the client's ticket. + +--- + +## Default state + +| Option | Description | Selected | +|--------|-------------|----------| +| Off by default, opt-in per client | A client only gets automation once explicitly enabled | ✓ | +| On by default, opt-out per client | Every client automated immediately unless explicitly disabled | | + +**User's choice:** Off by default, opt-in per client. +**Notes:** Matches Phase 20's "proposed-only" safety posture — nothing client-facing happens automatically without deliberate enablement. + +--- + +## Verdict behavior + +| Option | Description | Selected | +|--------|-------------|----------| +| New 4th verdict, same warn_user action | New disposition label, unchanged downstream action | | +| New verdict + no action needed | New disposition, empty/no-op recommended action | | +| New 4th verdict, new acknowledge_user action | New disposition + new action: professional thank-you message | ✓ (user's own answer, not a listed option) | + +**User's choice:** New 4th verdict with a new `acknowledge_user` recommended action — "a professional message letting them know we appreciate their attention in keeping the company secure." +**Notes:** Neither preset option captured this — the user wanted active positive reinforcement, not just a relabeled no-op. Became D-01/D-02 in CONTEXT.md. + +--- + +## Acknowledge-user delivery path + +| Option | Description | Selected | +|--------|-------------|----------| +| Publish-visible Autotask ticket note | Customer-visible note via existing TicketNotes write path, visibility flag flipped | ✓ | +| Direct reply-email to the reporter | New delivery mechanism, independent of the ticket | | + +**User's choice:** Publish-visible Autotask ticket note. +**Notes:** Reuses Phase 21's existing internal-note write path, just customer-visible instead of internal-only, so the reporting employee sees it via their normal ticket notification/portal. + +--- + +## Auto-approval carve-out + +| Option | Description | Selected | +|--------|-------------|----------| +| Skip approval for acknowledge_user only | Thank-you message auto-posts for opted-in companies; all other actions (quarantine, block, warn_user) still always require manual approval | ✓ | +| Still require manual approval for everything | Automation gate only controls parse+classify; report-to-ticket still needs an explicit Approve click | | + +**User's choice:** Skip approval for acknowledge_user only. +**Notes:** A narrow, explicit carve-out from Phase 20's proposed-only model — applies only to this one non-security, non-destructive action type. + +--- + +## Gate page location + +| Option | Description | Selected | +|--------|-------------|----------| +| New /admin/phishing-automation page, admin role | Dedicated page mirroring /admin/client-scope's exact pattern | ✓ | +| Same page, super-admin only | Same UI, gated to super-admin instead | | + +**User's choice:** New /admin/phishing-automation page, admin role. +**Notes:** Consistent with other /admin/* pages — this is a service-delivery toggle, not billing/security config. + +--- + +## Claude's Discretion + +- Exact new verdict string value and its StatusBadge color/variant. +- Exact acknowledge_user message copy (tone direction given, wording open). +- Automation-gate settings table schema (adapt company_scope's precedent to 3 booleans, opt-in). +- Whether automatic pipeline stages run inline in the webhook handler or via a queued worker. +- Whether to add audit logging (who/when) for gate toggle changes, per the integration_settings precedent. + +## Deferred Ideas + +None — discussion stayed within phase scope. The webhook fix and the confidence +display bug that surfaced during the same investigation were resolved as +separate quick tasks (260716-n46, 260716-pgr) prior to this discussion, not +folded into this phase.