docs(23): capture phase context

This commit is contained in:
lorentz 2026-07-16 18:37:04 -04:00
parent 8a11a9f331
commit 98c7153a10
2 changed files with 344 additions and 0 deletions

View file

@ -0,0 +1,246 @@
# Phase 23: Classification Disposition + Per-Client Automation Gate - Context
**Gathered:** 2026-07-16
**Status:** Ready for planning
<domain>
## 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).
</domain>
<decisions>
## 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.
</decisions>
<canonical_refs>
## 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).
</canonical_refs>
<code_context>
## 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).
</code_context>
<specifics>
## 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.
</specifics>
<deferred>
## 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.
</deferred>
---
*Phase: 23-classification-disposition-per-client-automation-gate*
*Context gathered: 2026-07-16*

View file

@ -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.