/** * /jobs/[id] — Investigation Bureau case file viewer. * * Server-rendered shell with the first snapshot fetched directly from * pg (one round-trip). A client island then polls /api/jobs/[id] every 3s * while the job is non-terminal (queued | running). * * Detectives: * - hypothesis_tournament → Sherlock Holmes * - evidence_chain → Edmond Locard * * Renders: * - Phase tracker (queued → claimed → running → complete | failed) * - Hypothesis cards w/ prior+posterior bars + Tetlock confidence_band badge * - Evidence cards w/ grade A/B/C badge + verbatim_excerpt + bbox crop link */ import { notFound } from "next/navigation"; import Link from "next/link"; import { pgQuery } from "@/lib/retrieval/db"; import { AuthBar } from "@/components/auth-bar"; import { BureauNav } from "@/components/bureau-nav"; import { JobStatusPoller } from "@/components/job-status-poller"; export const runtime = "nodejs"; export const dynamic = "force-dynamic"; interface InitialJob { job_id: string; kind: string; payload: Record | null; status: string; worker_id: string | null; started_at: string | null; finished_at: string | null; outputs: unknown; error: string | null; created_at: string; } export default async function JobPage({ params, }: { params: Promise<{ id: string }> }) { const { id } = await params; if (!/^[0-9a-f-]{36}$/i.test(id)) notFound(); const rows = await pgQuery( `SELECT job_id, kind, payload, status, worker_id, started_at, finished_at, outputs, error, created_at FROM public.investigation_jobs WHERE job_id = $1`, [id], ).catch(() => [] as InitialJob[]); const job = rows[0]; if (!job) notFound(); const detective = job.kind === "hypothesis_tournament" ? "holmes" : job.kind === "contradiction_scan" ? "dupin" : job.kind === "red_team_review" ? "schneier" : job.kind === "witness_analysis" ? "poirot" : job.kind === "outlier_scan" ? "taleb" : job.kind === "calibrate_hypothesis" ? "tetlock" : job.kind === "case_report" ? "case-writer" : "locard"; const detectiveName = detective === "holmes" ? "Sherlock Holmes" : detective === "dupin" ? "C. Auguste Dupin" : detective === "schneier" ? "Bruce Schneier" : detective === "poirot" ? "Hercule Poirot" : detective === "taleb" ? "Nassim Taleb" : detective === "tetlock" ? "Philip Tetlock" : detective === "case-writer" ? "Dr. Watson (Case-Writer)" : "Edmond Locard"; const detectiveSubtitle = detective === "holmes" ? "Hypothesis tournament · rival hypotheses with Bayesian update" : detective === "dupin" ? "Contradiction scan · pairs of chunks in irreconcilable tension" : detective === "schneier" ? "Red-team review · hidden assumptions, failure modes, alt explanations" : detective === "poirot" ? "Witness analysis · credibility / access / bias / corroboration" : detective === "taleb" ? "Outlier scan · chunks that violate the dominant model" : detective === "tetlock" ? "Calibration · honest Bayesian update with action recommendation" : detective === "case-writer" ? "Case narrative · five-act Watson assembly of all bureau artefacts" : "Evidence chain · verbatim quotes with chain of custody (Locard)"; const detectiveTone = detective === "holmes" ? "text-[#7fdbff]" : detective === "dupin" ? "text-[#ff8a4d]" : detective === "schneier" ? "text-[#ff3344]" : detective === "poirot" ? "text-[#9b5de5]" : detective === "taleb" ? "text-[#ffd23f]" : detective === "tetlock" ? "text-[#26d4cc]" : detective === "case-writer" ? "text-[#e0c080]" : "text-[#06d6a0]"; const detectiveBg = detective === "holmes" ? "from-[rgba(127,219,255,0.08)]" : detective === "dupin" ? "from-[rgba(255,138,77,0.08)]" : detective === "schneier" ? "from-[rgba(255,51,68,0.08)]" : detective === "poirot" ? "from-[rgba(155,93,229,0.08)]" : detective === "taleb" ? "from-[rgba(255,210,63,0.08)]" : detective === "tetlock" ? "from-[rgba(38,212,204,0.08)]" : detective === "case-writer" ? "from-[rgba(224,192,128,0.08)]" : "from-[rgba(6,214,160,0.08)]"; const payload = (job.payload ?? {}) as Record; const question = (payload.question ?? payload.topic ?? payload.hypothesis_id ?? payload.person_id) as string | undefined; const questionLabel = job.kind === "contradiction_scan" ? "Topic" : job.kind === "red_team_review" ? "Hypothesis under attack" : job.kind === "witness_analysis" ? "Witness under analysis" : job.kind === "outlier_scan" ? "Topic to outlier-scan" : job.kind === "calibrate_hypothesis" ? "Hypothesis under recalibration" : job.kind === "case_report" ? "Case to assemble" : "Question"; const docId = payload.doc_id as string | undefined; return (

{detectiveName}

{detectiveSubtitle}

{detective}
{question && (
{questionLabel}
{question}
)} {docId && (
Scope: {docId}
)}
); }