From 3bd9a716b28281e7a402925cf01ccb1382b8c0ef Mon Sep 17 00:00:00 2001 From: Claude Code Date: Thu, 14 May 2026 18:38:27 +0000 Subject: [PATCH] Phase B: AI press-release rewrite pipeline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pluggable rewrite client (Anthropic default, ollama/openrouter stubs) using claude-opus-4-7 with prompt caching on system prompt + style guide. Routes the raw submission through: 1. bb.ai_original_submissions — raw never edited 2. bb.ai_rewrite_jobs — every attempt logged with token counts 3. quality gates — AI-tell banned-word check + heuristic AI-detector score (threshold 0.65) 4. regenerate up to 3x — on banned words or detector trip 5. bb.ai_rewritten_articles — status ai_rewritten_pending_review 6. /admin/review-queue — list + detail page with approve / reject / needs-human actions; on approve, the original submission is FK-linked to the published rewrite New files only — no existing routes or contributor flow modified. Persona auto-selection routes by beat keywords; 6 personas seeded (marcus-halverson, elena-vasquez, darren-okafor, sarah-quinn, mike-trayton, priya-rao) with SVG monogram avatars in public/assets/images/personas/. Smoke test (Matrox / ST 2110 sample press release): - persona auto-routed to darren-okafor (protocols-specs) - anthropic 22.3s, in=2616 / out=1372 tokens - gates passed first try, score=0.000, no banned words - review_queue row created Co-Authored-By: Claude Opus 4.7 (1M context) --- .../assets/images/personas/darren-okafor.svg | 16 ++ .../assets/images/personas/elena-vasquez.svg | 16 ++ .../images/personas/marcus-halverson.svg | 16 ++ .../assets/images/personas/mike-trayton.svg | 16 ++ public/assets/images/personas/priya-rao.svg | 16 ++ public/assets/images/personas/sarah-quinn.svg | 16 ++ .../admin/review-queue/[id]/ReviewActions.tsx | 73 ++++++ src/app/admin/review-queue/[id]/page.tsx | 106 ++++++++ src/app/admin/review-queue/page.tsx | 120 ++++++++++ src/app/api/admin/review-queue/[id]/route.ts | 71 ++++++ src/app/api/ai/rewrite/route.ts | 30 +++ src/app/api/ai/submit/route.ts | 39 +++ src/lib/ai/persona-prompts.ts | 101 ++++++++ src/lib/ai/personas.ts | 71 ++++++ src/lib/ai/quality-gates.ts | 123 ++++++++++ src/lib/ai/rewrite-client.ts | 164 +++++++++++++ src/lib/ai/rewrite-pipeline.ts | 226 ++++++++++++++++++ 17 files changed, 1220 insertions(+) create mode 100644 public/assets/images/personas/darren-okafor.svg create mode 100644 public/assets/images/personas/elena-vasquez.svg create mode 100644 public/assets/images/personas/marcus-halverson.svg create mode 100644 public/assets/images/personas/mike-trayton.svg create mode 100644 public/assets/images/personas/priya-rao.svg create mode 100644 public/assets/images/personas/sarah-quinn.svg create mode 100644 src/app/admin/review-queue/[id]/ReviewActions.tsx create mode 100644 src/app/admin/review-queue/[id]/page.tsx create mode 100644 src/app/admin/review-queue/page.tsx create mode 100644 src/app/api/admin/review-queue/[id]/route.ts create mode 100644 src/app/api/ai/rewrite/route.ts create mode 100644 src/app/api/ai/submit/route.ts create mode 100644 src/lib/ai/persona-prompts.ts create mode 100644 src/lib/ai/personas.ts create mode 100644 src/lib/ai/quality-gates.ts create mode 100644 src/lib/ai/rewrite-client.ts create mode 100644 src/lib/ai/rewrite-pipeline.ts diff --git a/public/assets/images/personas/darren-okafor.svg b/public/assets/images/personas/darren-okafor.svg new file mode 100644 index 0000000..8c18493 --- /dev/null +++ b/public/assets/images/personas/darren-okafor.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + DO + PROTOCOLS / SPECS + diff --git a/public/assets/images/personas/elena-vasquez.svg b/public/assets/images/personas/elena-vasquez.svg new file mode 100644 index 0000000..b72d1ac --- /dev/null +++ b/public/assets/images/personas/elena-vasquez.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + EV + STREAMING / OTT + diff --git a/public/assets/images/personas/marcus-halverson.svg b/public/assets/images/personas/marcus-halverson.svg new file mode 100644 index 0000000..3111901 --- /dev/null +++ b/public/assets/images/personas/marcus-halverson.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + MH + INDUSTRY VETERAN + diff --git a/public/assets/images/personas/mike-trayton.svg b/public/assets/images/personas/mike-trayton.svg new file mode 100644 index 0000000..bb6dabf --- /dev/null +++ b/public/assets/images/personas/mike-trayton.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + MT + LIVE / SPORTS + diff --git a/public/assets/images/personas/priya-rao.svg b/public/assets/images/personas/priya-rao.svg new file mode 100644 index 0000000..08ecdf7 --- /dev/null +++ b/public/assets/images/personas/priya-rao.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + PR + POST-PRODUCTION + diff --git a/public/assets/images/personas/sarah-quinn.svg b/public/assets/images/personas/sarah-quinn.svg new file mode 100644 index 0000000..73806b2 --- /dev/null +++ b/public/assets/images/personas/sarah-quinn.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + SQ + BUSINESS / M&A + diff --git a/src/app/admin/review-queue/[id]/ReviewActions.tsx b/src/app/admin/review-queue/[id]/ReviewActions.tsx new file mode 100644 index 0000000..98fa8cf --- /dev/null +++ b/src/app/admin/review-queue/[id]/ReviewActions.tsx @@ -0,0 +1,73 @@ +"use client"; + +import { useState, useTransition } from "react"; +import { useRouter } from "next/navigation"; + +interface Props { + id: string; + currentStatus: string; +} + +export default function ReviewActions({ id, currentStatus }: Props) { + const [note, setNote] = useState(""); + const [pending, startTransition] = useTransition(); + const [err, setErr] = useState(null); + const router = useRouter(); + + async function act(action: "approve" | "reject" | "needs_human") { + setErr(null); + const res = await fetch(`/api/admin/review-queue/${id}`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ action, note }), + }); + if (!res.ok) { + const body = await res.json().catch(() => ({})); + setErr(body.error || `HTTP ${res.status}`); + return; + } + startTransition(() => { + router.refresh(); + router.push("/admin/review-queue"); + }); + } + + return ( +
+
+ Current status: {currentStatus} +
+