Commit graph

788 commits

Author SHA1 Message Date
e78f1b0cdf docs(22-05): complete Action Area Card plan
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-16 14:43:35 -04:00
1a7a5922fe feat(22-05): remediate + mark-false-positive + permission/resolved gating (REVIEW-06, D-05, D-06)
- Add GatedButton: buttons stay in the DOM (D-05) always, wrapped in a
  Tooltip explanation when disabled
- Gate all three actions with hasPermission(role, 'phishing', 'approve'|
  'remediate') from lib/permissions.ts — the identical check the server
  routes enforce, never a bespoke role === 'admin' string check (REVIEW-06)
- Derive resolved = campaignStatus === 'false_positive' OR any completed
  remediation_actions row; resolved-state tooltip mirrors the completed
  action's approver/date or the campaign's updated_at
- Remediate: AlertDialog confirmation listing approved action count/types,
  then POST /remediate (no body), refetch on success
- Mark as false positive: AlertDialog with optional reason Textarea, then
  POST /mark-false-positive { reason? }; disabled reason mirrors the
  server's 409 guard exactly (approved/completed remediation blocks it)
2026-07-16 14:42:05 -04:00
11190abafd feat(22-05): checkbox list + editable params + Approve selected (D-03)
- Create ActionAreaCard with a null-guard on classification (renders an
  informational note, never dereferences recommendedActions, for the
  default grouped-but-unclassified state)
- Render one checkbox row per recommended action with an always-visible
  params form pre-filled via deriveDefaultParams(actionType, evidence)
- Submit exact ApproveActionInput[] to POST /approve; purge_message
  mailboxes is edited as a comma-separated string and normalized to
  string[] at submit time
- Refetch via onActionComplete() on success (D-04, no optimistic mutation)
2026-07-16 14:40:00 -04:00
75cd454ff8 Merge branch 'worktree-agent-a432f929ffab2e5b8'
# Conflicts:
#	.planning/phases/22-approval-ui-livelink-addressable-campaign-review-and-approve/deferred-items.md
2026-07-16 14:33:16 -04:00
4f976b741f chore: merge executor worktree (worktree-agent-a972575d901be6c4f)
# Conflicts:
#	.planning/phases/22-approval-ui-livelink-addressable-campaign-review-and-approve/deferred-items.md
2026-07-16 14:32:29 -04:00
2c70747822 chore: merge executor worktree (worktree-agent-a5b1c5010c37561a1) 2026-07-16 14:32:03 -04:00
d81fc46dc0 docs(22-03): record self-check result in summary 2026-07-16 14:31:17 -04:00
f7516c04c7 docs(22-03): complete evidence display plan 2026-07-16 14:30:59 -04:00
fdc6a52dde docs(22-04): complete ClassificationCard + TimelineCard plan
- Add 22-04-SUMMARY.md documenting the two components delivered
- Log pre-existing unrelated itglue-search.test.ts failures to
  deferred-items.md (out of scope for this plan)
- Mark REVIEW-02, REVIEW-04 complete in REQUIREMENTS.md
2026-07-16 14:30:52 -04:00
9702847131 docs(22-01): append self-check results to SUMMARY.md
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W6RuWdiUiXrPK6FLBHjtpY
2026-07-16 14:30:38 -04:00
96507dc2c6 docs(22-01): complete Wave 0 pure-logic extraction plan
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W6RuWdiUiXrPK6FLBHjtpY
2026-07-16 14:30:18 -04:00
2d5761bf6f docs(22-03): log pre-existing itglue-search test failures as deferred
Out-of-scope failures observed during npm test for plan 22-03; unrelated
to this plan's files.
2026-07-16 14:29:40 -04:00
4ec5ba4979 feat(22-03): add EvidenceCard tabbed EML evidence display (REVIEW-03)
- components/phishing/evidence-card.tsx renders Headers, URLs,
  Attachments, Body preview, and Blast Radius tabs for a selected
  message
- Body preview renders inside a <pre className="whitespace-pre-wrap">
  as plain JSX text only, never via a raw-HTML injection prop
- URLs tab delegates to UrlList (D-09 inert copy-only)
- Blast radius renders explicit unavailable-state copy or a
  matched/delivered/held/rejected/clicked stat row + per-recipient
  table when ok
- CardTitle explicitly overridden with font-bold per UI-SPEC typography
2026-07-16 14:29:34 -04:00
e990a320b2 feat(22-04): add TimelineCard chronological event renderer
- Merges reports/classifications/audit-events into one ascending list
  (relies on server ordering, no client-side sort)
- Per-kind icon/label/tint: FileText for reports, Sparkles tinted by
  verdict for classifications, event_type table for audit rows
  (remediation_approved/completed, campaign_marked_false_positive,
  campaign_classified, humanized fallback for anything else)
- 8px rail dot + border-l connector per UI-SPEC Timeline Spec
2026-07-16 14:29:15 -04:00
d30a49f644 feat(22-01): implement mergeTimeline for reports/classifications/audit merge
- Discriminated union TimelineEntry with report/classification/audit variants
- Ascending sort by createdAt with stable report<classification<audit tie-break
2026-07-16 14:28:27 -04:00
37f6657839 test(22-01): add failing test for mergeTimeline
- Covers ascending chronological sort across reports/classifications/audit
- Asserts discriminant kind + source fields per variant, plus stable tie-break order
2026-07-16 14:27:59 -04:00
86cffc45b8 feat(22-01): implement deriveDefaultParams for the 7 remediation action types
- Pure switch over no_action/warn_user/block_sender/purge_message/
  reset_password/isolate_endpoint/disable_forwarding_rule
- Unknown/future action types fall through to {} rather than throwing
2026-07-16 14:27:33 -04:00
14adddfdf0 feat(22-04): add ClassificationCard read-only verdict display
- Renders latest classification verdict/confidence/summary/reasons
- Recommended-action chips (informational, no checkboxes)
- Requires-approval warning Alert when requiresApproval is true
- Reclassify button gated on hasPermission(role, 'phishing', 'analyze')
- Returns null when classification is missing (empty-state handled by plan 06)
2026-07-16 14:27:29 -04:00
c64a90572d test(22-01): add failing test for deriveDefaultParams
- Covers all 7 known action types plus unknown-type fallback
- Asserts null-evidence empty-string fallback behavior
2026-07-16 14:27:13 -04:00
87008a5da6 feat(22-03): add tooltip primitive + inert UrlList component (D-09)
- npx shadcn add tooltip generates components/ui/tooltip.tsx (official
  registry, no npm dependency added)
- components/phishing/url-list.tsx renders extracted URLs as inert
  <code> text with copy-to-clipboard only — no <a>/href, no <Link>,
  no navigating onClick per D-09
2026-07-16 14:27:11 -04:00
e619321b4b feat(22-01): implement resolveTicketToCampaign resolver service
- Pure lookup: reports row for a ticket id -> found/reportId/campaignId/ticketNumber
- Parameterized query only (WHERE ticket_id = $1), no requirePermission/NextResponse
2026-07-16 14:26:32 -04:00
20b1e1bb6f test(22-01): add failing test for resolveTicketToCampaign
- Covers no-report, ungrouped, and grouped resolution states
- Asserts parameterized ticket_id lookup query shape
2026-07-16 14:26:28 -04:00
4b5e31c068 docs(22): create phase plan 2026-07-16 14:10:33 -04:00
476eb017eb fix(22): resolve checker blockers (unclassified default state, LiveLink id gate) 2026-07-16 14:00:52 -04:00
b4707ce962 docs(22): create phase plan (6 plans, 3 waves) 2026-07-16 13:46:09 -04:00
9164dc6177 docs(phase-22): add validation strategy 2026-07-16 13:29:48 -04:00
7072190629 docs(22): research phase domain for approval UI (LiveLink) 2026-07-16 13:28:39 -04:00
5d10f66f6b docs(22): fix typography weight overflow in UI design contract
Checker flagged 4 distinct font weights in use together (400/500/600/700)
against a 2-weight max. Consolidate to 400 (Body+Label, differentiated by
size/color) and 700 (Heading+Display, differentiated by size only) —
requires an explicit font-bold override on CardTitle since shadcn's
default is font-semibold. Also addresses two non-blocking flags:
"Reclassify" -> "Reclassify ticket", and aria-label on icon-only Copy
buttons (URL/hash).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W6RuWdiUiXrPK6FLBHjtpY
2026-07-16 13:17:30 -04:00
fd2280ab2c docs(22): UI design contract for approval UI (LiveLink)
Composes existing Pulse design tokens into a phase-specific contract:
page layout for the ticket-scoped review page and new campaigns list
page, evidence/timeline/classification display specs, and the
approve/remediate/mark-false-positive action-area contract (D-03..D-06
disabled-state and confirmation copy).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W6RuWdiUiXrPK6FLBHjtpY
2026-07-16 13:12:36 -04:00
73f2e25644 docs(state): record phase 22 context session 2026-07-16 12:49:14 -04:00
47c3510667 docs(22): capture phase context 2026-07-16 12:49:01 -04:00
ec7c2114bf docs(phase-21): complete phase execution 2026-07-16 12:19:06 -04:00
5d34dc3506 docs(phase-21): update tracking after wave 2 2026-07-16 12:16:20 -04:00
28f604b1cf chore: merge executor worktree (21-02) 2026-07-16 12:15:52 -04:00
fff7d97c5d docs(21-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 12:15:25 -04:00
950227ea4e docs(21-02): add plan summary for triage-note service and endpoint
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W6RuWdiUiXrPK6FLBHjtpY
2026-07-16 12:15:07 -04:00
e3a9cb5191 feat(21-02): add POST /api/phishing/campaigns/[id]/triage-note route
Structural twin of the classify route: requirePermission('phishing',
'analyze') gate, UUID guard, campaign-exists 404 check, delegates to
generateAndPostTriageNote and returns its result verbatim (note text +
per-ticket posted/error status, D-06). No audit-event write — deferred
per 21-CONTEXT.md.
2026-07-16 12:14:23 -04:00
2d410f8d15 feat(21-02): implement triage-note-service (evidence gather + note post loop)
GREEN: generateAndPostTriageNote(campaignId) gathers linked reports,
most-recent classification (NUMERIC confidence coerced to a JS number),
current remediation_actions, and real url indicators via the
reports->messages->indicators join; renders the sanitized note via Plan
01's formatTriageNote, then posts one internal TicketNotes write per
linked ticket with independent per-ticket error capture so a single
write failure never aborts the call (D-05) and note text is always
returned (D-06).
2026-07-16 12:13:50 -04:00
34a0269e9b test(21-02): add failing test for triage-note service
RED: generateAndPostTriageNote does not exist yet — covers per-ticket
write loop, D-05 partial-failure isolation, D-06 note-text-always-returned,
indicator-URL sanitization flow, and NUMERIC confidence coercion.
2026-07-16 12:13:18 -04:00
4bbea170b2 docs(phase-21): update tracking after wave 1 2026-07-16 12:08:43 -04:00
818f1fe0ae chore: merge executor worktree (21-01) 2026-07-16 12:08:01 -04:00
96734399e5 docs(21-01): append self-check result to summary 2026-07-16 12:07:38 -04:00
03bb560874 docs(21-01): complete triage-note sanitizer/formatter plan
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W6RuWdiUiXrPK6FLBHjtpY
2026-07-16 12:07:24 -04:00
17ba0e880f feat(21-01): implement triage-note formatter + TriageNoteEvidence contract
- formatTriageNote renders verdict/confidence/summary/reasons/blast-radius
  (both branches)/recommended actions/current remediation state as prose
- Routes indicator URLs through sanitizeUrl and the whole assembled output
  through sanitizeNoteText before returning
- Handles null verdict/confidence gracefully
- Exports TriageNoteEvidence interface for Plan 02
- All 9 formatter tests pass
2026-07-16 12:06:31 -04:00
b4e05eb6ad test(21-01): add failing tests for triage-note formatter
- Cover verdict/confidence rendering, reasons/summary sections
- Cover both BlastRadiusResult branches (ok and unavailable)
- Cover remediation-state rendering (empty and populated)
- Cover URL sanitization and null-verdict graceful handling
2026-07-16 12:05:59 -04:00
226f300513 feat(21-01): implement triage-note sanitizer
- sanitizeUrl strips query+fragment, keeps scheme+host+path, never throws
- sanitizeNoteText redacts Bearer/Authorization tokens and credential
  query-param values while preserving sender emails and attachment hashes
- All 8 sanitizer tests pass
2026-07-16 12:05:34 -04:00
cc93707e41 test(21-01): add failing tests for triage-note sanitizer
- Cover URL query/fragment stripping, malformed-URL no-throw
- Cover Bearer token and credential query-param redaction
- Cover email/hash preservation (evidence, not secrets)
2026-07-16 12:04:58 -04:00
235bc49810 fix(21): join real url indicators + cast NUMERIC confidence in triage-note plan 2026-07-16 11:37:50 -04:00
b0a15f30f2 docs(21): create phase plan 2026-07-16 11:28:19 -04:00
8d918f67bb docs(state): record phase 21 context session 2026-07-16 11:17:01 -04:00