disclosure-bureau/web/components/bureau-snapshot.tsx

352 lines
17 KiB
TypeScript
Raw Normal View History

W3.9: surface the Investigation Bureau on the homepage + /bureau hub Closes a UX gap the user surfaced: W3.5-3.8 built 8 detectives, 4 new URL endpoints (/jobs/[id], /h/[id], /c/[slug], /api/h/[id]/red-team) and a chat tool, but the homepage was unchanged — the bureau was invisible unless you knew the URL or asked the chat to invoke request_investigation. Homepage (web/app/page.tsx): - Title `▍ war.gov/ufo — Investigative Wiki` → `▍ The Disclosure Bureau` - Subtitle expanded from "Holmes · Poirot · Dupin · Locard" to all 8 detectives (Holmes · Locard · Dupin · Schneier · Poirot · Taleb · Tetlock · Case-Writer) - New `🔎 bureau` topbar link (gold, between graph/stats and batch) - BureauSnapshot inserted right after the header BureauSnapshot (web/components/bureau-snapshot.tsx) — server component: - 8 detective tiles with role labels (each in its tone color) - 6 clickable counters (evidence / hypotheses / contradictions / witnesses / outliers / case reports) — anchor to /bureau#section - 6 "recent artefacts" columns surfacing the last 3-4 of each kind: hypotheses with prior→posterior + band + ↳reviewed_by marker, contradictions with topic + resolution_status, evidence with Grade badge + verbatim quote, outliers with title + scope.kind, witness analyses with canonical_name + credibility + verdict, case reports with slug + link to /c/<slug> - "Recent jobs" strip linking to /jobs/[id] color-coded by status - Reports read from /data/ufo/case/reports/ via fs.readdir + stat, sorted by mtime — no DB round-trip needed for that section /bureau (web/app/bureau/page.tsx) — full hub: - Header with full counts - 7 sections (anchored to homepage counter links): Case reports, Hypotheses, Evidence, Contradictions, Outliers, Witnesses, Recent jobs table — each rendering up to 100 rows - Reports section parses frontmatter from each .md to surface topic + n_hypotheses + n_evidence on the card Runtime fixes batched in: - Poirot: coerce entity_pk via Number() — node-postgres returns BIGINT as string by default; writer's Number.isFinite() rejected it as "person_entity_pk required" (j-edgar-hoover retry path) - Tetlock: write_calibration rationale cap 600 → 1200 chars. Prompt still asks ≤ 600 but a 2× slack beats failing the job on honest analysis. Observed live: Tetlock emitted ~620 chars on H-0003 and the writer rejected the entire calibration. - Case-Writer: Promise.all of 5 queries × max_parallel=2 jobs demanded up to 10 connections against the investigator role's rolconnlimit=4 → "too many connections for role investigator". Sequentialized — the LLM call is the hot path, not these queries. Smoke results visible now on the homepage: - 3 hypotheses (H-0001/2/3) about green fireballs origin - 3 contradictions (R-0001/2/3) about color, geographic confinement, exclusive-green vs multicolored - 2 evidence cards (E-0002/3) Grade B - 3 outliers (G-0001/2/3) — including Taleb's deliberate meteor-shower-camouflage flag - 1 case report at /c/green-fireballs-sandia (Watson 13.4 KB, five-act narrative, fully cited) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 01:41:28 +00:00
/**
* BureauSnapshot homepage panel surfacing the Investigation Bureau's
* live artefact stack.
*
* Reads counts + the 3 most-recent items from each table directly via pg.
* Server component renders straight into the homepage shell, no client
* hydration cost.
*/
import Link from "next/link";
import { pgQuery } from "@/lib/retrieval/db";
W3.10: clickable detective tiles + quick-launch form + doc bureau panel Builds on top of W3.9 to turn the homepage Bureau from a read-only dashboard into a working command center. UI improvements (web/components/bureau-snapshot.tsx): - Detective tiles are now <Link>s — each navigates to its primary artefact section in /bureau (Holmes→#hypotheses, Locard→#evidence, Dupin→#contradictions, Schneier→#hypotheses, Poirot→#witnesses, Taleb→#outliers, Tetlock→#hypotheses, Case-Writer→#reports). Hover bg matches the detective's tone color. - <QuickLaunch /> form inserted right under the tiles. New <QuickLaunch /> client component: - Detective dropdown (7 active kinds; evidence_chain not yet exposed here since it needs a doc_id better picked from the doc page). - Single input swaps placeholder + aria-label by kind: question for Holmes, topic for Dupin/Taleb/Case-Writer, hypothesis_id for Schneier/Tetlock, person_id for Poirot. - Submits to POST /api/bureau/launch and redirects to /jobs/[id] via the next.js router. - Loading state ("queueing…") + error display inline. POST /api/bureau/launch (web/app/api/bureau/launch/route.ts): - Same 8-kind validator as the chat tool's request_investigation. - Auth required when Supabase is configured (triggered_by = user:email). - Returns { job_id, kind, detective, status_url, eta_seconds }. DocBureauPanel on /d/[docId] (web/components/doc-bureau-panel.tsx): - Server component inserted between the doc header and AnomalyHighlights. - Surfaces every bureau artefact that touches the doc: · Evidence whose source_page_id starts with docId/p · Hypotheses citing any of those evidence_ids · Contradictions whose chunks[] has any item with this doc_id · Gaps/outliers with scope.doc_id == docId · Case reports whose markdown body references docId (filesystem scan) - Empty state shows "Investigation Bureau — untouched" with a CTA linking back to the homepage to launch the first investigation. - When non-empty, header counts total artefacts + links to /bureau for the full view. Metadata (web/app/layout.tsx): - description rewritten from "Investigative wiki of the US Department of War UAP/UFO archive (war.gov/ufo)" to one that names the bureau + the 8 detectives. Affects SERP previews + social-card defaults. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 02:33:00 +00:00
import { QuickLaunch } from "./quick-launch";
W3.9: surface the Investigation Bureau on the homepage + /bureau hub Closes a UX gap the user surfaced: W3.5-3.8 built 8 detectives, 4 new URL endpoints (/jobs/[id], /h/[id], /c/[slug], /api/h/[id]/red-team) and a chat tool, but the homepage was unchanged — the bureau was invisible unless you knew the URL or asked the chat to invoke request_investigation. Homepage (web/app/page.tsx): - Title `▍ war.gov/ufo — Investigative Wiki` → `▍ The Disclosure Bureau` - Subtitle expanded from "Holmes · Poirot · Dupin · Locard" to all 8 detectives (Holmes · Locard · Dupin · Schneier · Poirot · Taleb · Tetlock · Case-Writer) - New `🔎 bureau` topbar link (gold, between graph/stats and batch) - BureauSnapshot inserted right after the header BureauSnapshot (web/components/bureau-snapshot.tsx) — server component: - 8 detective tiles with role labels (each in its tone color) - 6 clickable counters (evidence / hypotheses / contradictions / witnesses / outliers / case reports) — anchor to /bureau#section - 6 "recent artefacts" columns surfacing the last 3-4 of each kind: hypotheses with prior→posterior + band + ↳reviewed_by marker, contradictions with topic + resolution_status, evidence with Grade badge + verbatim quote, outliers with title + scope.kind, witness analyses with canonical_name + credibility + verdict, case reports with slug + link to /c/<slug> - "Recent jobs" strip linking to /jobs/[id] color-coded by status - Reports read from /data/ufo/case/reports/ via fs.readdir + stat, sorted by mtime — no DB round-trip needed for that section /bureau (web/app/bureau/page.tsx) — full hub: - Header with full counts - 7 sections (anchored to homepage counter links): Case reports, Hypotheses, Evidence, Contradictions, Outliers, Witnesses, Recent jobs table — each rendering up to 100 rows - Reports section parses frontmatter from each .md to surface topic + n_hypotheses + n_evidence on the card Runtime fixes batched in: - Poirot: coerce entity_pk via Number() — node-postgres returns BIGINT as string by default; writer's Number.isFinite() rejected it as "person_entity_pk required" (j-edgar-hoover retry path) - Tetlock: write_calibration rationale cap 600 → 1200 chars. Prompt still asks ≤ 600 but a 2× slack beats failing the job on honest analysis. Observed live: Tetlock emitted ~620 chars on H-0003 and the writer rejected the entire calibration. - Case-Writer: Promise.all of 5 queries × max_parallel=2 jobs demanded up to 10 connections against the investigator role's rolconnlimit=4 → "too many connections for role investigator". Sequentialized — the LLM call is the hot path, not these queries. Smoke results visible now on the homepage: - 3 hypotheses (H-0001/2/3) about green fireballs origin - 3 contradictions (R-0001/2/3) about color, geographic confinement, exclusive-green vs multicolored - 2 evidence cards (E-0002/3) Grade B - 3 outliers (G-0001/2/3) — including Taleb's deliberate meteor-shower-camouflage flag - 1 case report at /c/green-fireballs-sandia (Watson 13.4 KB, five-act narrative, fully cited) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 01:41:28 +00:00
interface CountRow { c: string }
interface RecentEvidence { evidence_id: string; grade: string; verbatim_excerpt: string; source_page_id: string; confidence_band: string | null }
W4: bilingual EN + PT-BR Investigation Bureau (CLAUDE.md §3 contract) User flagged that the bureau was emitting English-only output, violating the project's bilingual rule. Every narrative field now ships in both languages: stored in sibling DB columns + rendered as adjacent markdown sections per CLAUDE.md §3. Migration 0007 (apply as supabase_admin): - public.hypotheses +question_pt_br, +position_pt_br, +argument_for_pt_br, +argument_against_pt_br - public.contradictions +topic_pt_br, +notes_pt_br - public.witnesses +access_to_event_pt_br, +bias_notes_pt_br, +verdict_pt_br - public.gaps +description_pt_br, +suggested_next_move_pt_br - public.evidence: unchanged (verbatim_excerpt stays source-language) - JSONB siblings inside contradictions.chunks + gaps.scope handled at runtime (statement_pt_br, title_pt_br, dominant_model_pt_br, why_surprising_pt_br, what_it_implies_pt_br). Detective prompts (all 7) rewritten with explicit bilingual JSON contract: - Output protocol section names every EN field + its _pt_br sibling - "Bilingual is mandatory" warning in the task instruction - Sentinel skip-states unchanged (NO_HYPOTHESES, NO_CONTRADICTIONS, INSUFFICIENT_TESTIMONY, INSUFFICIENT_HYPOTHESIS, NO_OUTLIERS, NO_NEW_EVIDENCE, INSUFFICIENT_ARTEFACTS) - Schneier: parallel arrays — hidden_assumptions[i] matches hidden_assumptions_pt_br[i], lengths must match - Case-Writer: interleaved §1 (EN) / §1 (PT-BR) per act in the body Writer-side validation (all 7 tools): - Reject INSERT if PT-BR sibling missing when EN field is set - Persist both languages atomically in one INSERT (no half-updates) - Markdown renderers write adjacent EN+PT-BR sections in case files (## Argument for (EN) followed by ## Argumento a favor (PT-BR), etc.) Detective parse layer (all 7 detectives): - Coerce both keys from JSON output - "incomplete_bilingual_*" skip reason when either side missing - Defensive: PT-BR fields trimmed + length-capped same as EN Orchestrator propagates question_pt_br + topic_pt_br through job payload to runHolmes / runCaseWriter, mirroring the chat-tool entry point. Web (UI): - /api/jobs/[id] hydrates _pt_br siblings from pg - job-status-poller HypothesisCard: PT-BR primary, EN in <details> fallback when both exist - ContradictionCard: PT-BR statement primary + secondary EN quote - WitnessCard: PT-BR verdict primary + secondary EN quote, panels in PT - GapCard: PT-BR title/why/implies primary - /bureau hub: SELECTs both columns, renders PT-BR primary - /h/[id]: ArgumentPanel renders PT-BR primary with collapsible EN fallback when both exist - BureauSnapshot homepage: position_pt_br / topic_pt_br / verdict_pt_br primary - DocBureauPanel /d/[doc]: same primary-PT-BR pattern - New web/lib/i18n/pick.ts helper (unused yet by chat/agents — kept for future locale-driven switching when both languages are equally full; current rule is PT-BR-first since the user is brasileiro) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 15:02:59 +00:00
interface RecentHypothesis { hypothesis_id: string; position: string; position_pt_br: string | null; posterior: number | string | null; confidence_band: string | null; reviewed_by: string | null }
interface RecentContradiction { contradiction_id: string; topic: string; topic_pt_br: string | null; resolution_status: string }
interface RecentGap { gap_id: string; description: string; description_pt_br: string | null; scope: unknown }
interface RecentWitness { witness_id: string; canonical_name: string | null; credibility: string | null; verdict: string | null; verdict_pt_br: string | null }
W3.9: surface the Investigation Bureau on the homepage + /bureau hub Closes a UX gap the user surfaced: W3.5-3.8 built 8 detectives, 4 new URL endpoints (/jobs/[id], /h/[id], /c/[slug], /api/h/[id]/red-team) and a chat tool, but the homepage was unchanged — the bureau was invisible unless you knew the URL or asked the chat to invoke request_investigation. Homepage (web/app/page.tsx): - Title `▍ war.gov/ufo — Investigative Wiki` → `▍ The Disclosure Bureau` - Subtitle expanded from "Holmes · Poirot · Dupin · Locard" to all 8 detectives (Holmes · Locard · Dupin · Schneier · Poirot · Taleb · Tetlock · Case-Writer) - New `🔎 bureau` topbar link (gold, between graph/stats and batch) - BureauSnapshot inserted right after the header BureauSnapshot (web/components/bureau-snapshot.tsx) — server component: - 8 detective tiles with role labels (each in its tone color) - 6 clickable counters (evidence / hypotheses / contradictions / witnesses / outliers / case reports) — anchor to /bureau#section - 6 "recent artefacts" columns surfacing the last 3-4 of each kind: hypotheses with prior→posterior + band + ↳reviewed_by marker, contradictions with topic + resolution_status, evidence with Grade badge + verbatim quote, outliers with title + scope.kind, witness analyses with canonical_name + credibility + verdict, case reports with slug + link to /c/<slug> - "Recent jobs" strip linking to /jobs/[id] color-coded by status - Reports read from /data/ufo/case/reports/ via fs.readdir + stat, sorted by mtime — no DB round-trip needed for that section /bureau (web/app/bureau/page.tsx) — full hub: - Header with full counts - 7 sections (anchored to homepage counter links): Case reports, Hypotheses, Evidence, Contradictions, Outliers, Witnesses, Recent jobs table — each rendering up to 100 rows - Reports section parses frontmatter from each .md to surface topic + n_hypotheses + n_evidence on the card Runtime fixes batched in: - Poirot: coerce entity_pk via Number() — node-postgres returns BIGINT as string by default; writer's Number.isFinite() rejected it as "person_entity_pk required" (j-edgar-hoover retry path) - Tetlock: write_calibration rationale cap 600 → 1200 chars. Prompt still asks ≤ 600 but a 2× slack beats failing the job on honest analysis. Observed live: Tetlock emitted ~620 chars on H-0003 and the writer rejected the entire calibration. - Case-Writer: Promise.all of 5 queries × max_parallel=2 jobs demanded up to 10 connections against the investigator role's rolconnlimit=4 → "too many connections for role investigator". Sequentialized — the LLM call is the hot path, not these queries. Smoke results visible now on the homepage: - 3 hypotheses (H-0001/2/3) about green fireballs origin - 3 contradictions (R-0001/2/3) about color, geographic confinement, exclusive-green vs multicolored - 2 evidence cards (E-0002/3) Grade B - 3 outliers (G-0001/2/3) — including Taleb's deliberate meteor-shower-camouflage flag - 1 case report at /c/green-fireballs-sandia (Watson 13.4 KB, five-act narrative, fully cited) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 01:41:28 +00:00
interface RecentJob { job_id: string; kind: string; status: string; created_at: string; payload: Record<string, unknown> | null }
const DETECTIVES = [
W3.10: clickable detective tiles + quick-launch form + doc bureau panel Builds on top of W3.9 to turn the homepage Bureau from a read-only dashboard into a working command center. UI improvements (web/components/bureau-snapshot.tsx): - Detective tiles are now <Link>s — each navigates to its primary artefact section in /bureau (Holmes→#hypotheses, Locard→#evidence, Dupin→#contradictions, Schneier→#hypotheses, Poirot→#witnesses, Taleb→#outliers, Tetlock→#hypotheses, Case-Writer→#reports). Hover bg matches the detective's tone color. - <QuickLaunch /> form inserted right under the tiles. New <QuickLaunch /> client component: - Detective dropdown (7 active kinds; evidence_chain not yet exposed here since it needs a doc_id better picked from the doc page). - Single input swaps placeholder + aria-label by kind: question for Holmes, topic for Dupin/Taleb/Case-Writer, hypothesis_id for Schneier/Tetlock, person_id for Poirot. - Submits to POST /api/bureau/launch and redirects to /jobs/[id] via the next.js router. - Loading state ("queueing…") + error display inline. POST /api/bureau/launch (web/app/api/bureau/launch/route.ts): - Same 8-kind validator as the chat tool's request_investigation. - Auth required when Supabase is configured (triggered_by = user:email). - Returns { job_id, kind, detective, status_url, eta_seconds }. DocBureauPanel on /d/[docId] (web/components/doc-bureau-panel.tsx): - Server component inserted between the doc header and AnomalyHighlights. - Surfaces every bureau artefact that touches the doc: · Evidence whose source_page_id starts with docId/p · Hypotheses citing any of those evidence_ids · Contradictions whose chunks[] has any item with this doc_id · Gaps/outliers with scope.doc_id == docId · Case reports whose markdown body references docId (filesystem scan) - Empty state shows "Investigation Bureau — untouched" with a CTA linking back to the homepage to launch the first investigation. - When non-empty, header counts total artefacts + links to /bureau for the full view. Metadata (web/app/layout.tsx): - description rewritten from "Investigative wiki of the US Department of War UAP/UFO archive (war.gov/ufo)" to one that names the bureau + the 8 detectives. Affects SERP previews + social-card defaults. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 02:33:00 +00:00
{ slug: "holmes", name: "Holmes", role: "Hypothesis tournament", anchor: "/bureau#hypotheses", tone: "text-[#7fdbff] border-[#7fdbff] hover:bg-[rgba(127,219,255,0.06)]" },
{ slug: "locard", name: "Locard", role: "Evidence chain", anchor: "/bureau#evidence", tone: "text-[#06d6a0] border-[#06d6a0] hover:bg-[rgba(6,214,160,0.06)]" },
{ slug: "dupin", name: "Dupin", role: "Contradiction scan", anchor: "/bureau#contradictions", tone: "text-[#ff8a4d] border-[#ff8a4d] hover:bg-[rgba(255,138,77,0.06)]" },
{ slug: "schneier", name: "Schneier", role: "Red-team review", anchor: "/bureau#hypotheses", tone: "text-[#ff3344] border-[#ff3344] hover:bg-[rgba(255,51,68,0.06)]" },
{ slug: "poirot", name: "Poirot", role: "Witness analysis", anchor: "/bureau#witnesses", tone: "text-[#9b5de5] border-[#9b5de5] hover:bg-[rgba(155,93,229,0.06)]" },
{ slug: "taleb", name: "Taleb", role: "Outlier hunter", anchor: "/bureau#outliers", tone: "text-[#ffd23f] border-[#ffd23f] hover:bg-[rgba(255,210,63,0.06)]" },
{ slug: "tetlock", name: "Tetlock", role: "Posterior calibration", anchor: "/bureau#hypotheses", tone: "text-[#26d4cc] border-[#26d4cc] hover:bg-[rgba(38,212,204,0.06)]" },
{ slug: "case-writer", name: "Case-Writer", role: "Five-act narrative", anchor: "/bureau#reports", tone: "text-[#e0c080] border-[#e0c080] hover:bg-[rgba(224,192,128,0.06)]" },
W3.9: surface the Investigation Bureau on the homepage + /bureau hub Closes a UX gap the user surfaced: W3.5-3.8 built 8 detectives, 4 new URL endpoints (/jobs/[id], /h/[id], /c/[slug], /api/h/[id]/red-team) and a chat tool, but the homepage was unchanged — the bureau was invisible unless you knew the URL or asked the chat to invoke request_investigation. Homepage (web/app/page.tsx): - Title `▍ war.gov/ufo — Investigative Wiki` → `▍ The Disclosure Bureau` - Subtitle expanded from "Holmes · Poirot · Dupin · Locard" to all 8 detectives (Holmes · Locard · Dupin · Schneier · Poirot · Taleb · Tetlock · Case-Writer) - New `🔎 bureau` topbar link (gold, between graph/stats and batch) - BureauSnapshot inserted right after the header BureauSnapshot (web/components/bureau-snapshot.tsx) — server component: - 8 detective tiles with role labels (each in its tone color) - 6 clickable counters (evidence / hypotheses / contradictions / witnesses / outliers / case reports) — anchor to /bureau#section - 6 "recent artefacts" columns surfacing the last 3-4 of each kind: hypotheses with prior→posterior + band + ↳reviewed_by marker, contradictions with topic + resolution_status, evidence with Grade badge + verbatim quote, outliers with title + scope.kind, witness analyses with canonical_name + credibility + verdict, case reports with slug + link to /c/<slug> - "Recent jobs" strip linking to /jobs/[id] color-coded by status - Reports read from /data/ufo/case/reports/ via fs.readdir + stat, sorted by mtime — no DB round-trip needed for that section /bureau (web/app/bureau/page.tsx) — full hub: - Header with full counts - 7 sections (anchored to homepage counter links): Case reports, Hypotheses, Evidence, Contradictions, Outliers, Witnesses, Recent jobs table — each rendering up to 100 rows - Reports section parses frontmatter from each .md to surface topic + n_hypotheses + n_evidence on the card Runtime fixes batched in: - Poirot: coerce entity_pk via Number() — node-postgres returns BIGINT as string by default; writer's Number.isFinite() rejected it as "person_entity_pk required" (j-edgar-hoover retry path) - Tetlock: write_calibration rationale cap 600 → 1200 chars. Prompt still asks ≤ 600 but a 2× slack beats failing the job on honest analysis. Observed live: Tetlock emitted ~620 chars on H-0003 and the writer rejected the entire calibration. - Case-Writer: Promise.all of 5 queries × max_parallel=2 jobs demanded up to 10 connections against the investigator role's rolconnlimit=4 → "too many connections for role investigator". Sequentialized — the LLM call is the hot path, not these queries. Smoke results visible now on the homepage: - 3 hypotheses (H-0001/2/3) about green fireballs origin - 3 contradictions (R-0001/2/3) about color, geographic confinement, exclusive-green vs multicolored - 2 evidence cards (E-0002/3) Grade B - 3 outliers (G-0001/2/3) — including Taleb's deliberate meteor-shower-camouflage flag - 1 case report at /c/green-fireballs-sandia (Watson 13.4 KB, five-act narrative, fully cited) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 01:41:28 +00:00
] as const;
const BAND_TONE: Record<string, string> = {
high: "text-[#06d6a0]",
medium: "text-[#3fde6a]",
low: "text-[#ffa500]",
speculation: "text-[#ff6ec7]",
};
const GRADE_TONE: Record<string, string> = {
A: "text-[#06d6a0] border-[#06d6a0]",
B: "text-[#3fde6a] border-[#3fde6a]",
C: "text-[#ffa500] border-[#ffa500]",
};
async function loadSnapshot() {
// Counts come from a single round-trip via UNION ALL.
const counts = await pgQuery<CountRow & { k: string }>(
`SELECT 'evidence' AS k, COUNT(*)::text AS c FROM public.evidence
UNION ALL SELECT 'hypotheses', COUNT(*)::text FROM public.hypotheses
UNION ALL SELECT 'contradictions', COUNT(*)::text FROM public.contradictions
UNION ALL SELECT 'witnesses', COUNT(*)::text FROM public.witnesses
UNION ALL SELECT 'gaps', COUNT(*)::text FROM public.gaps
UNION ALL SELECT 'open_hyp', COUNT(*)::text FROM public.hypotheses WHERE status='open'
UNION ALL SELECT 'jobs_running', COUNT(*)::text FROM public.investigation_jobs WHERE status IN ('queued','running')
UNION ALL SELECT 'jobs_complete_24h', COUNT(*)::text FROM public.investigation_jobs WHERE status='complete' AND finished_at > NOW() - INTERVAL '24 hours'`,
).catch(() => [] as Array<CountRow & { k: string }>);
const byKey: Record<string, number> = {};
for (const r of counts) byKey[r.k] = Number(r.c);
const [hyp, ev, ctr, gap, wit, jobs] = await Promise.all([
pgQuery<RecentHypothesis>(
W4: bilingual EN + PT-BR Investigation Bureau (CLAUDE.md §3 contract) User flagged that the bureau was emitting English-only output, violating the project's bilingual rule. Every narrative field now ships in both languages: stored in sibling DB columns + rendered as adjacent markdown sections per CLAUDE.md §3. Migration 0007 (apply as supabase_admin): - public.hypotheses +question_pt_br, +position_pt_br, +argument_for_pt_br, +argument_against_pt_br - public.contradictions +topic_pt_br, +notes_pt_br - public.witnesses +access_to_event_pt_br, +bias_notes_pt_br, +verdict_pt_br - public.gaps +description_pt_br, +suggested_next_move_pt_br - public.evidence: unchanged (verbatim_excerpt stays source-language) - JSONB siblings inside contradictions.chunks + gaps.scope handled at runtime (statement_pt_br, title_pt_br, dominant_model_pt_br, why_surprising_pt_br, what_it_implies_pt_br). Detective prompts (all 7) rewritten with explicit bilingual JSON contract: - Output protocol section names every EN field + its _pt_br sibling - "Bilingual is mandatory" warning in the task instruction - Sentinel skip-states unchanged (NO_HYPOTHESES, NO_CONTRADICTIONS, INSUFFICIENT_TESTIMONY, INSUFFICIENT_HYPOTHESIS, NO_OUTLIERS, NO_NEW_EVIDENCE, INSUFFICIENT_ARTEFACTS) - Schneier: parallel arrays — hidden_assumptions[i] matches hidden_assumptions_pt_br[i], lengths must match - Case-Writer: interleaved §1 (EN) / §1 (PT-BR) per act in the body Writer-side validation (all 7 tools): - Reject INSERT if PT-BR sibling missing when EN field is set - Persist both languages atomically in one INSERT (no half-updates) - Markdown renderers write adjacent EN+PT-BR sections in case files (## Argument for (EN) followed by ## Argumento a favor (PT-BR), etc.) Detective parse layer (all 7 detectives): - Coerce both keys from JSON output - "incomplete_bilingual_*" skip reason when either side missing - Defensive: PT-BR fields trimmed + length-capped same as EN Orchestrator propagates question_pt_br + topic_pt_br through job payload to runHolmes / runCaseWriter, mirroring the chat-tool entry point. Web (UI): - /api/jobs/[id] hydrates _pt_br siblings from pg - job-status-poller HypothesisCard: PT-BR primary, EN in <details> fallback when both exist - ContradictionCard: PT-BR statement primary + secondary EN quote - WitnessCard: PT-BR verdict primary + secondary EN quote, panels in PT - GapCard: PT-BR title/why/implies primary - /bureau hub: SELECTs both columns, renders PT-BR primary - /h/[id]: ArgumentPanel renders PT-BR primary with collapsible EN fallback when both exist - BureauSnapshot homepage: position_pt_br / topic_pt_br / verdict_pt_br primary - DocBureauPanel /d/[doc]: same primary-PT-BR pattern - New web/lib/i18n/pick.ts helper (unused yet by chat/agents — kept for future locale-driven switching when both languages are equally full; current rule is PT-BR-first since the user is brasileiro) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 15:02:59 +00:00
`SELECT hypothesis_id, position, position_pt_br, posterior, confidence_band, reviewed_by
W3.9: surface the Investigation Bureau on the homepage + /bureau hub Closes a UX gap the user surfaced: W3.5-3.8 built 8 detectives, 4 new URL endpoints (/jobs/[id], /h/[id], /c/[slug], /api/h/[id]/red-team) and a chat tool, but the homepage was unchanged — the bureau was invisible unless you knew the URL or asked the chat to invoke request_investigation. Homepage (web/app/page.tsx): - Title `▍ war.gov/ufo — Investigative Wiki` → `▍ The Disclosure Bureau` - Subtitle expanded from "Holmes · Poirot · Dupin · Locard" to all 8 detectives (Holmes · Locard · Dupin · Schneier · Poirot · Taleb · Tetlock · Case-Writer) - New `🔎 bureau` topbar link (gold, between graph/stats and batch) - BureauSnapshot inserted right after the header BureauSnapshot (web/components/bureau-snapshot.tsx) — server component: - 8 detective tiles with role labels (each in its tone color) - 6 clickable counters (evidence / hypotheses / contradictions / witnesses / outliers / case reports) — anchor to /bureau#section - 6 "recent artefacts" columns surfacing the last 3-4 of each kind: hypotheses with prior→posterior + band + ↳reviewed_by marker, contradictions with topic + resolution_status, evidence with Grade badge + verbatim quote, outliers with title + scope.kind, witness analyses with canonical_name + credibility + verdict, case reports with slug + link to /c/<slug> - "Recent jobs" strip linking to /jobs/[id] color-coded by status - Reports read from /data/ufo/case/reports/ via fs.readdir + stat, sorted by mtime — no DB round-trip needed for that section /bureau (web/app/bureau/page.tsx) — full hub: - Header with full counts - 7 sections (anchored to homepage counter links): Case reports, Hypotheses, Evidence, Contradictions, Outliers, Witnesses, Recent jobs table — each rendering up to 100 rows - Reports section parses frontmatter from each .md to surface topic + n_hypotheses + n_evidence on the card Runtime fixes batched in: - Poirot: coerce entity_pk via Number() — node-postgres returns BIGINT as string by default; writer's Number.isFinite() rejected it as "person_entity_pk required" (j-edgar-hoover retry path) - Tetlock: write_calibration rationale cap 600 → 1200 chars. Prompt still asks ≤ 600 but a 2× slack beats failing the job on honest analysis. Observed live: Tetlock emitted ~620 chars on H-0003 and the writer rejected the entire calibration. - Case-Writer: Promise.all of 5 queries × max_parallel=2 jobs demanded up to 10 connections against the investigator role's rolconnlimit=4 → "too many connections for role investigator". Sequentialized — the LLM call is the hot path, not these queries. Smoke results visible now on the homepage: - 3 hypotheses (H-0001/2/3) about green fireballs origin - 3 contradictions (R-0001/2/3) about color, geographic confinement, exclusive-green vs multicolored - 2 evidence cards (E-0002/3) Grade B - 3 outliers (G-0001/2/3) — including Taleb's deliberate meteor-shower-camouflage flag - 1 case report at /c/green-fireballs-sandia (Watson 13.4 KB, five-act narrative, fully cited) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 01:41:28 +00:00
FROM public.hypotheses ORDER BY created_at DESC LIMIT 4`,
).catch(() => []),
pgQuery<RecentEvidence>(
`SELECT evidence_id, grade, verbatim_excerpt, source_page_id, confidence_band
FROM public.evidence ORDER BY created_at DESC LIMIT 3`,
).catch(() => []),
pgQuery<RecentContradiction>(
W4: bilingual EN + PT-BR Investigation Bureau (CLAUDE.md §3 contract) User flagged that the bureau was emitting English-only output, violating the project's bilingual rule. Every narrative field now ships in both languages: stored in sibling DB columns + rendered as adjacent markdown sections per CLAUDE.md §3. Migration 0007 (apply as supabase_admin): - public.hypotheses +question_pt_br, +position_pt_br, +argument_for_pt_br, +argument_against_pt_br - public.contradictions +topic_pt_br, +notes_pt_br - public.witnesses +access_to_event_pt_br, +bias_notes_pt_br, +verdict_pt_br - public.gaps +description_pt_br, +suggested_next_move_pt_br - public.evidence: unchanged (verbatim_excerpt stays source-language) - JSONB siblings inside contradictions.chunks + gaps.scope handled at runtime (statement_pt_br, title_pt_br, dominant_model_pt_br, why_surprising_pt_br, what_it_implies_pt_br). Detective prompts (all 7) rewritten with explicit bilingual JSON contract: - Output protocol section names every EN field + its _pt_br sibling - "Bilingual is mandatory" warning in the task instruction - Sentinel skip-states unchanged (NO_HYPOTHESES, NO_CONTRADICTIONS, INSUFFICIENT_TESTIMONY, INSUFFICIENT_HYPOTHESIS, NO_OUTLIERS, NO_NEW_EVIDENCE, INSUFFICIENT_ARTEFACTS) - Schneier: parallel arrays — hidden_assumptions[i] matches hidden_assumptions_pt_br[i], lengths must match - Case-Writer: interleaved §1 (EN) / §1 (PT-BR) per act in the body Writer-side validation (all 7 tools): - Reject INSERT if PT-BR sibling missing when EN field is set - Persist both languages atomically in one INSERT (no half-updates) - Markdown renderers write adjacent EN+PT-BR sections in case files (## Argument for (EN) followed by ## Argumento a favor (PT-BR), etc.) Detective parse layer (all 7 detectives): - Coerce both keys from JSON output - "incomplete_bilingual_*" skip reason when either side missing - Defensive: PT-BR fields trimmed + length-capped same as EN Orchestrator propagates question_pt_br + topic_pt_br through job payload to runHolmes / runCaseWriter, mirroring the chat-tool entry point. Web (UI): - /api/jobs/[id] hydrates _pt_br siblings from pg - job-status-poller HypothesisCard: PT-BR primary, EN in <details> fallback when both exist - ContradictionCard: PT-BR statement primary + secondary EN quote - WitnessCard: PT-BR verdict primary + secondary EN quote, panels in PT - GapCard: PT-BR title/why/implies primary - /bureau hub: SELECTs both columns, renders PT-BR primary - /h/[id]: ArgumentPanel renders PT-BR primary with collapsible EN fallback when both exist - BureauSnapshot homepage: position_pt_br / topic_pt_br / verdict_pt_br primary - DocBureauPanel /d/[doc]: same primary-PT-BR pattern - New web/lib/i18n/pick.ts helper (unused yet by chat/agents — kept for future locale-driven switching when both languages are equally full; current rule is PT-BR-first since the user is brasileiro) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 15:02:59 +00:00
`SELECT contradiction_id, topic, topic_pt_br, resolution_status
W3.9: surface the Investigation Bureau on the homepage + /bureau hub Closes a UX gap the user surfaced: W3.5-3.8 built 8 detectives, 4 new URL endpoints (/jobs/[id], /h/[id], /c/[slug], /api/h/[id]/red-team) and a chat tool, but the homepage was unchanged — the bureau was invisible unless you knew the URL or asked the chat to invoke request_investigation. Homepage (web/app/page.tsx): - Title `▍ war.gov/ufo — Investigative Wiki` → `▍ The Disclosure Bureau` - Subtitle expanded from "Holmes · Poirot · Dupin · Locard" to all 8 detectives (Holmes · Locard · Dupin · Schneier · Poirot · Taleb · Tetlock · Case-Writer) - New `🔎 bureau` topbar link (gold, between graph/stats and batch) - BureauSnapshot inserted right after the header BureauSnapshot (web/components/bureau-snapshot.tsx) — server component: - 8 detective tiles with role labels (each in its tone color) - 6 clickable counters (evidence / hypotheses / contradictions / witnesses / outliers / case reports) — anchor to /bureau#section - 6 "recent artefacts" columns surfacing the last 3-4 of each kind: hypotheses with prior→posterior + band + ↳reviewed_by marker, contradictions with topic + resolution_status, evidence with Grade badge + verbatim quote, outliers with title + scope.kind, witness analyses with canonical_name + credibility + verdict, case reports with slug + link to /c/<slug> - "Recent jobs" strip linking to /jobs/[id] color-coded by status - Reports read from /data/ufo/case/reports/ via fs.readdir + stat, sorted by mtime — no DB round-trip needed for that section /bureau (web/app/bureau/page.tsx) — full hub: - Header with full counts - 7 sections (anchored to homepage counter links): Case reports, Hypotheses, Evidence, Contradictions, Outliers, Witnesses, Recent jobs table — each rendering up to 100 rows - Reports section parses frontmatter from each .md to surface topic + n_hypotheses + n_evidence on the card Runtime fixes batched in: - Poirot: coerce entity_pk via Number() — node-postgres returns BIGINT as string by default; writer's Number.isFinite() rejected it as "person_entity_pk required" (j-edgar-hoover retry path) - Tetlock: write_calibration rationale cap 600 → 1200 chars. Prompt still asks ≤ 600 but a 2× slack beats failing the job on honest analysis. Observed live: Tetlock emitted ~620 chars on H-0003 and the writer rejected the entire calibration. - Case-Writer: Promise.all of 5 queries × max_parallel=2 jobs demanded up to 10 connections against the investigator role's rolconnlimit=4 → "too many connections for role investigator". Sequentialized — the LLM call is the hot path, not these queries. Smoke results visible now on the homepage: - 3 hypotheses (H-0001/2/3) about green fireballs origin - 3 contradictions (R-0001/2/3) about color, geographic confinement, exclusive-green vs multicolored - 2 evidence cards (E-0002/3) Grade B - 3 outliers (G-0001/2/3) — including Taleb's deliberate meteor-shower-camouflage flag - 1 case report at /c/green-fireballs-sandia (Watson 13.4 KB, five-act narrative, fully cited) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 01:41:28 +00:00
FROM public.contradictions ORDER BY created_at DESC LIMIT 3`,
).catch(() => []),
pgQuery<RecentGap>(
W4: bilingual EN + PT-BR Investigation Bureau (CLAUDE.md §3 contract) User flagged that the bureau was emitting English-only output, violating the project's bilingual rule. Every narrative field now ships in both languages: stored in sibling DB columns + rendered as adjacent markdown sections per CLAUDE.md §3. Migration 0007 (apply as supabase_admin): - public.hypotheses +question_pt_br, +position_pt_br, +argument_for_pt_br, +argument_against_pt_br - public.contradictions +topic_pt_br, +notes_pt_br - public.witnesses +access_to_event_pt_br, +bias_notes_pt_br, +verdict_pt_br - public.gaps +description_pt_br, +suggested_next_move_pt_br - public.evidence: unchanged (verbatim_excerpt stays source-language) - JSONB siblings inside contradictions.chunks + gaps.scope handled at runtime (statement_pt_br, title_pt_br, dominant_model_pt_br, why_surprising_pt_br, what_it_implies_pt_br). Detective prompts (all 7) rewritten with explicit bilingual JSON contract: - Output protocol section names every EN field + its _pt_br sibling - "Bilingual is mandatory" warning in the task instruction - Sentinel skip-states unchanged (NO_HYPOTHESES, NO_CONTRADICTIONS, INSUFFICIENT_TESTIMONY, INSUFFICIENT_HYPOTHESIS, NO_OUTLIERS, NO_NEW_EVIDENCE, INSUFFICIENT_ARTEFACTS) - Schneier: parallel arrays — hidden_assumptions[i] matches hidden_assumptions_pt_br[i], lengths must match - Case-Writer: interleaved §1 (EN) / §1 (PT-BR) per act in the body Writer-side validation (all 7 tools): - Reject INSERT if PT-BR sibling missing when EN field is set - Persist both languages atomically in one INSERT (no half-updates) - Markdown renderers write adjacent EN+PT-BR sections in case files (## Argument for (EN) followed by ## Argumento a favor (PT-BR), etc.) Detective parse layer (all 7 detectives): - Coerce both keys from JSON output - "incomplete_bilingual_*" skip reason when either side missing - Defensive: PT-BR fields trimmed + length-capped same as EN Orchestrator propagates question_pt_br + topic_pt_br through job payload to runHolmes / runCaseWriter, mirroring the chat-tool entry point. Web (UI): - /api/jobs/[id] hydrates _pt_br siblings from pg - job-status-poller HypothesisCard: PT-BR primary, EN in <details> fallback when both exist - ContradictionCard: PT-BR statement primary + secondary EN quote - WitnessCard: PT-BR verdict primary + secondary EN quote, panels in PT - GapCard: PT-BR title/why/implies primary - /bureau hub: SELECTs both columns, renders PT-BR primary - /h/[id]: ArgumentPanel renders PT-BR primary with collapsible EN fallback when both exist - BureauSnapshot homepage: position_pt_br / topic_pt_br / verdict_pt_br primary - DocBureauPanel /d/[doc]: same primary-PT-BR pattern - New web/lib/i18n/pick.ts helper (unused yet by chat/agents — kept for future locale-driven switching when both languages are equally full; current rule is PT-BR-first since the user is brasileiro) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 15:02:59 +00:00
`SELECT gap_id, description, description_pt_br, scope
FROM public.gaps ORDER BY created_at DESC LIMIT 3`,
W3.9: surface the Investigation Bureau on the homepage + /bureau hub Closes a UX gap the user surfaced: W3.5-3.8 built 8 detectives, 4 new URL endpoints (/jobs/[id], /h/[id], /c/[slug], /api/h/[id]/red-team) and a chat tool, but the homepage was unchanged — the bureau was invisible unless you knew the URL or asked the chat to invoke request_investigation. Homepage (web/app/page.tsx): - Title `▍ war.gov/ufo — Investigative Wiki` → `▍ The Disclosure Bureau` - Subtitle expanded from "Holmes · Poirot · Dupin · Locard" to all 8 detectives (Holmes · Locard · Dupin · Schneier · Poirot · Taleb · Tetlock · Case-Writer) - New `🔎 bureau` topbar link (gold, between graph/stats and batch) - BureauSnapshot inserted right after the header BureauSnapshot (web/components/bureau-snapshot.tsx) — server component: - 8 detective tiles with role labels (each in its tone color) - 6 clickable counters (evidence / hypotheses / contradictions / witnesses / outliers / case reports) — anchor to /bureau#section - 6 "recent artefacts" columns surfacing the last 3-4 of each kind: hypotheses with prior→posterior + band + ↳reviewed_by marker, contradictions with topic + resolution_status, evidence with Grade badge + verbatim quote, outliers with title + scope.kind, witness analyses with canonical_name + credibility + verdict, case reports with slug + link to /c/<slug> - "Recent jobs" strip linking to /jobs/[id] color-coded by status - Reports read from /data/ufo/case/reports/ via fs.readdir + stat, sorted by mtime — no DB round-trip needed for that section /bureau (web/app/bureau/page.tsx) — full hub: - Header with full counts - 7 sections (anchored to homepage counter links): Case reports, Hypotheses, Evidence, Contradictions, Outliers, Witnesses, Recent jobs table — each rendering up to 100 rows - Reports section parses frontmatter from each .md to surface topic + n_hypotheses + n_evidence on the card Runtime fixes batched in: - Poirot: coerce entity_pk via Number() — node-postgres returns BIGINT as string by default; writer's Number.isFinite() rejected it as "person_entity_pk required" (j-edgar-hoover retry path) - Tetlock: write_calibration rationale cap 600 → 1200 chars. Prompt still asks ≤ 600 but a 2× slack beats failing the job on honest analysis. Observed live: Tetlock emitted ~620 chars on H-0003 and the writer rejected the entire calibration. - Case-Writer: Promise.all of 5 queries × max_parallel=2 jobs demanded up to 10 connections against the investigator role's rolconnlimit=4 → "too many connections for role investigator". Sequentialized — the LLM call is the hot path, not these queries. Smoke results visible now on the homepage: - 3 hypotheses (H-0001/2/3) about green fireballs origin - 3 contradictions (R-0001/2/3) about color, geographic confinement, exclusive-green vs multicolored - 2 evidence cards (E-0002/3) Grade B - 3 outliers (G-0001/2/3) — including Taleb's deliberate meteor-shower-camouflage flag - 1 case report at /c/green-fireballs-sandia (Watson 13.4 KB, five-act narrative, fully cited) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 01:41:28 +00:00
).catch(() => []),
pgQuery<RecentWitness>(
W4: bilingual EN + PT-BR Investigation Bureau (CLAUDE.md §3 contract) User flagged that the bureau was emitting English-only output, violating the project's bilingual rule. Every narrative field now ships in both languages: stored in sibling DB columns + rendered as adjacent markdown sections per CLAUDE.md §3. Migration 0007 (apply as supabase_admin): - public.hypotheses +question_pt_br, +position_pt_br, +argument_for_pt_br, +argument_against_pt_br - public.contradictions +topic_pt_br, +notes_pt_br - public.witnesses +access_to_event_pt_br, +bias_notes_pt_br, +verdict_pt_br - public.gaps +description_pt_br, +suggested_next_move_pt_br - public.evidence: unchanged (verbatim_excerpt stays source-language) - JSONB siblings inside contradictions.chunks + gaps.scope handled at runtime (statement_pt_br, title_pt_br, dominant_model_pt_br, why_surprising_pt_br, what_it_implies_pt_br). Detective prompts (all 7) rewritten with explicit bilingual JSON contract: - Output protocol section names every EN field + its _pt_br sibling - "Bilingual is mandatory" warning in the task instruction - Sentinel skip-states unchanged (NO_HYPOTHESES, NO_CONTRADICTIONS, INSUFFICIENT_TESTIMONY, INSUFFICIENT_HYPOTHESIS, NO_OUTLIERS, NO_NEW_EVIDENCE, INSUFFICIENT_ARTEFACTS) - Schneier: parallel arrays — hidden_assumptions[i] matches hidden_assumptions_pt_br[i], lengths must match - Case-Writer: interleaved §1 (EN) / §1 (PT-BR) per act in the body Writer-side validation (all 7 tools): - Reject INSERT if PT-BR sibling missing when EN field is set - Persist both languages atomically in one INSERT (no half-updates) - Markdown renderers write adjacent EN+PT-BR sections in case files (## Argument for (EN) followed by ## Argumento a favor (PT-BR), etc.) Detective parse layer (all 7 detectives): - Coerce both keys from JSON output - "incomplete_bilingual_*" skip reason when either side missing - Defensive: PT-BR fields trimmed + length-capped same as EN Orchestrator propagates question_pt_br + topic_pt_br through job payload to runHolmes / runCaseWriter, mirroring the chat-tool entry point. Web (UI): - /api/jobs/[id] hydrates _pt_br siblings from pg - job-status-poller HypothesisCard: PT-BR primary, EN in <details> fallback when both exist - ContradictionCard: PT-BR statement primary + secondary EN quote - WitnessCard: PT-BR verdict primary + secondary EN quote, panels in PT - GapCard: PT-BR title/why/implies primary - /bureau hub: SELECTs both columns, renders PT-BR primary - /h/[id]: ArgumentPanel renders PT-BR primary with collapsible EN fallback when both exist - BureauSnapshot homepage: position_pt_br / topic_pt_br / verdict_pt_br primary - DocBureauPanel /d/[doc]: same primary-PT-BR pattern - New web/lib/i18n/pick.ts helper (unused yet by chat/agents — kept for future locale-driven switching when both languages are equally full; current rule is PT-BR-first since the user is brasileiro) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 15:02:59 +00:00
`SELECT w.witness_id, e.canonical_name, w.credibility, w.verdict, w.verdict_pt_br
W3.9: surface the Investigation Bureau on the homepage + /bureau hub Closes a UX gap the user surfaced: W3.5-3.8 built 8 detectives, 4 new URL endpoints (/jobs/[id], /h/[id], /c/[slug], /api/h/[id]/red-team) and a chat tool, but the homepage was unchanged — the bureau was invisible unless you knew the URL or asked the chat to invoke request_investigation. Homepage (web/app/page.tsx): - Title `▍ war.gov/ufo — Investigative Wiki` → `▍ The Disclosure Bureau` - Subtitle expanded from "Holmes · Poirot · Dupin · Locard" to all 8 detectives (Holmes · Locard · Dupin · Schneier · Poirot · Taleb · Tetlock · Case-Writer) - New `🔎 bureau` topbar link (gold, between graph/stats and batch) - BureauSnapshot inserted right after the header BureauSnapshot (web/components/bureau-snapshot.tsx) — server component: - 8 detective tiles with role labels (each in its tone color) - 6 clickable counters (evidence / hypotheses / contradictions / witnesses / outliers / case reports) — anchor to /bureau#section - 6 "recent artefacts" columns surfacing the last 3-4 of each kind: hypotheses with prior→posterior + band + ↳reviewed_by marker, contradictions with topic + resolution_status, evidence with Grade badge + verbatim quote, outliers with title + scope.kind, witness analyses with canonical_name + credibility + verdict, case reports with slug + link to /c/<slug> - "Recent jobs" strip linking to /jobs/[id] color-coded by status - Reports read from /data/ufo/case/reports/ via fs.readdir + stat, sorted by mtime — no DB round-trip needed for that section /bureau (web/app/bureau/page.tsx) — full hub: - Header with full counts - 7 sections (anchored to homepage counter links): Case reports, Hypotheses, Evidence, Contradictions, Outliers, Witnesses, Recent jobs table — each rendering up to 100 rows - Reports section parses frontmatter from each .md to surface topic + n_hypotheses + n_evidence on the card Runtime fixes batched in: - Poirot: coerce entity_pk via Number() — node-postgres returns BIGINT as string by default; writer's Number.isFinite() rejected it as "person_entity_pk required" (j-edgar-hoover retry path) - Tetlock: write_calibration rationale cap 600 → 1200 chars. Prompt still asks ≤ 600 but a 2× slack beats failing the job on honest analysis. Observed live: Tetlock emitted ~620 chars on H-0003 and the writer rejected the entire calibration. - Case-Writer: Promise.all of 5 queries × max_parallel=2 jobs demanded up to 10 connections against the investigator role's rolconnlimit=4 → "too many connections for role investigator". Sequentialized — the LLM call is the hot path, not these queries. Smoke results visible now on the homepage: - 3 hypotheses (H-0001/2/3) about green fireballs origin - 3 contradictions (R-0001/2/3) about color, geographic confinement, exclusive-green vs multicolored - 2 evidence cards (E-0002/3) Grade B - 3 outliers (G-0001/2/3) — including Taleb's deliberate meteor-shower-camouflage flag - 1 case report at /c/green-fireballs-sandia (Watson 13.4 KB, five-act narrative, fully cited) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 01:41:28 +00:00
FROM public.witnesses w
LEFT JOIN public.entities e ON e.entity_pk = w.person_entity_pk
ORDER BY w.created_at DESC LIMIT 3`,
).catch(() => []),
pgQuery<RecentJob>(
`SELECT job_id, kind, status, created_at, payload
FROM public.investigation_jobs ORDER BY created_at DESC LIMIT 6`,
).catch(() => []),
]);
// Case reports live on disk. Read the reports dir.
const { readdir, stat } = await import("node:fs/promises");
const path = await import("node:path");
const reportsDir = path.join(process.env.CASE_ROOT || "/data/ufo/case", "reports");
let reports: Array<{ slug: string; mtimeMs: number }> = [];
try {
const files = await readdir(reportsDir);
const items = await Promise.all(
files.filter((f) => f.endsWith(".md")).map(async (f) => {
const st = await stat(path.join(reportsDir, f));
return { slug: f.replace(/\.md$/, ""), mtimeMs: st.mtimeMs };
}),
);
reports = items.sort((a, b) => b.mtimeMs - a.mtimeMs).slice(0, 4);
} catch { /* no reports yet — fine */ }
return { counts: byKey, hyp, ev, ctr, gap, wit, jobs, reports };
}
export async function BureauSnapshot() {
const snap = await loadSnapshot().catch(() => null);
if (!snap) {
return (
<section className="mb-10 border border-dashed border-[rgba(127,219,255,0.18)] rounded-lg p-4 text-[11px] text-[#9aa6b8] font-mono">
Investigation Bureau snapshot unavailable (db unreachable).
</section>
);
}
const { counts, hyp, ev, ctr, gap, wit, jobs, reports } = snap;
const totalArtefacts = (counts.evidence ?? 0) + (counts.hypotheses ?? 0) + (counts.contradictions ?? 0)
+ (counts.witnesses ?? 0) + (counts.gaps ?? 0) + reports.length;
const running = counts.jobs_running ?? 0;
return (
<section className="mb-10">
{/* Section heading */}
<div className="flex items-baseline justify-between mb-4 flex-wrap gap-2">
<div>
<h2 className="font-mono text-lg text-[#e7ecf3]">
<span className="text-[#7fdbff]"></span> The Investigation Bureau
</h2>
<p className="text-[11px] text-[#5a6678] font-mono mt-1">
8 AI detectives · {totalArtefacts} artefacts in the case folder
{running > 0 && <span className="text-[#ffd23f]"> · {running} job{running === 1 ? "" : "s"} in progress</span>}
</p>
</div>
<Link
href="/bureau"
className="font-mono text-xs px-3 py-1.5 border border-[#e0c080] text-[#e0c080] hover:bg-[rgba(224,192,128,0.10)] rounded"
>
open the bureau
</Link>
</div>
W3.10: clickable detective tiles + quick-launch form + doc bureau panel Builds on top of W3.9 to turn the homepage Bureau from a read-only dashboard into a working command center. UI improvements (web/components/bureau-snapshot.tsx): - Detective tiles are now <Link>s — each navigates to its primary artefact section in /bureau (Holmes→#hypotheses, Locard→#evidence, Dupin→#contradictions, Schneier→#hypotheses, Poirot→#witnesses, Taleb→#outliers, Tetlock→#hypotheses, Case-Writer→#reports). Hover bg matches the detective's tone color. - <QuickLaunch /> form inserted right under the tiles. New <QuickLaunch /> client component: - Detective dropdown (7 active kinds; evidence_chain not yet exposed here since it needs a doc_id better picked from the doc page). - Single input swaps placeholder + aria-label by kind: question for Holmes, topic for Dupin/Taleb/Case-Writer, hypothesis_id for Schneier/Tetlock, person_id for Poirot. - Submits to POST /api/bureau/launch and redirects to /jobs/[id] via the next.js router. - Loading state ("queueing…") + error display inline. POST /api/bureau/launch (web/app/api/bureau/launch/route.ts): - Same 8-kind validator as the chat tool's request_investigation. - Auth required when Supabase is configured (triggered_by = user:email). - Returns { job_id, kind, detective, status_url, eta_seconds }. DocBureauPanel on /d/[docId] (web/components/doc-bureau-panel.tsx): - Server component inserted between the doc header and AnomalyHighlights. - Surfaces every bureau artefact that touches the doc: · Evidence whose source_page_id starts with docId/p · Hypotheses citing any of those evidence_ids · Contradictions whose chunks[] has any item with this doc_id · Gaps/outliers with scope.doc_id == docId · Case reports whose markdown body references docId (filesystem scan) - Empty state shows "Investigation Bureau — untouched" with a CTA linking back to the homepage to launch the first investigation. - When non-empty, header counts total artefacts + links to /bureau for the full view. Metadata (web/app/layout.tsx): - description rewritten from "Investigative wiki of the US Department of War UAP/UFO archive (war.gov/ufo)" to one that names the bureau + the 8 detectives. Affects SERP previews + social-card defaults. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 02:33:00 +00:00
{/* Detective tiles — clickable to their artefact section in /bureau */}
W3.9: surface the Investigation Bureau on the homepage + /bureau hub Closes a UX gap the user surfaced: W3.5-3.8 built 8 detectives, 4 new URL endpoints (/jobs/[id], /h/[id], /c/[slug], /api/h/[id]/red-team) and a chat tool, but the homepage was unchanged — the bureau was invisible unless you knew the URL or asked the chat to invoke request_investigation. Homepage (web/app/page.tsx): - Title `▍ war.gov/ufo — Investigative Wiki` → `▍ The Disclosure Bureau` - Subtitle expanded from "Holmes · Poirot · Dupin · Locard" to all 8 detectives (Holmes · Locard · Dupin · Schneier · Poirot · Taleb · Tetlock · Case-Writer) - New `🔎 bureau` topbar link (gold, between graph/stats and batch) - BureauSnapshot inserted right after the header BureauSnapshot (web/components/bureau-snapshot.tsx) — server component: - 8 detective tiles with role labels (each in its tone color) - 6 clickable counters (evidence / hypotheses / contradictions / witnesses / outliers / case reports) — anchor to /bureau#section - 6 "recent artefacts" columns surfacing the last 3-4 of each kind: hypotheses with prior→posterior + band + ↳reviewed_by marker, contradictions with topic + resolution_status, evidence with Grade badge + verbatim quote, outliers with title + scope.kind, witness analyses with canonical_name + credibility + verdict, case reports with slug + link to /c/<slug> - "Recent jobs" strip linking to /jobs/[id] color-coded by status - Reports read from /data/ufo/case/reports/ via fs.readdir + stat, sorted by mtime — no DB round-trip needed for that section /bureau (web/app/bureau/page.tsx) — full hub: - Header with full counts - 7 sections (anchored to homepage counter links): Case reports, Hypotheses, Evidence, Contradictions, Outliers, Witnesses, Recent jobs table — each rendering up to 100 rows - Reports section parses frontmatter from each .md to surface topic + n_hypotheses + n_evidence on the card Runtime fixes batched in: - Poirot: coerce entity_pk via Number() — node-postgres returns BIGINT as string by default; writer's Number.isFinite() rejected it as "person_entity_pk required" (j-edgar-hoover retry path) - Tetlock: write_calibration rationale cap 600 → 1200 chars. Prompt still asks ≤ 600 but a 2× slack beats failing the job on honest analysis. Observed live: Tetlock emitted ~620 chars on H-0003 and the writer rejected the entire calibration. - Case-Writer: Promise.all of 5 queries × max_parallel=2 jobs demanded up to 10 connections against the investigator role's rolconnlimit=4 → "too many connections for role investigator". Sequentialized — the LLM call is the hot path, not these queries. Smoke results visible now on the homepage: - 3 hypotheses (H-0001/2/3) about green fireballs origin - 3 contradictions (R-0001/2/3) about color, geographic confinement, exclusive-green vs multicolored - 2 evidence cards (E-0002/3) Grade B - 3 outliers (G-0001/2/3) — including Taleb's deliberate meteor-shower-camouflage flag - 1 case report at /c/green-fireballs-sandia (Watson 13.4 KB, five-act narrative, fully cited) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 01:41:28 +00:00
<div className="grid grid-cols-2 md:grid-cols-4 gap-2 mb-5">
{DETECTIVES.map((d) => (
W3.10: clickable detective tiles + quick-launch form + doc bureau panel Builds on top of W3.9 to turn the homepage Bureau from a read-only dashboard into a working command center. UI improvements (web/components/bureau-snapshot.tsx): - Detective tiles are now <Link>s — each navigates to its primary artefact section in /bureau (Holmes→#hypotheses, Locard→#evidence, Dupin→#contradictions, Schneier→#hypotheses, Poirot→#witnesses, Taleb→#outliers, Tetlock→#hypotheses, Case-Writer→#reports). Hover bg matches the detective's tone color. - <QuickLaunch /> form inserted right under the tiles. New <QuickLaunch /> client component: - Detective dropdown (7 active kinds; evidence_chain not yet exposed here since it needs a doc_id better picked from the doc page). - Single input swaps placeholder + aria-label by kind: question for Holmes, topic for Dupin/Taleb/Case-Writer, hypothesis_id for Schneier/Tetlock, person_id for Poirot. - Submits to POST /api/bureau/launch and redirects to /jobs/[id] via the next.js router. - Loading state ("queueing…") + error display inline. POST /api/bureau/launch (web/app/api/bureau/launch/route.ts): - Same 8-kind validator as the chat tool's request_investigation. - Auth required when Supabase is configured (triggered_by = user:email). - Returns { job_id, kind, detective, status_url, eta_seconds }. DocBureauPanel on /d/[docId] (web/components/doc-bureau-panel.tsx): - Server component inserted between the doc header and AnomalyHighlights. - Surfaces every bureau artefact that touches the doc: · Evidence whose source_page_id starts with docId/p · Hypotheses citing any of those evidence_ids · Contradictions whose chunks[] has any item with this doc_id · Gaps/outliers with scope.doc_id == docId · Case reports whose markdown body references docId (filesystem scan) - Empty state shows "Investigation Bureau — untouched" with a CTA linking back to the homepage to launch the first investigation. - When non-empty, header counts total artefacts + links to /bureau for the full view. Metadata (web/app/layout.tsx): - description rewritten from "Investigative wiki of the US Department of War UAP/UFO archive (war.gov/ufo)" to one that names the bureau + the 8 detectives. Affects SERP previews + social-card defaults. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 02:33:00 +00:00
<Link
W3.9: surface the Investigation Bureau on the homepage + /bureau hub Closes a UX gap the user surfaced: W3.5-3.8 built 8 detectives, 4 new URL endpoints (/jobs/[id], /h/[id], /c/[slug], /api/h/[id]/red-team) and a chat tool, but the homepage was unchanged — the bureau was invisible unless you knew the URL or asked the chat to invoke request_investigation. Homepage (web/app/page.tsx): - Title `▍ war.gov/ufo — Investigative Wiki` → `▍ The Disclosure Bureau` - Subtitle expanded from "Holmes · Poirot · Dupin · Locard" to all 8 detectives (Holmes · Locard · Dupin · Schneier · Poirot · Taleb · Tetlock · Case-Writer) - New `🔎 bureau` topbar link (gold, between graph/stats and batch) - BureauSnapshot inserted right after the header BureauSnapshot (web/components/bureau-snapshot.tsx) — server component: - 8 detective tiles with role labels (each in its tone color) - 6 clickable counters (evidence / hypotheses / contradictions / witnesses / outliers / case reports) — anchor to /bureau#section - 6 "recent artefacts" columns surfacing the last 3-4 of each kind: hypotheses with prior→posterior + band + ↳reviewed_by marker, contradictions with topic + resolution_status, evidence with Grade badge + verbatim quote, outliers with title + scope.kind, witness analyses with canonical_name + credibility + verdict, case reports with slug + link to /c/<slug> - "Recent jobs" strip linking to /jobs/[id] color-coded by status - Reports read from /data/ufo/case/reports/ via fs.readdir + stat, sorted by mtime — no DB round-trip needed for that section /bureau (web/app/bureau/page.tsx) — full hub: - Header with full counts - 7 sections (anchored to homepage counter links): Case reports, Hypotheses, Evidence, Contradictions, Outliers, Witnesses, Recent jobs table — each rendering up to 100 rows - Reports section parses frontmatter from each .md to surface topic + n_hypotheses + n_evidence on the card Runtime fixes batched in: - Poirot: coerce entity_pk via Number() — node-postgres returns BIGINT as string by default; writer's Number.isFinite() rejected it as "person_entity_pk required" (j-edgar-hoover retry path) - Tetlock: write_calibration rationale cap 600 → 1200 chars. Prompt still asks ≤ 600 but a 2× slack beats failing the job on honest analysis. Observed live: Tetlock emitted ~620 chars on H-0003 and the writer rejected the entire calibration. - Case-Writer: Promise.all of 5 queries × max_parallel=2 jobs demanded up to 10 connections against the investigator role's rolconnlimit=4 → "too many connections for role investigator". Sequentialized — the LLM call is the hot path, not these queries. Smoke results visible now on the homepage: - 3 hypotheses (H-0001/2/3) about green fireballs origin - 3 contradictions (R-0001/2/3) about color, geographic confinement, exclusive-green vs multicolored - 2 evidence cards (E-0002/3) Grade B - 3 outliers (G-0001/2/3) — including Taleb's deliberate meteor-shower-camouflage flag - 1 case report at /c/green-fireballs-sandia (Watson 13.4 KB, five-act narrative, fully cited) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 01:41:28 +00:00
key={d.slug}
W3.10: clickable detective tiles + quick-launch form + doc bureau panel Builds on top of W3.9 to turn the homepage Bureau from a read-only dashboard into a working command center. UI improvements (web/components/bureau-snapshot.tsx): - Detective tiles are now <Link>s — each navigates to its primary artefact section in /bureau (Holmes→#hypotheses, Locard→#evidence, Dupin→#contradictions, Schneier→#hypotheses, Poirot→#witnesses, Taleb→#outliers, Tetlock→#hypotheses, Case-Writer→#reports). Hover bg matches the detective's tone color. - <QuickLaunch /> form inserted right under the tiles. New <QuickLaunch /> client component: - Detective dropdown (7 active kinds; evidence_chain not yet exposed here since it needs a doc_id better picked from the doc page). - Single input swaps placeholder + aria-label by kind: question for Holmes, topic for Dupin/Taleb/Case-Writer, hypothesis_id for Schneier/Tetlock, person_id for Poirot. - Submits to POST /api/bureau/launch and redirects to /jobs/[id] via the next.js router. - Loading state ("queueing…") + error display inline. POST /api/bureau/launch (web/app/api/bureau/launch/route.ts): - Same 8-kind validator as the chat tool's request_investigation. - Auth required when Supabase is configured (triggered_by = user:email). - Returns { job_id, kind, detective, status_url, eta_seconds }. DocBureauPanel on /d/[docId] (web/components/doc-bureau-panel.tsx): - Server component inserted between the doc header and AnomalyHighlights. - Surfaces every bureau artefact that touches the doc: · Evidence whose source_page_id starts with docId/p · Hypotheses citing any of those evidence_ids · Contradictions whose chunks[] has any item with this doc_id · Gaps/outliers with scope.doc_id == docId · Case reports whose markdown body references docId (filesystem scan) - Empty state shows "Investigation Bureau — untouched" with a CTA linking back to the homepage to launch the first investigation. - When non-empty, header counts total artefacts + links to /bureau for the full view. Metadata (web/app/layout.tsx): - description rewritten from "Investigative wiki of the US Department of War UAP/UFO archive (war.gov/ufo)" to one that names the bureau + the 8 detectives. Affects SERP previews + social-card defaults. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 02:33:00 +00:00
href={d.anchor}
className={`block border ${d.tone} bg-[#0d1220] rounded p-2 text-[11px] font-mono leading-snug transition-colors`}
title={`See ${d.role} artefacts →`}
W3.9: surface the Investigation Bureau on the homepage + /bureau hub Closes a UX gap the user surfaced: W3.5-3.8 built 8 detectives, 4 new URL endpoints (/jobs/[id], /h/[id], /c/[slug], /api/h/[id]/red-team) and a chat tool, but the homepage was unchanged — the bureau was invisible unless you knew the URL or asked the chat to invoke request_investigation. Homepage (web/app/page.tsx): - Title `▍ war.gov/ufo — Investigative Wiki` → `▍ The Disclosure Bureau` - Subtitle expanded from "Holmes · Poirot · Dupin · Locard" to all 8 detectives (Holmes · Locard · Dupin · Schneier · Poirot · Taleb · Tetlock · Case-Writer) - New `🔎 bureau` topbar link (gold, between graph/stats and batch) - BureauSnapshot inserted right after the header BureauSnapshot (web/components/bureau-snapshot.tsx) — server component: - 8 detective tiles with role labels (each in its tone color) - 6 clickable counters (evidence / hypotheses / contradictions / witnesses / outliers / case reports) — anchor to /bureau#section - 6 "recent artefacts" columns surfacing the last 3-4 of each kind: hypotheses with prior→posterior + band + ↳reviewed_by marker, contradictions with topic + resolution_status, evidence with Grade badge + verbatim quote, outliers with title + scope.kind, witness analyses with canonical_name + credibility + verdict, case reports with slug + link to /c/<slug> - "Recent jobs" strip linking to /jobs/[id] color-coded by status - Reports read from /data/ufo/case/reports/ via fs.readdir + stat, sorted by mtime — no DB round-trip needed for that section /bureau (web/app/bureau/page.tsx) — full hub: - Header with full counts - 7 sections (anchored to homepage counter links): Case reports, Hypotheses, Evidence, Contradictions, Outliers, Witnesses, Recent jobs table — each rendering up to 100 rows - Reports section parses frontmatter from each .md to surface topic + n_hypotheses + n_evidence on the card Runtime fixes batched in: - Poirot: coerce entity_pk via Number() — node-postgres returns BIGINT as string by default; writer's Number.isFinite() rejected it as "person_entity_pk required" (j-edgar-hoover retry path) - Tetlock: write_calibration rationale cap 600 → 1200 chars. Prompt still asks ≤ 600 but a 2× slack beats failing the job on honest analysis. Observed live: Tetlock emitted ~620 chars on H-0003 and the writer rejected the entire calibration. - Case-Writer: Promise.all of 5 queries × max_parallel=2 jobs demanded up to 10 connections against the investigator role's rolconnlimit=4 → "too many connections for role investigator". Sequentialized — the LLM call is the hot path, not these queries. Smoke results visible now on the homepage: - 3 hypotheses (H-0001/2/3) about green fireballs origin - 3 contradictions (R-0001/2/3) about color, geographic confinement, exclusive-green vs multicolored - 2 evidence cards (E-0002/3) Grade B - 3 outliers (G-0001/2/3) — including Taleb's deliberate meteor-shower-camouflage flag - 1 case report at /c/green-fireballs-sandia (Watson 13.4 KB, five-act narrative, fully cited) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 01:41:28 +00:00
>
<div className="flex items-baseline justify-between">
<span className="font-semibold">{d.name}</span>
<span className="text-[9px] text-[#5a6678] uppercase">{d.slug}</span>
</div>
<div className="text-[10px] text-[#9aa6b8] mt-0.5">{d.role}</div>
W3.10: clickable detective tiles + quick-launch form + doc bureau panel Builds on top of W3.9 to turn the homepage Bureau from a read-only dashboard into a working command center. UI improvements (web/components/bureau-snapshot.tsx): - Detective tiles are now <Link>s — each navigates to its primary artefact section in /bureau (Holmes→#hypotheses, Locard→#evidence, Dupin→#contradictions, Schneier→#hypotheses, Poirot→#witnesses, Taleb→#outliers, Tetlock→#hypotheses, Case-Writer→#reports). Hover bg matches the detective's tone color. - <QuickLaunch /> form inserted right under the tiles. New <QuickLaunch /> client component: - Detective dropdown (7 active kinds; evidence_chain not yet exposed here since it needs a doc_id better picked from the doc page). - Single input swaps placeholder + aria-label by kind: question for Holmes, topic for Dupin/Taleb/Case-Writer, hypothesis_id for Schneier/Tetlock, person_id for Poirot. - Submits to POST /api/bureau/launch and redirects to /jobs/[id] via the next.js router. - Loading state ("queueing…") + error display inline. POST /api/bureau/launch (web/app/api/bureau/launch/route.ts): - Same 8-kind validator as the chat tool's request_investigation. - Auth required when Supabase is configured (triggered_by = user:email). - Returns { job_id, kind, detective, status_url, eta_seconds }. DocBureauPanel on /d/[docId] (web/components/doc-bureau-panel.tsx): - Server component inserted between the doc header and AnomalyHighlights. - Surfaces every bureau artefact that touches the doc: · Evidence whose source_page_id starts with docId/p · Hypotheses citing any of those evidence_ids · Contradictions whose chunks[] has any item with this doc_id · Gaps/outliers with scope.doc_id == docId · Case reports whose markdown body references docId (filesystem scan) - Empty state shows "Investigation Bureau — untouched" with a CTA linking back to the homepage to launch the first investigation. - When non-empty, header counts total artefacts + links to /bureau for the full view. Metadata (web/app/layout.tsx): - description rewritten from "Investigative wiki of the US Department of War UAP/UFO archive (war.gov/ufo)" to one that names the bureau + the 8 detectives. Affects SERP previews + social-card defaults. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 02:33:00 +00:00
</Link>
W3.9: surface the Investigation Bureau on the homepage + /bureau hub Closes a UX gap the user surfaced: W3.5-3.8 built 8 detectives, 4 new URL endpoints (/jobs/[id], /h/[id], /c/[slug], /api/h/[id]/red-team) and a chat tool, but the homepage was unchanged — the bureau was invisible unless you knew the URL or asked the chat to invoke request_investigation. Homepage (web/app/page.tsx): - Title `▍ war.gov/ufo — Investigative Wiki` → `▍ The Disclosure Bureau` - Subtitle expanded from "Holmes · Poirot · Dupin · Locard" to all 8 detectives (Holmes · Locard · Dupin · Schneier · Poirot · Taleb · Tetlock · Case-Writer) - New `🔎 bureau` topbar link (gold, between graph/stats and batch) - BureauSnapshot inserted right after the header BureauSnapshot (web/components/bureau-snapshot.tsx) — server component: - 8 detective tiles with role labels (each in its tone color) - 6 clickable counters (evidence / hypotheses / contradictions / witnesses / outliers / case reports) — anchor to /bureau#section - 6 "recent artefacts" columns surfacing the last 3-4 of each kind: hypotheses with prior→posterior + band + ↳reviewed_by marker, contradictions with topic + resolution_status, evidence with Grade badge + verbatim quote, outliers with title + scope.kind, witness analyses with canonical_name + credibility + verdict, case reports with slug + link to /c/<slug> - "Recent jobs" strip linking to /jobs/[id] color-coded by status - Reports read from /data/ufo/case/reports/ via fs.readdir + stat, sorted by mtime — no DB round-trip needed for that section /bureau (web/app/bureau/page.tsx) — full hub: - Header with full counts - 7 sections (anchored to homepage counter links): Case reports, Hypotheses, Evidence, Contradictions, Outliers, Witnesses, Recent jobs table — each rendering up to 100 rows - Reports section parses frontmatter from each .md to surface topic + n_hypotheses + n_evidence on the card Runtime fixes batched in: - Poirot: coerce entity_pk via Number() — node-postgres returns BIGINT as string by default; writer's Number.isFinite() rejected it as "person_entity_pk required" (j-edgar-hoover retry path) - Tetlock: write_calibration rationale cap 600 → 1200 chars. Prompt still asks ≤ 600 but a 2× slack beats failing the job on honest analysis. Observed live: Tetlock emitted ~620 chars on H-0003 and the writer rejected the entire calibration. - Case-Writer: Promise.all of 5 queries × max_parallel=2 jobs demanded up to 10 connections against the investigator role's rolconnlimit=4 → "too many connections for role investigator". Sequentialized — the LLM call is the hot path, not these queries. Smoke results visible now on the homepage: - 3 hypotheses (H-0001/2/3) about green fireballs origin - 3 contradictions (R-0001/2/3) about color, geographic confinement, exclusive-green vs multicolored - 2 evidence cards (E-0002/3) Grade B - 3 outliers (G-0001/2/3) — including Taleb's deliberate meteor-shower-camouflage flag - 1 case report at /c/green-fireballs-sandia (Watson 13.4 KB, five-act narrative, fully cited) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 01:41:28 +00:00
))}
</div>
W3.10: clickable detective tiles + quick-launch form + doc bureau panel Builds on top of W3.9 to turn the homepage Bureau from a read-only dashboard into a working command center. UI improvements (web/components/bureau-snapshot.tsx): - Detective tiles are now <Link>s — each navigates to its primary artefact section in /bureau (Holmes→#hypotheses, Locard→#evidence, Dupin→#contradictions, Schneier→#hypotheses, Poirot→#witnesses, Taleb→#outliers, Tetlock→#hypotheses, Case-Writer→#reports). Hover bg matches the detective's tone color. - <QuickLaunch /> form inserted right under the tiles. New <QuickLaunch /> client component: - Detective dropdown (7 active kinds; evidence_chain not yet exposed here since it needs a doc_id better picked from the doc page). - Single input swaps placeholder + aria-label by kind: question for Holmes, topic for Dupin/Taleb/Case-Writer, hypothesis_id for Schneier/Tetlock, person_id for Poirot. - Submits to POST /api/bureau/launch and redirects to /jobs/[id] via the next.js router. - Loading state ("queueing…") + error display inline. POST /api/bureau/launch (web/app/api/bureau/launch/route.ts): - Same 8-kind validator as the chat tool's request_investigation. - Auth required when Supabase is configured (triggered_by = user:email). - Returns { job_id, kind, detective, status_url, eta_seconds }. DocBureauPanel on /d/[docId] (web/components/doc-bureau-panel.tsx): - Server component inserted between the doc header and AnomalyHighlights. - Surfaces every bureau artefact that touches the doc: · Evidence whose source_page_id starts with docId/p · Hypotheses citing any of those evidence_ids · Contradictions whose chunks[] has any item with this doc_id · Gaps/outliers with scope.doc_id == docId · Case reports whose markdown body references docId (filesystem scan) - Empty state shows "Investigation Bureau — untouched" with a CTA linking back to the homepage to launch the first investigation. - When non-empty, header counts total artefacts + links to /bureau for the full view. Metadata (web/app/layout.tsx): - description rewritten from "Investigative wiki of the US Department of War UAP/UFO archive (war.gov/ufo)" to one that names the bureau + the 8 detectives. Affects SERP previews + social-card defaults. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 02:33:00 +00:00
{/* Quick-launch form */}
<QuickLaunch />
W3.9: surface the Investigation Bureau on the homepage + /bureau hub Closes a UX gap the user surfaced: W3.5-3.8 built 8 detectives, 4 new URL endpoints (/jobs/[id], /h/[id], /c/[slug], /api/h/[id]/red-team) and a chat tool, but the homepage was unchanged — the bureau was invisible unless you knew the URL or asked the chat to invoke request_investigation. Homepage (web/app/page.tsx): - Title `▍ war.gov/ufo — Investigative Wiki` → `▍ The Disclosure Bureau` - Subtitle expanded from "Holmes · Poirot · Dupin · Locard" to all 8 detectives (Holmes · Locard · Dupin · Schneier · Poirot · Taleb · Tetlock · Case-Writer) - New `🔎 bureau` topbar link (gold, between graph/stats and batch) - BureauSnapshot inserted right after the header BureauSnapshot (web/components/bureau-snapshot.tsx) — server component: - 8 detective tiles with role labels (each in its tone color) - 6 clickable counters (evidence / hypotheses / contradictions / witnesses / outliers / case reports) — anchor to /bureau#section - 6 "recent artefacts" columns surfacing the last 3-4 of each kind: hypotheses with prior→posterior + band + ↳reviewed_by marker, contradictions with topic + resolution_status, evidence with Grade badge + verbatim quote, outliers with title + scope.kind, witness analyses with canonical_name + credibility + verdict, case reports with slug + link to /c/<slug> - "Recent jobs" strip linking to /jobs/[id] color-coded by status - Reports read from /data/ufo/case/reports/ via fs.readdir + stat, sorted by mtime — no DB round-trip needed for that section /bureau (web/app/bureau/page.tsx) — full hub: - Header with full counts - 7 sections (anchored to homepage counter links): Case reports, Hypotheses, Evidence, Contradictions, Outliers, Witnesses, Recent jobs table — each rendering up to 100 rows - Reports section parses frontmatter from each .md to surface topic + n_hypotheses + n_evidence on the card Runtime fixes batched in: - Poirot: coerce entity_pk via Number() — node-postgres returns BIGINT as string by default; writer's Number.isFinite() rejected it as "person_entity_pk required" (j-edgar-hoover retry path) - Tetlock: write_calibration rationale cap 600 → 1200 chars. Prompt still asks ≤ 600 but a 2× slack beats failing the job on honest analysis. Observed live: Tetlock emitted ~620 chars on H-0003 and the writer rejected the entire calibration. - Case-Writer: Promise.all of 5 queries × max_parallel=2 jobs demanded up to 10 connections against the investigator role's rolconnlimit=4 → "too many connections for role investigator". Sequentialized — the LLM call is the hot path, not these queries. Smoke results visible now on the homepage: - 3 hypotheses (H-0001/2/3) about green fireballs origin - 3 contradictions (R-0001/2/3) about color, geographic confinement, exclusive-green vs multicolored - 2 evidence cards (E-0002/3) Grade B - 3 outliers (G-0001/2/3) — including Taleb's deliberate meteor-shower-camouflage flag - 1 case report at /c/green-fireballs-sandia (Watson 13.4 KB, five-act narrative, fully cited) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 01:41:28 +00:00
{/* Counter row */}
<div className="grid grid-cols-3 md:grid-cols-6 gap-2 mb-5 text-center">
<Counter href="/bureau#evidence" label="evidence" value={counts.evidence ?? 0} color="text-[#06d6a0]" />
<Counter href="/bureau#hypotheses" label="hypotheses" value={counts.hypotheses ?? 0} color="text-[#7fdbff]" />
<Counter href="/bureau#contradictions" label="contradictions" value={counts.contradictions ?? 0} color="text-[#ff8a4d]" />
<Counter href="/bureau#witnesses" label="witnesses" value={counts.witnesses ?? 0} color="text-[#9b5de5]" />
<Counter href="/bureau#outliers" label="outliers" value={counts.gaps ?? 0} color="text-[#ffd23f]" />
<Counter href="/bureau#reports" label="case reports" value={reports.length} color="text-[#e0c080]" />
</div>
{/* Recent artefacts grid: hypotheses + contradictions + outliers + reports */}
<div className="grid md:grid-cols-2 gap-3 mb-3">
{hyp.length > 0 && (
<ArtefactColumn
title="Recent hypotheses"
color="text-[#7fdbff]"
border="border-[rgba(127,219,255,0.18)]"
>
{hyp.map((h) => {
const post = h.posterior !== null ? Number(h.posterior) : null;
const bandTone = (h.confidence_band && BAND_TONE[h.confidence_band]) || "text-[#9aa6b8]";
return (
<Link key={h.hypothesis_id} href={`/h/${h.hypothesis_id}`} className="block py-1.5 border-t border-[rgba(127,219,255,0.08)] first:border-t-0 hover:bg-[rgba(127,219,255,0.04)]">
<div className="flex items-baseline justify-between gap-2">
<span className="text-[10px] font-mono text-[#5a6678]">{h.hypothesis_id}</span>
<span className={`text-[10px] font-mono ${bandTone}`}>
{h.confidence_band ?? "—"}{post !== null && ` · ${post.toFixed(2)}`}
</span>
</div>
W4: bilingual EN + PT-BR Investigation Bureau (CLAUDE.md §3 contract) User flagged that the bureau was emitting English-only output, violating the project's bilingual rule. Every narrative field now ships in both languages: stored in sibling DB columns + rendered as adjacent markdown sections per CLAUDE.md §3. Migration 0007 (apply as supabase_admin): - public.hypotheses +question_pt_br, +position_pt_br, +argument_for_pt_br, +argument_against_pt_br - public.contradictions +topic_pt_br, +notes_pt_br - public.witnesses +access_to_event_pt_br, +bias_notes_pt_br, +verdict_pt_br - public.gaps +description_pt_br, +suggested_next_move_pt_br - public.evidence: unchanged (verbatim_excerpt stays source-language) - JSONB siblings inside contradictions.chunks + gaps.scope handled at runtime (statement_pt_br, title_pt_br, dominant_model_pt_br, why_surprising_pt_br, what_it_implies_pt_br). Detective prompts (all 7) rewritten with explicit bilingual JSON contract: - Output protocol section names every EN field + its _pt_br sibling - "Bilingual is mandatory" warning in the task instruction - Sentinel skip-states unchanged (NO_HYPOTHESES, NO_CONTRADICTIONS, INSUFFICIENT_TESTIMONY, INSUFFICIENT_HYPOTHESIS, NO_OUTLIERS, NO_NEW_EVIDENCE, INSUFFICIENT_ARTEFACTS) - Schneier: parallel arrays — hidden_assumptions[i] matches hidden_assumptions_pt_br[i], lengths must match - Case-Writer: interleaved §1 (EN) / §1 (PT-BR) per act in the body Writer-side validation (all 7 tools): - Reject INSERT if PT-BR sibling missing when EN field is set - Persist both languages atomically in one INSERT (no half-updates) - Markdown renderers write adjacent EN+PT-BR sections in case files (## Argument for (EN) followed by ## Argumento a favor (PT-BR), etc.) Detective parse layer (all 7 detectives): - Coerce both keys from JSON output - "incomplete_bilingual_*" skip reason when either side missing - Defensive: PT-BR fields trimmed + length-capped same as EN Orchestrator propagates question_pt_br + topic_pt_br through job payload to runHolmes / runCaseWriter, mirroring the chat-tool entry point. Web (UI): - /api/jobs/[id] hydrates _pt_br siblings from pg - job-status-poller HypothesisCard: PT-BR primary, EN in <details> fallback when both exist - ContradictionCard: PT-BR statement primary + secondary EN quote - WitnessCard: PT-BR verdict primary + secondary EN quote, panels in PT - GapCard: PT-BR title/why/implies primary - /bureau hub: SELECTs both columns, renders PT-BR primary - /h/[id]: ArgumentPanel renders PT-BR primary with collapsible EN fallback when both exist - BureauSnapshot homepage: position_pt_br / topic_pt_br / verdict_pt_br primary - DocBureauPanel /d/[doc]: same primary-PT-BR pattern - New web/lib/i18n/pick.ts helper (unused yet by chat/agents — kept for future locale-driven switching when both languages are equally full; current rule is PT-BR-first since the user is brasileiro) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 15:02:59 +00:00
<div className="text-[12px] text-[#cbd2dd] leading-snug mt-0.5">{h.position_pt_br || h.position}</div>
W3.9: surface the Investigation Bureau on the homepage + /bureau hub Closes a UX gap the user surfaced: W3.5-3.8 built 8 detectives, 4 new URL endpoints (/jobs/[id], /h/[id], /c/[slug], /api/h/[id]/red-team) and a chat tool, but the homepage was unchanged — the bureau was invisible unless you knew the URL or asked the chat to invoke request_investigation. Homepage (web/app/page.tsx): - Title `▍ war.gov/ufo — Investigative Wiki` → `▍ The Disclosure Bureau` - Subtitle expanded from "Holmes · Poirot · Dupin · Locard" to all 8 detectives (Holmes · Locard · Dupin · Schneier · Poirot · Taleb · Tetlock · Case-Writer) - New `🔎 bureau` topbar link (gold, between graph/stats and batch) - BureauSnapshot inserted right after the header BureauSnapshot (web/components/bureau-snapshot.tsx) — server component: - 8 detective tiles with role labels (each in its tone color) - 6 clickable counters (evidence / hypotheses / contradictions / witnesses / outliers / case reports) — anchor to /bureau#section - 6 "recent artefacts" columns surfacing the last 3-4 of each kind: hypotheses with prior→posterior + band + ↳reviewed_by marker, contradictions with topic + resolution_status, evidence with Grade badge + verbatim quote, outliers with title + scope.kind, witness analyses with canonical_name + credibility + verdict, case reports with slug + link to /c/<slug> - "Recent jobs" strip linking to /jobs/[id] color-coded by status - Reports read from /data/ufo/case/reports/ via fs.readdir + stat, sorted by mtime — no DB round-trip needed for that section /bureau (web/app/bureau/page.tsx) — full hub: - Header with full counts - 7 sections (anchored to homepage counter links): Case reports, Hypotheses, Evidence, Contradictions, Outliers, Witnesses, Recent jobs table — each rendering up to 100 rows - Reports section parses frontmatter from each .md to surface topic + n_hypotheses + n_evidence on the card Runtime fixes batched in: - Poirot: coerce entity_pk via Number() — node-postgres returns BIGINT as string by default; writer's Number.isFinite() rejected it as "person_entity_pk required" (j-edgar-hoover retry path) - Tetlock: write_calibration rationale cap 600 → 1200 chars. Prompt still asks ≤ 600 but a 2× slack beats failing the job on honest analysis. Observed live: Tetlock emitted ~620 chars on H-0003 and the writer rejected the entire calibration. - Case-Writer: Promise.all of 5 queries × max_parallel=2 jobs demanded up to 10 connections against the investigator role's rolconnlimit=4 → "too many connections for role investigator". Sequentialized — the LLM call is the hot path, not these queries. Smoke results visible now on the homepage: - 3 hypotheses (H-0001/2/3) about green fireballs origin - 3 contradictions (R-0001/2/3) about color, geographic confinement, exclusive-green vs multicolored - 2 evidence cards (E-0002/3) Grade B - 3 outliers (G-0001/2/3) — including Taleb's deliberate meteor-shower-camouflage flag - 1 case report at /c/green-fireballs-sandia (Watson 13.4 KB, five-act narrative, fully cited) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 01:41:28 +00:00
{h.reviewed_by && (
<div className="text-[9px] font-mono text-[#ff3344] mt-0.5"> reviewed by {h.reviewed_by}</div>
)}
</Link>
);
})}
</ArtefactColumn>
)}
{ctr.length > 0 && (
<ArtefactColumn
title="Recent contradictions"
color="text-[#ff8a4d]"
border="border-[rgba(255,138,77,0.18)]"
>
{ctr.map((c) => (
<div key={c.contradiction_id} className="py-1.5 border-t border-[rgba(255,138,77,0.08)] first:border-t-0">
<div className="flex items-baseline justify-between gap-2">
<span className="text-[10px] font-mono text-[#5a6678]">{c.contradiction_id}</span>
<span className="text-[10px] font-mono text-[#9aa6b8]">{c.resolution_status}</span>
</div>
W4: bilingual EN + PT-BR Investigation Bureau (CLAUDE.md §3 contract) User flagged that the bureau was emitting English-only output, violating the project's bilingual rule. Every narrative field now ships in both languages: stored in sibling DB columns + rendered as adjacent markdown sections per CLAUDE.md §3. Migration 0007 (apply as supabase_admin): - public.hypotheses +question_pt_br, +position_pt_br, +argument_for_pt_br, +argument_against_pt_br - public.contradictions +topic_pt_br, +notes_pt_br - public.witnesses +access_to_event_pt_br, +bias_notes_pt_br, +verdict_pt_br - public.gaps +description_pt_br, +suggested_next_move_pt_br - public.evidence: unchanged (verbatim_excerpt stays source-language) - JSONB siblings inside contradictions.chunks + gaps.scope handled at runtime (statement_pt_br, title_pt_br, dominant_model_pt_br, why_surprising_pt_br, what_it_implies_pt_br). Detective prompts (all 7) rewritten with explicit bilingual JSON contract: - Output protocol section names every EN field + its _pt_br sibling - "Bilingual is mandatory" warning in the task instruction - Sentinel skip-states unchanged (NO_HYPOTHESES, NO_CONTRADICTIONS, INSUFFICIENT_TESTIMONY, INSUFFICIENT_HYPOTHESIS, NO_OUTLIERS, NO_NEW_EVIDENCE, INSUFFICIENT_ARTEFACTS) - Schneier: parallel arrays — hidden_assumptions[i] matches hidden_assumptions_pt_br[i], lengths must match - Case-Writer: interleaved §1 (EN) / §1 (PT-BR) per act in the body Writer-side validation (all 7 tools): - Reject INSERT if PT-BR sibling missing when EN field is set - Persist both languages atomically in one INSERT (no half-updates) - Markdown renderers write adjacent EN+PT-BR sections in case files (## Argument for (EN) followed by ## Argumento a favor (PT-BR), etc.) Detective parse layer (all 7 detectives): - Coerce both keys from JSON output - "incomplete_bilingual_*" skip reason when either side missing - Defensive: PT-BR fields trimmed + length-capped same as EN Orchestrator propagates question_pt_br + topic_pt_br through job payload to runHolmes / runCaseWriter, mirroring the chat-tool entry point. Web (UI): - /api/jobs/[id] hydrates _pt_br siblings from pg - job-status-poller HypothesisCard: PT-BR primary, EN in <details> fallback when both exist - ContradictionCard: PT-BR statement primary + secondary EN quote - WitnessCard: PT-BR verdict primary + secondary EN quote, panels in PT - GapCard: PT-BR title/why/implies primary - /bureau hub: SELECTs both columns, renders PT-BR primary - /h/[id]: ArgumentPanel renders PT-BR primary with collapsible EN fallback when both exist - BureauSnapshot homepage: position_pt_br / topic_pt_br / verdict_pt_br primary - DocBureauPanel /d/[doc]: same primary-PT-BR pattern - New web/lib/i18n/pick.ts helper (unused yet by chat/agents — kept for future locale-driven switching when both languages are equally full; current rule is PT-BR-first since the user is brasileiro) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 15:02:59 +00:00
<div className="text-[12px] text-[#cbd2dd] leading-snug mt-0.5">{c.topic_pt_br || c.topic}</div>
W3.9: surface the Investigation Bureau on the homepage + /bureau hub Closes a UX gap the user surfaced: W3.5-3.8 built 8 detectives, 4 new URL endpoints (/jobs/[id], /h/[id], /c/[slug], /api/h/[id]/red-team) and a chat tool, but the homepage was unchanged — the bureau was invisible unless you knew the URL or asked the chat to invoke request_investigation. Homepage (web/app/page.tsx): - Title `▍ war.gov/ufo — Investigative Wiki` → `▍ The Disclosure Bureau` - Subtitle expanded from "Holmes · Poirot · Dupin · Locard" to all 8 detectives (Holmes · Locard · Dupin · Schneier · Poirot · Taleb · Tetlock · Case-Writer) - New `🔎 bureau` topbar link (gold, between graph/stats and batch) - BureauSnapshot inserted right after the header BureauSnapshot (web/components/bureau-snapshot.tsx) — server component: - 8 detective tiles with role labels (each in its tone color) - 6 clickable counters (evidence / hypotheses / contradictions / witnesses / outliers / case reports) — anchor to /bureau#section - 6 "recent artefacts" columns surfacing the last 3-4 of each kind: hypotheses with prior→posterior + band + ↳reviewed_by marker, contradictions with topic + resolution_status, evidence with Grade badge + verbatim quote, outliers with title + scope.kind, witness analyses with canonical_name + credibility + verdict, case reports with slug + link to /c/<slug> - "Recent jobs" strip linking to /jobs/[id] color-coded by status - Reports read from /data/ufo/case/reports/ via fs.readdir + stat, sorted by mtime — no DB round-trip needed for that section /bureau (web/app/bureau/page.tsx) — full hub: - Header with full counts - 7 sections (anchored to homepage counter links): Case reports, Hypotheses, Evidence, Contradictions, Outliers, Witnesses, Recent jobs table — each rendering up to 100 rows - Reports section parses frontmatter from each .md to surface topic + n_hypotheses + n_evidence on the card Runtime fixes batched in: - Poirot: coerce entity_pk via Number() — node-postgres returns BIGINT as string by default; writer's Number.isFinite() rejected it as "person_entity_pk required" (j-edgar-hoover retry path) - Tetlock: write_calibration rationale cap 600 → 1200 chars. Prompt still asks ≤ 600 but a 2× slack beats failing the job on honest analysis. Observed live: Tetlock emitted ~620 chars on H-0003 and the writer rejected the entire calibration. - Case-Writer: Promise.all of 5 queries × max_parallel=2 jobs demanded up to 10 connections against the investigator role's rolconnlimit=4 → "too many connections for role investigator". Sequentialized — the LLM call is the hot path, not these queries. Smoke results visible now on the homepage: - 3 hypotheses (H-0001/2/3) about green fireballs origin - 3 contradictions (R-0001/2/3) about color, geographic confinement, exclusive-green vs multicolored - 2 evidence cards (E-0002/3) Grade B - 3 outliers (G-0001/2/3) — including Taleb's deliberate meteor-shower-camouflage flag - 1 case report at /c/green-fireballs-sandia (Watson 13.4 KB, five-act narrative, fully cited) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 01:41:28 +00:00
</div>
))}
</ArtefactColumn>
)}
{ev.length > 0 && (
<ArtefactColumn
title="Recent evidence"
color="text-[#06d6a0]"
border="border-[rgba(6,214,160,0.18)]"
>
{ev.map((e) => {
const tone = (e.grade && GRADE_TONE[e.grade]) || "text-[#9aa6b8]";
return (
<div key={e.evidence_id} className="py-1.5 border-t border-[rgba(6,214,160,0.08)] first:border-t-0">
<div className="flex items-baseline justify-between gap-2">
<span className="text-[10px] font-mono text-[#5a6678]">{e.evidence_id}</span>
<span className={`text-[10px] font-mono ${tone} px-1.5 border rounded`}>Grade {e.grade}</span>
</div>
<blockquote className="text-[12px] text-[#cbd2dd] italic mt-0.5 leading-snug border-l-2 border-[#06d6a0] pl-2">
{(e.verbatim_excerpt || "").slice(0, 140)}
</blockquote>
</div>
);
})}
</ArtefactColumn>
)}
{gap.length > 0 && (
<ArtefactColumn
title="Recent outliers"
color="text-[#ffd23f]"
border="border-[rgba(255,210,63,0.25)]"
>
{gap.map((g) => {
const s = (g.scope ?? {}) as Record<string, unknown>;
W4: bilingual EN + PT-BR Investigation Bureau (CLAUDE.md §3 contract) User flagged that the bureau was emitting English-only output, violating the project's bilingual rule. Every narrative field now ships in both languages: stored in sibling DB columns + rendered as adjacent markdown sections per CLAUDE.md §3. Migration 0007 (apply as supabase_admin): - public.hypotheses +question_pt_br, +position_pt_br, +argument_for_pt_br, +argument_against_pt_br - public.contradictions +topic_pt_br, +notes_pt_br - public.witnesses +access_to_event_pt_br, +bias_notes_pt_br, +verdict_pt_br - public.gaps +description_pt_br, +suggested_next_move_pt_br - public.evidence: unchanged (verbatim_excerpt stays source-language) - JSONB siblings inside contradictions.chunks + gaps.scope handled at runtime (statement_pt_br, title_pt_br, dominant_model_pt_br, why_surprising_pt_br, what_it_implies_pt_br). Detective prompts (all 7) rewritten with explicit bilingual JSON contract: - Output protocol section names every EN field + its _pt_br sibling - "Bilingual is mandatory" warning in the task instruction - Sentinel skip-states unchanged (NO_HYPOTHESES, NO_CONTRADICTIONS, INSUFFICIENT_TESTIMONY, INSUFFICIENT_HYPOTHESIS, NO_OUTLIERS, NO_NEW_EVIDENCE, INSUFFICIENT_ARTEFACTS) - Schneier: parallel arrays — hidden_assumptions[i] matches hidden_assumptions_pt_br[i], lengths must match - Case-Writer: interleaved §1 (EN) / §1 (PT-BR) per act in the body Writer-side validation (all 7 tools): - Reject INSERT if PT-BR sibling missing when EN field is set - Persist both languages atomically in one INSERT (no half-updates) - Markdown renderers write adjacent EN+PT-BR sections in case files (## Argument for (EN) followed by ## Argumento a favor (PT-BR), etc.) Detective parse layer (all 7 detectives): - Coerce both keys from JSON output - "incomplete_bilingual_*" skip reason when either side missing - Defensive: PT-BR fields trimmed + length-capped same as EN Orchestrator propagates question_pt_br + topic_pt_br through job payload to runHolmes / runCaseWriter, mirroring the chat-tool entry point. Web (UI): - /api/jobs/[id] hydrates _pt_br siblings from pg - job-status-poller HypothesisCard: PT-BR primary, EN in <details> fallback when both exist - ContradictionCard: PT-BR statement primary + secondary EN quote - WitnessCard: PT-BR verdict primary + secondary EN quote, panels in PT - GapCard: PT-BR title/why/implies primary - /bureau hub: SELECTs both columns, renders PT-BR primary - /h/[id]: ArgumentPanel renders PT-BR primary with collapsible EN fallback when both exist - BureauSnapshot homepage: position_pt_br / topic_pt_br / verdict_pt_br primary - DocBureauPanel /d/[doc]: same primary-PT-BR pattern - New web/lib/i18n/pick.ts helper (unused yet by chat/agents — kept for future locale-driven switching when both languages are equally full; current rule is PT-BR-first since the user is brasileiro) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 15:02:59 +00:00
const title = (s.title_pt_br as string) || (s.title as string)
|| g.description_pt_br || g.description;
W3.9: surface the Investigation Bureau on the homepage + /bureau hub Closes a UX gap the user surfaced: W3.5-3.8 built 8 detectives, 4 new URL endpoints (/jobs/[id], /h/[id], /c/[slug], /api/h/[id]/red-team) and a chat tool, but the homepage was unchanged — the bureau was invisible unless you knew the URL or asked the chat to invoke request_investigation. Homepage (web/app/page.tsx): - Title `▍ war.gov/ufo — Investigative Wiki` → `▍ The Disclosure Bureau` - Subtitle expanded from "Holmes · Poirot · Dupin · Locard" to all 8 detectives (Holmes · Locard · Dupin · Schneier · Poirot · Taleb · Tetlock · Case-Writer) - New `🔎 bureau` topbar link (gold, between graph/stats and batch) - BureauSnapshot inserted right after the header BureauSnapshot (web/components/bureau-snapshot.tsx) — server component: - 8 detective tiles with role labels (each in its tone color) - 6 clickable counters (evidence / hypotheses / contradictions / witnesses / outliers / case reports) — anchor to /bureau#section - 6 "recent artefacts" columns surfacing the last 3-4 of each kind: hypotheses with prior→posterior + band + ↳reviewed_by marker, contradictions with topic + resolution_status, evidence with Grade badge + verbatim quote, outliers with title + scope.kind, witness analyses with canonical_name + credibility + verdict, case reports with slug + link to /c/<slug> - "Recent jobs" strip linking to /jobs/[id] color-coded by status - Reports read from /data/ufo/case/reports/ via fs.readdir + stat, sorted by mtime — no DB round-trip needed for that section /bureau (web/app/bureau/page.tsx) — full hub: - Header with full counts - 7 sections (anchored to homepage counter links): Case reports, Hypotheses, Evidence, Contradictions, Outliers, Witnesses, Recent jobs table — each rendering up to 100 rows - Reports section parses frontmatter from each .md to surface topic + n_hypotheses + n_evidence on the card Runtime fixes batched in: - Poirot: coerce entity_pk via Number() — node-postgres returns BIGINT as string by default; writer's Number.isFinite() rejected it as "person_entity_pk required" (j-edgar-hoover retry path) - Tetlock: write_calibration rationale cap 600 → 1200 chars. Prompt still asks ≤ 600 but a 2× slack beats failing the job on honest analysis. Observed live: Tetlock emitted ~620 chars on H-0003 and the writer rejected the entire calibration. - Case-Writer: Promise.all of 5 queries × max_parallel=2 jobs demanded up to 10 connections against the investigator role's rolconnlimit=4 → "too many connections for role investigator". Sequentialized — the LLM call is the hot path, not these queries. Smoke results visible now on the homepage: - 3 hypotheses (H-0001/2/3) about green fireballs origin - 3 contradictions (R-0001/2/3) about color, geographic confinement, exclusive-green vs multicolored - 2 evidence cards (E-0002/3) Grade B - 3 outliers (G-0001/2/3) — including Taleb's deliberate meteor-shower-camouflage flag - 1 case report at /c/green-fireballs-sandia (Watson 13.4 KB, five-act narrative, fully cited) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 01:41:28 +00:00
return (
<div key={g.gap_id} className="py-1.5 border-t border-[rgba(255,210,63,0.08)] first:border-t-0">
<div className="flex items-baseline justify-between gap-2">
<span className="text-[10px] font-mono text-[#5a6678]">{g.gap_id}</span>
{s.kind === "outlier" && (
<span className="text-[10px] font-mono text-[#ffd23f]">outlier</span>
)}
</div>
<div className="text-[12px] text-[#cbd2dd] leading-snug mt-0.5">{title}</div>
</div>
);
})}
</ArtefactColumn>
)}
{wit.length > 0 && (
<ArtefactColumn
title="Witness analyses"
color="text-[#9b5de5]"
border="border-[rgba(155,93,229,0.18)]"
>
{wit.map((w) => (
<div key={w.witness_id} className="py-1.5 border-t border-[rgba(155,93,229,0.08)] first:border-t-0">
<div className="flex items-baseline justify-between gap-2">
<span className="text-[10px] font-mono text-[#5a6678]">{w.witness_id}</span>
<span className="text-[10px] font-mono text-[#9b5de5]">{w.credibility ?? "—"}</span>
</div>
<div className="text-[12px] text-[#cbd2dd] leading-snug mt-0.5 font-semibold">{w.canonical_name ?? "—"}</div>
W4: bilingual EN + PT-BR Investigation Bureau (CLAUDE.md §3 contract) User flagged that the bureau was emitting English-only output, violating the project's bilingual rule. Every narrative field now ships in both languages: stored in sibling DB columns + rendered as adjacent markdown sections per CLAUDE.md §3. Migration 0007 (apply as supabase_admin): - public.hypotheses +question_pt_br, +position_pt_br, +argument_for_pt_br, +argument_against_pt_br - public.contradictions +topic_pt_br, +notes_pt_br - public.witnesses +access_to_event_pt_br, +bias_notes_pt_br, +verdict_pt_br - public.gaps +description_pt_br, +suggested_next_move_pt_br - public.evidence: unchanged (verbatim_excerpt stays source-language) - JSONB siblings inside contradictions.chunks + gaps.scope handled at runtime (statement_pt_br, title_pt_br, dominant_model_pt_br, why_surprising_pt_br, what_it_implies_pt_br). Detective prompts (all 7) rewritten with explicit bilingual JSON contract: - Output protocol section names every EN field + its _pt_br sibling - "Bilingual is mandatory" warning in the task instruction - Sentinel skip-states unchanged (NO_HYPOTHESES, NO_CONTRADICTIONS, INSUFFICIENT_TESTIMONY, INSUFFICIENT_HYPOTHESIS, NO_OUTLIERS, NO_NEW_EVIDENCE, INSUFFICIENT_ARTEFACTS) - Schneier: parallel arrays — hidden_assumptions[i] matches hidden_assumptions_pt_br[i], lengths must match - Case-Writer: interleaved §1 (EN) / §1 (PT-BR) per act in the body Writer-side validation (all 7 tools): - Reject INSERT if PT-BR sibling missing when EN field is set - Persist both languages atomically in one INSERT (no half-updates) - Markdown renderers write adjacent EN+PT-BR sections in case files (## Argument for (EN) followed by ## Argumento a favor (PT-BR), etc.) Detective parse layer (all 7 detectives): - Coerce both keys from JSON output - "incomplete_bilingual_*" skip reason when either side missing - Defensive: PT-BR fields trimmed + length-capped same as EN Orchestrator propagates question_pt_br + topic_pt_br through job payload to runHolmes / runCaseWriter, mirroring the chat-tool entry point. Web (UI): - /api/jobs/[id] hydrates _pt_br siblings from pg - job-status-poller HypothesisCard: PT-BR primary, EN in <details> fallback when both exist - ContradictionCard: PT-BR statement primary + secondary EN quote - WitnessCard: PT-BR verdict primary + secondary EN quote, panels in PT - GapCard: PT-BR title/why/implies primary - /bureau hub: SELECTs both columns, renders PT-BR primary - /h/[id]: ArgumentPanel renders PT-BR primary with collapsible EN fallback when both exist - BureauSnapshot homepage: position_pt_br / topic_pt_br / verdict_pt_br primary - DocBureauPanel /d/[doc]: same primary-PT-BR pattern - New web/lib/i18n/pick.ts helper (unused yet by chat/agents — kept for future locale-driven switching when both languages are equally full; current rule is PT-BR-first since the user is brasileiro) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 15:02:59 +00:00
{(w.verdict_pt_br || w.verdict) && <div className="text-[11px] text-[#9aa6b8] mt-0.5 italic">{(w.verdict_pt_br || w.verdict || "").slice(0, 200)}</div>}
W3.9: surface the Investigation Bureau on the homepage + /bureau hub Closes a UX gap the user surfaced: W3.5-3.8 built 8 detectives, 4 new URL endpoints (/jobs/[id], /h/[id], /c/[slug], /api/h/[id]/red-team) and a chat tool, but the homepage was unchanged — the bureau was invisible unless you knew the URL or asked the chat to invoke request_investigation. Homepage (web/app/page.tsx): - Title `▍ war.gov/ufo — Investigative Wiki` → `▍ The Disclosure Bureau` - Subtitle expanded from "Holmes · Poirot · Dupin · Locard" to all 8 detectives (Holmes · Locard · Dupin · Schneier · Poirot · Taleb · Tetlock · Case-Writer) - New `🔎 bureau` topbar link (gold, between graph/stats and batch) - BureauSnapshot inserted right after the header BureauSnapshot (web/components/bureau-snapshot.tsx) — server component: - 8 detective tiles with role labels (each in its tone color) - 6 clickable counters (evidence / hypotheses / contradictions / witnesses / outliers / case reports) — anchor to /bureau#section - 6 "recent artefacts" columns surfacing the last 3-4 of each kind: hypotheses with prior→posterior + band + ↳reviewed_by marker, contradictions with topic + resolution_status, evidence with Grade badge + verbatim quote, outliers with title + scope.kind, witness analyses with canonical_name + credibility + verdict, case reports with slug + link to /c/<slug> - "Recent jobs" strip linking to /jobs/[id] color-coded by status - Reports read from /data/ufo/case/reports/ via fs.readdir + stat, sorted by mtime — no DB round-trip needed for that section /bureau (web/app/bureau/page.tsx) — full hub: - Header with full counts - 7 sections (anchored to homepage counter links): Case reports, Hypotheses, Evidence, Contradictions, Outliers, Witnesses, Recent jobs table — each rendering up to 100 rows - Reports section parses frontmatter from each .md to surface topic + n_hypotheses + n_evidence on the card Runtime fixes batched in: - Poirot: coerce entity_pk via Number() — node-postgres returns BIGINT as string by default; writer's Number.isFinite() rejected it as "person_entity_pk required" (j-edgar-hoover retry path) - Tetlock: write_calibration rationale cap 600 → 1200 chars. Prompt still asks ≤ 600 but a 2× slack beats failing the job on honest analysis. Observed live: Tetlock emitted ~620 chars on H-0003 and the writer rejected the entire calibration. - Case-Writer: Promise.all of 5 queries × max_parallel=2 jobs demanded up to 10 connections against the investigator role's rolconnlimit=4 → "too many connections for role investigator". Sequentialized — the LLM call is the hot path, not these queries. Smoke results visible now on the homepage: - 3 hypotheses (H-0001/2/3) about green fireballs origin - 3 contradictions (R-0001/2/3) about color, geographic confinement, exclusive-green vs multicolored - 2 evidence cards (E-0002/3) Grade B - 3 outliers (G-0001/2/3) — including Taleb's deliberate meteor-shower-camouflage flag - 1 case report at /c/green-fireballs-sandia (Watson 13.4 KB, five-act narrative, fully cited) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 01:41:28 +00:00
</div>
))}
</ArtefactColumn>
)}
{reports.length > 0 && (
<ArtefactColumn
title="Case reports"
color="text-[#e0c080]"
border="border-[rgba(224,192,128,0.25)]"
>
{reports.map((r) => (
<Link key={r.slug} href={`/c/${r.slug}`} className="block py-1.5 border-t border-[rgba(224,192,128,0.08)] first:border-t-0 hover:bg-[rgba(224,192,128,0.04)]">
<div className="text-[10px] font-mono text-[#5a6678]">case-report</div>
<div className="text-[12px] text-[#e0c080] leading-snug mt-0.5 font-medium">/c/{r.slug}</div>
<div className="text-[10px] font-mono text-[#5a6678] mt-0.5">Watson five-act narrative </div>
</Link>
))}
</ArtefactColumn>
)}
</div>
{/* Job activity strip */}
{jobs.length > 0 && (
<div className="text-[10px] font-mono text-[#5a6678] mt-2">
Recent jobs:{" "}
{jobs.slice(0, 4).map((j, i) => (
<span key={j.job_id}>
{i > 0 && <span className="text-[#3a4250]"> · </span>}
<Link
href={`/jobs/${j.job_id}`}
className={`hover:underline ${
j.status === "complete" ? "text-[#06d6a0]" :
j.status === "failed" ? "text-[#ff6ec7]" :
j.status === "running" ? "text-[#ffd23f]" :
"text-[#9aa6b8]"
}`}
>
{j.kind}/{j.status}
</Link>
</span>
))}
</div>
)}
</section>
);
}
function Counter({ label, value, color, href }: { label: string; value: number; color: string; href: string }) {
return (
<Link href={href} className="block rounded border border-[rgba(127,219,255,0.08)] bg-[#0d1220] py-2 hover:border-[rgba(127,219,255,0.20)]">
<div className={`text-2xl font-mono font-bold ${color}`}>{value}</div>
<div className="text-[10px] font-mono text-[#5a6678] uppercase tracking-wider mt-0.5">{label}</div>
</Link>
);
}
function ArtefactColumn({
title, color, border, children,
}: { title: string; color: string; border: string; children: React.ReactNode }) {
return (
<div className={`rounded-lg border ${border} bg-[#0d1220] p-3`}>
<div className={`text-[10px] font-mono uppercase tracking-wider ${color} mb-1`}>{title}</div>
<div>{children}</div>
</div>
);
}