---
phase: 23-classification-disposition-per-client-automation-gate
plan: 04
type: execute
wave: 2
depends_on: ["23-03"]
files_modified:
  - app/admin/phishing-automation/page.tsx
  - app/admin/page.tsx
autonomous: false
requirements: [AUTOGATE-02]
must_haves:
  truths:
    - "An admin can open /admin/phishing-automation and see a searchable/filterable company table"
    - "Each company row exposes three independent Switch toggles: auto-parse, auto-classify, auto-report"
    - "Toggling a switch PATCHes /api/admin/phishing-automation/{companyId} and reflects the new state"
    - "The page is reachable from the /admin index card grid"
  artifacts:
    - path: "app/admin/phishing-automation/page.tsx"
      provides: "admin gate page (table + search/filter + 3 Switches per row)"
      min_lines: 120
    - path: "app/admin/page.tsx"
      provides: "tile linking to /admin/phishing-automation"
      contains: "phishing-automation"
  key_links:
    - from: "page toggle()"
      to: "/api/admin/phishing-automation/{companyId}"
      via: "fetch PATCH"
      pattern: "fetch\\(`/api/admin/phishing-automation/"
    - from: "page load"
      to: "/api/admin/phishing-automation"
      via: "fetch GET in useEffect"
      pattern: "fetch\\('/api/admin/phishing-automation'"
---

<objective>
Build the `/admin/phishing-automation` page: a searchable/filterable company table with three per-row `Switch` toggles (auto-parse, auto-classify, auto-report), structurally mirroring the existing `/admin/client-scope` page. Add a tile to the `/admin` index card grid so the page is discoverable.

Purpose: The human control surface for the per-company automation gate (D-08).
Output: New admin page + admin index tile.
</objective>

<execution_context>
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
@$HOME/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/23-classification-disposition-per-client-automation-gate/23-CONTEXT.md
@.planning/phases/23-classification-disposition-per-client-automation-gate/23-PATTERNS.md

<interfaces>
<!-- GET /api/admin/phishing-automation returns (Plan 03): -->
{ companies: Array<{ id: string; companyName: string; companyType: number | null; companyTypeLabel: string | null; autoParse: boolean; autoClassify: boolean; autoReport: boolean }> }
<!-- PATCH /api/admin/phishing-automation/{companyId} body: { autoParse, autoClassify, autoReport } (send all three current values) -->

Analog: app/admin/client-scope/page.tsx (243 lines, near line-for-line template):
- imports block lines 1-28 (shadcn Card/Table/Switch/Input/Select, PageHeader, lucide icons, sonner toast)
- Company interface + state lines 30-49
- load() useEffect lines 51-76
- toggle(company, next) lines 78-100
- search Input + type Select chrome lines 102-178
- <Table>/<TableRow> with trailing single <Switch> cell lines 200-236

/admin index tile pattern: app/admin/page.tsx tile objects (lines 238-249) — { title, href, icon, description } inside a section's tiles[] array.
</interfaces>
</context>

<tasks>

<task type="checkpoint:human-verify" gate="blocking">
  <name>Task 1: Build /admin/phishing-automation page (3-toggle company table)</name>
  <files>app/admin/phishing-automation/page.tsx</files>
  <read_first>
    - app/admin/client-scope/page.tsx (full file — the near-verbatim template)
    - components/ui/switch.tsx (Switch props)
    - .planning/phases/23-classification-disposition-per-client-automation-gate/23-PATTERNS.md (page.tsx section — Company interface with 3 booleans, per-stage toggle(company, stage, next), three <Switch> cells)
  </read_first>
  <action>
    Create `app/admin/phishing-automation/page.tsx` as a `'use client'` page cloning client-scope/page.tsx's structure. Extend the `Company` interface with `autoParse: boolean; autoClassify: boolean; autoReport: boolean` (drop the single `inScope`). Repoint the load fetch to `/api/admin/phishing-automation`. Replace the single `toggle(company, next)` with a per-stage `toggle(company: Company, stage: 'autoParse' | 'autoClassify' | 'autoReport', next: boolean)` that PATCHes `/api/admin/phishing-automation/${company.id}` with a body containing ALL three current flag values (the two unchanged from local state plus the toggled one), updates local state on success, and uses the same sonner error/toast handling as client-scope. In the table, add THREE header cells (Auto-parse, Auto-classify, Auto-report) and render three `<Switch>` cells per row, each wired to `toggle(company, '<stage>', next)` with an `aria-label` like `Auto-parse for ${company.companyName}`. Reuse the search Input + company-type Select filter chrome verbatim. Use a `PageHeader` titled "Phishing Automation" with a description explaining the opt-in gate and that auto-report only auto-posts the acknowledge_user thank-you note for User Awareness verdicts. Add a short helper caption near the toggles clarifying stage dependencies (report meaningfully requires classify; classify requires parse) — informational only, do not enforce in UI.
  </action>
  <verify>
    <automated>cd /opt/stacks/pulse && npx tsc --noEmit --pretty</automated>
    <what-built>The /admin/phishing-automation page with a company table and three per-row Switch toggles wired to the Plan 03 API.</what-built>
    <how-to-verify>
      1. Run `npm run dev` and sign in as an admin.
      2. Visit http://localhost:3100/admin/phishing-automation — confirm the company table loads with three toggle columns (Auto-parse, Auto-classify, Auto-report), all OFF by default.
      3. Toggle Auto-parse for one company; confirm a success toast and that the switch stays on after a page reload (persisted via PATCH).
      4. Use the search box and company-type filter; confirm the list filters.
    </how-to-verify>
    <resume-signal>Type "approved" or describe issues</resume-signal>
  </verify>
  <acceptance_criteria>
    - `grep -q "fetch('/api/admin/phishing-automation'" app/admin/phishing-automation/page.tsx` (GET load)
    - `grep -q "fetch(\`/api/admin/phishing-automation/" app/admin/phishing-automation/page.tsx` (PATCH toggle)
    - Three distinct `<Switch` elements per row bound to autoParse/autoClassify/autoReport
    - `npx tsc --noEmit` exits 0
    - Human verification confirms toggles persist across reload
  </acceptance_criteria>
  <done>Admins can view and toggle per-company auto-parse/classify/report on /admin/phishing-automation; toggles persist; type-check passes and human verification approved.</done>
</task>

<task type="auto">
  <name>Task 2: Add admin index tile for Phishing Automation</name>
  <files>app/admin/page.tsx</files>
  <read_first>
    - app/admin/page.tsx (tile object shape lines 238-249, the "Tools & Data" section and its icon imports; the Client Scope tile at lines 241-246 is the direct pattern)
  </read_first>
  <action>
    Add a new tile object to the same section that contains the "Client Scope" tile (Tools & Data) in `app/admin/page.tsx`: `{ title: 'Phishing Automation', href: '/admin/phishing-automation', icon: <a security/bot lucide icon already imported, e.g. ShieldCheck or Bot — import it if not present>, description: 'Per-company opt-in gate for automatic phishing parse / classify / acknowledge-note stages' }`. Ensure the chosen icon is imported at the top of the file if not already.
  </action>
  <verify>
    <automated>cd /opt/stacks/pulse && grep -q "/admin/phishing-automation" app/admin/page.tsx && npx tsc --noEmit --pretty</automated>
  </verify>
  <acceptance_criteria>
    - `grep -q "'/admin/phishing-automation'" app/admin/page.tsx` succeeds
    - The tile has a title, href, icon (imported), and description
    - `npx tsc --noEmit` exits 0
  </acceptance_criteria>
  <done>The /admin index card grid links to /admin/phishing-automation; type-check passes.</done>
</task>

</tasks>

<threat_model>
## Trust Boundaries

| Boundary | Description |
|----------|-------------|
| operator browser -> admin page/API | admin views + toggles gate flags; page-level admin gate + API-level requireAdmin |

## STRIDE Threat Register

| Threat ID | Category | Component | Disposition | Mitigation Plan |
|-----------|----------|-----------|-------------|-----------------|
| T-23-07 | Elevation of Privilege | /admin/phishing-automation page | mitigate | Server-side authority is the Plan 03 requireAdmin() gate on every API call; the page performs no privileged action except through those admin-gated endpoints (middleware also blocks unauth'd /admin/* page loads) |
</threat_model>

<verification>
- `npx tsc --noEmit --pretty` passes
- Page loads, three toggles per row persist via PATCH, reachable from /admin index (human-verified)
</verification>

<success_criteria>
- An admin can open /admin/phishing-automation from the /admin index and toggle each of the three per-company stages independently, with changes persisting
</success_criteria>

<output>
Create `.planning/phases/23-classification-disposition-per-client-automation-gate/23-04-SUMMARY.md` when done
</output>
