/** * /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(); // Reader-facing: no detective surfacing. Every kind reads as "case // investigation in progress" with neutral copy. Detective identities // remain internal in the runtime (audit log) but never reach the UI. const detectiveName = "Investigation in progress"; const detectiveSubtitle = job.kind === "case_report" ? "Assembling a narrative case file from the primary record" : job.kind === "evidence_chain" ? "Pulling verbatim citations from the document" : "Reading the archive for this case"; const detectiveTone = "text-[#e0c080]"; const detectiveBg = "from-[rgba(224,192,128,0.06)]"; 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}

{job.kind}
{question && (
{questionLabel}
{question}
)} {docId && (
Scope: {docId}
)}
); }