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.