---
adr: 004
title: "Case Bureau pipeline — Hypothesis tournament (Tetlock) + Evidence chain-of-custody (Locard)"
status: Proposed
date: 2026-05-17
owner: sa-principal
related_phase: Fase 2
---

# ADR-004 — Case Bureau pipeline

## Status

Proposed. **Diferencial competitivo do produto.** Sem Case Bureau o Disclosure Bureau é "wiki bonita com chat" — comoditizável.

## Context

O `/Users/guto/ufo/CLAUDE.md` define 12 tipos investigativos (evidence, witness, hypothesis, gap, relation, timeline, actor_profile, case_report, residual_uncertainty, …). O `/Users/guto/ufo/CLAUDE-schema-full.md` formaliza o frontmatter de cada um.

**Estado físico:**

```
case/
├── connect-the-dots/   0 files
├── evidence/           0 files
├── gaps/               2 placeholder files (bootstrap)
├── hypotheses/         0 files
├── profiles/           0 files
├── timelines/          0 files
└── witnesses/          0 files
```

**Nunca rodou.** O brief lista isso como gap crítico #2. O `case-report.md` referenciado em `wiki/index.md` é um link quebrado.

O contrato exige:

- **6 quality rubrics threshold 0.85** em `case_report.md` (chain_of_custody_completeness, confidence_calibration_match, hypothesis_tournament_discipline ≥3 hipóteses, residual_uncertainty_presence, audit_trail_per_claim, red_team_pass).
- **Confidence bands Tetlock**: high (≥0.90) / medium (0.60-0.89) / low (0.30-0.59) / speculation (<0.30).
- **Evidence grades Locard**: A (≥3 custody steps) / B (≥2) / C (≥1).
- **8 detetives**: Holmes/Poirot/Dupin/Locard + Schneier/Tetlock/Taleb (papéis declarados, sem implementação).

## Opções consideradas

### Opção A — Pipeline batch sobre todo o corpus

- Para cada `event` com `total_mentions ≥ 5`, criar case automaticamente; gerar 3 hipóteses; bayesianizar.
- **Custo:** 8d.
- **Prós:** cobertura ampla.
- **Contras:** **viola proporcionalidade Fowler.** 7.091 events × ($0.30 por case) = $2k+ em LLM. Quality cai. Output ilegível.

### Opção B — Seed manual + pipeline incremental

- 5 casos âncora seedados manualmente (canonical events).
- Pipeline gera hipóteses para cada caso âncora.
- Próximos casos abrem por demanda (user pede "abra case para Event-X" via chat ou admin).
- **Custo:** 12d (3d seed + 4d hypothesis engine + 5d UI).
- **Prós:** qualidade controlada; ROI por case.
- **Contras:** cobertura inicial pequena.

### Opção C — Outsource para business parceira

- Persona desta arquitetura: chamar `juridical-singularity` ou `research-intelligence` via harness para o trabalho investigativo.
- **Custo:** ? (depende da business).
- **Contras:** Case Bureau é **a alma do produto Disclosure Bureau**; outsourcear é estranho. Manter in-house.

## Decisão

**Adotar Opção B — 5 casos âncora seedados + pipeline incremental.**

Justificativas:

1. **Qualidade > cobertura.** Um Case Bureau com 5 casos exemplares vence 7k casos genéricos. Persona A/B/C aprende o padrão e confia no método.
2. **Proporcional ao orçamento atual** (Claude Max 20x).
3. **Ciclo de feedback rápido.** Cada case rodado refina o prompt do hypothesis engine.

## Implementação

### Etapa 1 — Schema (1d)

Migration `0003_case_bureau.sql` (esboço em entregável 04, §4.4.1). Tabelas:

- `cases(case_id, canonical_name, scope_summary, status, …)`
- `hypotheses(hypothesis_id, case_id, statement_en, statement_pt, prior, likelihood, posterior, evidence_for[], evidence_against[], red_team_pass, confidence_band, authored_by, …)`
- `evidence(evidence_id, case_id, doc_id, page, bbox, verbatim_excerpt, evidence_grade, chain_of_custody, custody_gaps[], …)`
- `witnesses(witness_id, case_id, witness_name, account_summary, verdict, …)`
- `gaps(gap_id, case_id, description, criticality, …)`
- `case_reports(case_id, body_md, quality_rubrics JSONB, last_review)`

RLS: `SELECT public USING (TRUE)` (corpus público); writes via `service_role` (pipeline only).

### Etapa 2 — Seed (2d)

`scripts/case/30_seed.py` cria 5 casos âncora:

| `case_id` | Evento | Justificativa |
|---|---|---|
| `CASE-ROSWELL-1947` | Roswell incident, julho 1947 | Canonical UAP case; rico em documentos FBI Vault. |
| `CASE-NIMITZ-TIC-TAC-2004` | USS Nimitz / Princeton Tic-Tac encounter | Caso DoD oficial; vídeo + radar data. |
| `CASE-PHOENIX-LIGHTS-1997` | Phoenix Lights, março 1997 | Multi-witness; corpus tem FAA reports. |
| `CASE-HOTTEL-MEMO-1950` | FBI Hottel memo, Roswell adjacent | Single-document case; mostra leitura crítica. |
| `CASE-APOLLO-SKYLAB-1973` | Skylab IV anomalous photos | NASA corpus; bridge para civilian science angle. |

Cada seed cria:

- 1 `case_report` skeleton com `quality_rubrics: null` (será preenchido após review).
- 3-5 `evidence` rows extraídas dos chunks corpus existentes (chunks com `ufo_anomaly=true` para esses docs, ordenados por relevância).
- 2-3 `witness` rows quando aplicável (relatos primários no corpus).
- 1 `timeline` com event_ids relacionados.

### Etapa 3 — Hypothesis Tournament Engine (4d)

`scripts/case/31_hypothesis_tournament.py`:

```
INPUT: case_id
WORKFLOW:
  1. Load case + evidence + witnesses (DB).
  2. For each detective_role in ['holmes','poirot','dupin']:
       prompt = build_prompt(case, role, evidence_summary)
       call Claude Code OAuth (Sonnet) --max-turns 3
       parse output → 1 hypothesis with statement_en/pt, prior, likelihood
       insert into hypotheses(authored_by=role)
  3. After 3 hypotheses generated:
       normalize priors so Σ = 1.0
       compute posterior_i = (prior_i × likelihood_i) / Σ_j(prior_j × likelihood_j)
       assign confidence_band by posterior thresholds (Tetlock):
         ≥0.70 high, 0.40-0.69 medium, 0.15-0.39 low, <0.15 speculation
  4. Write H-NNNN.md files in case/hypotheses/
  5. Update case_report.md with leaderboard
```

Prompts são versionados em `scripts/case/prompts/{holmes,poirot,dupin}_hypothesis.md`. ADR-007 (futuro) congelará a versão de prompt usada por release.

### Etapa 4 — Locard Chain-of-Custody Audit (1d como parte da fase 2)

`scripts/case/32_evidence_audit.py`: para cada evidence row, valida:

- `verbatim_excerpt` matches a chunk em DB (não fabricado).
- `chain_of_custody` array tem ≥1 step (Grade C), ≥2 (B), ≥3 (A).
- Se gap, popula `custody_gaps[]` com descrição.

Output: `case/<case_id>/evidence_audit.md` com sumário.

### Etapa 5 — Red-Team Review (chief-detective) (2d)

`scripts/case/33_red_team.py`:

```
INPUT: case_id
WORKFLOW:
  1. Load hypotheses sorted by posterior DESC.
  2. Take top hypothesis. Build adversarial prompt:
       "You are the chief detective. The dominant hypothesis is X with
        posterior Y. Find the strongest argument that this hypothesis is
        wrong. Cite specific evidence by evidence_id."
  3. Call Claude Code OAuth (Sonnet).
  4. Parse output → residual_uncertainty.md entry.
  5. Quality rubric red_team_pass = (residual_uncertainty has substance).
  6. Update case_report.md quality_rubrics.
```

### Etapa 6 — UI (5d, parte da fase 2)

Rotas novas:

- `/case` — lista os casos.
- `/case/[case_id]` — overview: status, timeline, 3 hipóteses, evidence count.
- `/case/[case_id]/hypotheses` — tournament leaderboard, cada card com posterior, evidence_for[], red-team box.
- `/case/[case_id]/evidence` — lista evidence; cada card com bbox crop inline + chain_of_custody timeline.

Componentes: `CaseTimelineLane`, `HypothesisTournamentCard`, `EvidenceChainCustody`, `WitnessVerdictBadge`, `ResidualUncertaintyBox`.

Tools de chat novos (em `web/lib/chat/tools.ts`): `read_case`, `read_hypothesis`, `read_evidence`, `list_open_cases`.

### Etapa 7 — Quality gate (incluído na fase 2)

`scripts/case/34_quality_check.py` valida as 6 rubrics no `case_report.md`. Saída JSON com scores. Gate threshold 0.85 (fase 2 inicial pode relaxar para 0.80 conforme prompts maturam).

## Consequences

### Positivas

- **5 cases âncora exemplares** demonstram o método. Confiança do usuário sobe.
- **Hypothesis tournament real** com posteriores calculados, não decoração.
- **Red-team forçado** evita âncora cognitiva — o produto se desafia.
- **Citações de chat ganham `evidence_card` artifact** (ADR-001) — UI mais rica.
- Quality rubrics no `case_report.md` viram **assinatura epistemológica** do Bureau.

### Negativas

- Pipeline LLM-intensivo. Custo estimado: ~$30-50 para os 5 casos âncora, ~$5 por case novo via demanda.
- Quality rubrics threshold 0.85 é alto; primeiros cases podem precisar revisão humana antes do publish.
- 12d de engenharia — fase pesada.

### Neutras

- Não toca chat protocol (ADR-001) nem retrieval pipeline. Adiciona camada.
- 24 tipos de markdown do CLAUDE.md ganham 7 com conteúdo real pela primeira vez.

## Risco mitigado

- **Hipóteses fabricadas (LLM inventa fatos):** prompt exige citação por `chunk_id` para cada claim; pipeline `32_evidence_audit.py` valida match em DB. Linha fabricada = hypothesis rejeitada.
- **Red-team viciado:** prompt adversarial é separado em outro arquivo, com persona explicitamente cética.
- **Quality rubrics objetivas vs subjetivas:** as 6 rubrics são todas auditáveis (counting custody steps, hypothesis count ≥3, residual entries ≥1, …). Não há "qualidade narrativa" como rubric — isso fica para a curadoria humana opcional.

## Validação

- 5 cases na `cases` table com `status='open'` ou `'closed'`.
- Cada case com ≥3 hipóteses; ≥1 com `red_team_pass=true`.
- `case_report.md` por case com `quality_rubrics` JSONB todas ≥0.80.
- UI `/case/case-nimitz-tic-tac-2004` renderiza completo (timeline + hypotheses + evidence + red-team).
- Chat tool `read_case` resolve via inline `case_card` artifact.

## Referências

- `CLAUDE.md §4` (24 tipos), `§8` (confidence calibration Tetlock), `§10` (procedência Locard), `§12` (quality gates).
- `CLAUDE-schema-full.md` (schemas YAML de evidence, hypothesis, witness, case_report, residual_uncertainty).
- Gap #2 do brief.
