phase-0: kill stubs, ship 20 curated anchor events, configure SMTP
- scripts/03-dedup-entities.py: stop emitting placeholder narrative ("Stub. Will
be enriched in Phase 7"); write summary_status=none + null fields instead.
- scripts/maintain/41_strip_stubs.py: idempotent migration that cleaned the
22,096 entity .md files (now zero stub strings in wiki/).
- scripts/synthesize/01_anchor_events.py: curated 20 anchor UAP events
(Roswell, Nimitz Tic-Tac, Phoenix Lights, Operação Prato, AATIP, etc.) with
bilingual Holmes-Watson narrative via claude -p --model sonnet
(CLAUDE_CODE_OAUTH_TOKEN). All summary_status=curated, confidence=high.
- web/api/timeline + timeline-view: filter narrative-less events by default,
render "curado" badge for hand-vetted ones, drop the date display alone.
- CLAUDE-schema-full.md: document the summary_status enum and the four states.
- docker-compose.yml: SMTP_HOST=mail.spacemail.com configured;
GOTRUE_MAILER_AUTOCONFIRM flipped to false (real email confirmation working).
- .nirvana/outputs/.../systems-atelier/: 5 deliverables of the architecture
audit that produced this roadmap.
This commit is contained in:
parent
19d0678e55
commit
4459bd17e4
46 changed files with 4418 additions and 17 deletions
63
.nirvana/outputs/disclosure-bureau/brief.md
Normal file
63
.nirvana/outputs/disclosure-bureau/brief.md
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
# Brief
|
||||
|
||||
**Business:** systems-atelier
|
||||
**Project ID:** disclosure-bureau
|
||||
**Submitted:** 2026-05-18T01:48:43Z
|
||||
|
||||
## Conteúdo
|
||||
|
||||
Audite e proponha plano de modernização completo do **The Disclosure Bureau** — sistema de investigação sobre documentos declassificados.
|
||||
|
||||
# Contexto
|
||||
|
||||
The Disclosure Bureau (https://disclosure.top) recebe documentos (PDF/imagens), extrai conteúdo via vision LLM, converte em markdown estruturado bbox-aware, gera embeddings densos + esparsos, popula grafo de co-menções entre entidades, e expõe uma wiki investigativa + agente de chat (Sherlock). Corpus atual: 116 PDFs do war.gov/ufo (DOD UAP, FBI Vault, NASA Apollo/Skylab, FOIA) → 3.435 páginas → 20.974 chunks → 34.370 entidades → 248.398 menções.
|
||||
|
||||
**Repo:** `/Users/guto/ufo` (commit baseline `19d0678`)
|
||||
**Live:** https://disclosure.top
|
||||
**Instrução canônica:** `/Users/guto/ufo/CLAUDE.md` + `/Users/guto/ufo/CLAUDE-schema-full.md`
|
||||
**Memory dev:** `/Users/guto/.claude/projects/-Users-guto-ufo/memory/MEMORY.md`
|
||||
|
||||
## Pipeline atual
|
||||
1. PDF → PNG 72 DPI (pdftoppm) + OCR (pdftotext)
|
||||
2. Chunking agêntico com Sonnet 4.6 via subagents → raw/<doc>--subagent/chunks/cNNNN.md (frontmatter YAML + corpo bilíngue EN+PT-BR)
|
||||
3. Embedding BGE-M3 (1024d) self-hosted FastAPI; reranker BGE-Reranker-v2-M3
|
||||
4. Postgres 15.8 + pgvector 0.8 + pg_trgm + tsvector → hybrid search RRF
|
||||
5. Entity mentions materializadas via ILIKE
|
||||
6. Next.js 15 (App Router) + Tailwind + Sigma.js/ForceAtlas2
|
||||
|
||||
## Stack self-hosted (Docker Compose em VPS)
|
||||
Next.js 15 / React 19 · Supabase (GoTrue, PostgREST, Realtime, Storage, Studio, Kong 2.8, Imgproxy, Meilisearch) · Postgres + pgvector · BGE-M3/Reranker (PyTorch CPU) · Traefik TLS · Sigma.js graphology.
|
||||
|
||||
## Restrições não-negociáveis
|
||||
- PROIBIDO ANTHROPIC_API_KEY. Usar CLAUDE_CODE_OAUTH_TOKEN (Claude Code SDK) OU OPENROUTER_API_KEY (fallback).
|
||||
- Bilíngue EN+PT-BR (português brasileiro, UTF-8 com acentos preservados).
|
||||
- Identificadores em inglês internacional.
|
||||
- Markdown puro como source-of-truth.
|
||||
- VPS self-hosted via Docker Compose. Sem SaaS proprietário no data plane.
|
||||
|
||||
## Funciona hoje
|
||||
Homepage bento 116 docs · Hybrid search 1.3s · Páginas chunk-a-chunk · Crops bbox auto-tighten · Allowlist image_types · Grafo Sigma.js ForceAtlas2 · Entity pages DB-live · Chat Sherlock (signup/login + Kong corrigido) · /admin/stats, /admin/batch, /admin/indexer.
|
||||
|
||||
## Gaps críticos
|
||||
1. Timeline (/timeline) — eventos extraídos mas todos com placeholder "_Stub. Will be enriched in Phase 7._" — usuário furioso com esse "Phase X"
|
||||
2. Case Bureau (case/case-report.md, hypotheses Tetlock, evidence Locard) — schema definido, pipeline nunca rodou
|
||||
3. Dashboards investigativos reais (não admin técnico) — clusters, geografia, atores, padrões temporais
|
||||
4. A2UI / AG-UI no chat — streaming tool-calls, artefatos clicáveis, crops bbox inline, multi-turn persistente
|
||||
5. ELIMINAR todos os "Stubs / Will be enriched in Phase X"
|
||||
|
||||
## Gaps médios
|
||||
Mobile responsivo precário · Sem onboarding · Sem export (PDF/CSV/PNG) · Sem saved views · Reranker 60s · Grafo trava em corpus grande.
|
||||
|
||||
## Visão longo prazo
|
||||
Multi-tenant/multi-corpus · API pública REST+GraphQL · MCP server · Visualização temporal-geo.
|
||||
|
||||
## Entregáveis solicitados
|
||||
1. codebase-analysis-report
|
||||
2. tech-debt-assessment
|
||||
3. product-discovery-brief
|
||||
4. system-architecture-doc
|
||||
5. ADRs onde necessário
|
||||
|
||||
Modo: zero-human. NÃO implementar agora — produzir análise + plano em fases priorizadas.
|
||||
|
||||
Outputs em ${PROJECTS_OUTPUT_DIR:-/Users/guto/ufo/.projects-outputs}/disclosure-bureau/businesses/systems-atelier/
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"schema_version": "1.0",
|
||||
"decisions": [],
|
||||
"open_questions": [],
|
||||
"project_id": "disclosure-bureau",
|
||||
"business_slug": "systems-atelier",
|
||||
"phase": "plan",
|
||||
"brief_original": "Audite e proponha plano de modernização completo do **The Disclosure Bureau** — sistema de investigação sobre documentos declassificados.\n\n# Contexto\n\nThe Disclosure Bureau (https://disclosure.top) recebe documentos (PDF/imagens), extrai conteúdo via vision LLM, converte em markdown estruturado bbox-aware, gera embeddings densos + esparsos, popula grafo de co-menções entre entidades, e expõe uma wiki investigativa + agente de chat (Sherlock). Corpus atual: 116 PDFs do war.gov/ufo (DOD UAP, FBI Vault, NASA Apollo/Skylab, FOIA) → 3.435 páginas → 20.974 chunks → 34.370 entidades → 248.398 menções.\n\n**Repo:** `/Users/guto/ufo` (commit baseline `19d0678`)\n**Live:** https://disclosure.top\n**Instrução canônica:** `/Users/guto/ufo/CLAUDE.md` + `/Users/guto/ufo/CLAUDE-schema-full.md`\n**Memory dev:** `/Users/guto/.claude/projects/-Users-guto-ufo/memory/MEMORY.md`\n\n## Pipeline atual\n1. PDF → PNG 72 DPI (pdftoppm) + OCR (pdftotext)\n2. Chunking agêntico com Sonnet 4.6 via subagents → raw/<doc>--subagent/chunks/cNNNN.md (frontmatter YAML + corpo bilíngue EN+PT-BR)\n3. Embedding BGE-M3 (1024d) self-hosted FastAPI; reranker BGE-Reranker-v2-M3\n4. Postgres 15.8 + pgvector 0.8 + pg_trgm + tsvector → hybrid search RRF\n5. Entity mentions materializadas via ILIKE\n6. Next.js 15 (App Router) + Tailwind + Sigma.js/ForceAtlas2\n\n## Stack self-hosted (Docker Compose em VPS)\nNext.js 15 / React 19 · Supabase (GoTrue, PostgREST, Realtime, Storage, Studio, Kong 2.8, Imgproxy, Meilisearch) · Postgres + pgvector · BGE-M3/Reranker (PyTorch CPU) · Traefik TLS · Sigma.js graphology.\n\n## Restrições não-negociáveis\n- PROIBIDO ANTHROPIC_API_KEY. Usar CLAUDE_CODE_OAUTH_TOKEN (Claude Code SDK) OU OPENROUTER_API_KEY (fallback).\n- Bilíngue EN+PT-BR (português brasileiro, UTF-8 com acentos preservados).\n- Identificadores em inglês internacional.\n- Markdown puro como source-of-truth.\n- VPS self-hosted via Docker Compose. Sem SaaS proprietário no data plane.\n\n## Funciona hoje\nHomepage bento 116 docs · Hybrid search 1.3s · Páginas chunk-a-chunk · Crops bbox auto-tighten · Allowlist image_types · Grafo Sigma.js ForceAtlas2 · Entity pages DB-live · Chat Sherlock (signup/login + Kong corrigido) · /admin/stats, /admin/batch, /admin/indexer.\n\n## Gaps críticos\n1. Timeline (/timeline) — eventos extraídos mas todos com placeholder \"_Stub. Will be enriched in Phase 7._\" — usuário furioso com esse \"Phase X\"\n2. Case Bureau (case/case-report.md, hypotheses Tetlock, evidence Locard) — schema definido, pipeline nunca rodou\n3. Dashboards investigativos reais (não admin técnico) — clusters, geografia, atores, padrões temporais\n4. A2UI / AG-UI no chat — streaming tool-calls, artefatos clicáveis, crops bbox inline, multi-turn persistente\n5. ELIMINAR todos os \"Stubs / Will be enriched in Phase X\"\n\n## Gaps médios\nMobile responsivo precário · Sem onboarding · Sem export (PDF/CSV/PNG) · Sem saved views · Reranker 60s · Grafo trava em corpus grande.\n\n## Visão longo prazo\nMulti-tenant/multi-corpus · API pública REST+GraphQL · MCP server · Visualização temporal-geo.\n\n## Entregáveis solicitados\n1. codebase-analysis-report\n2. tech-debt-assessment\n3. product-discovery-brief\n4. system-architecture-doc\n5. ADRs onde necessário\n\nModo: zero-human. NÃO implementar agora — produzir análise + plano em fases priorizadas.\n\nOutputs em ${PROJECTS_OUTPUT_DIR:-/Users/guto/ufo/.projects-outputs}/disclosure-bureau/businesses/systems-atelier/",
|
||||
"last_task_completed": null,
|
||||
"next_task_id": null,
|
||||
"audit_log_path": "audit.jsonl",
|
||||
"resumption_prompt_hint": "Project just received initial brief. Start at the brief_intake employee for systems-atelier.",
|
||||
"handoff_timestamp": "2026-05-18T01:48:43.851Z",
|
||||
"dag_snapshot_path": "dag-state.json"
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"ts":"2026-05-18T01:48:43Z","event":"brief_received","project_id":"disclosure-bureau","business_slug":"systems-atelier","brief_chars":3412}
|
||||
|
|
@ -0,0 +1,306 @@
|
|||
---
|
||||
title: "Codebase Analysis Report — The Disclosure Bureau"
|
||||
project: disclosure-bureau
|
||||
business: systems-atelier
|
||||
author: sa-principal (Principal Architect)
|
||||
baseline_commit: 19d0678
|
||||
generated_at: 2026-05-17
|
||||
schema_version: 0.1.0
|
||||
---
|
||||
|
||||
# 1. Codebase Analysis Report — as-is
|
||||
|
||||
Análise do estado atual do repositório `/Users/guto/ufo` no commit baseline `19d0678`, vivo em <https://disclosure.top>. Documento factual: o que existe, em que volume, com que qualidade. Recomendações ficam para os entregáveis 02–05.
|
||||
|
||||
## 1.1 Visão de mil pés
|
||||
|
||||
O Disclosure Bureau é um pipeline de ingestão Markdown-first + Next.js 15 + Supabase self-hosted, com retrieval híbrido BGE-M3 sobre 116 PDFs (3.435 páginas, 20.974 chunks bilingue, 34.370 entidades). É um sistema **brownfield maduro no pipeline de ingestão e raso na camada investigativa**: a base do "Karpathy LLM-wiki" funciona, mas as três promessas de produto (Timeline, Case Bureau, Investigative Dashboards) estão em estado de stub.
|
||||
|
||||
## 1.2 Layout físico
|
||||
|
||||
```
|
||||
/Users/guto/ufo/
|
||||
├── CLAUDE.md contrato vinculante v0.1.0 (10.5 KB, 24 tipos)
|
||||
├── CLAUDE-schema-full.md schema canônico (36.7 KB)
|
||||
├── README.md visão geral pública
|
||||
├── CORPUS-SNAPSHOT.md snapshot estatístico v0.2.0
|
||||
├── raw/ 252 entradas 116 PDFs imutáveis + 116 dirs `*--subagent` com chunks v0.2
|
||||
├── processing/ 11 subdirs PNG 72 DPI, OCR, vision JSON, crops, vídeos, frames UAP
|
||||
├── wiki/ 13 subdirs source-of-truth gerado (documents/, pages/, entities/, images-direct/, tables/, videos/)
|
||||
├── case/ 7 subdirs Investigation Bureau — TODOS VAZIOS (evidence, hypotheses, witnesses, ...)
|
||||
├── scripts/ 63 arquivos pipeline numerado 00–34 + ~30 scripts ad-hoc de rebuild
|
||||
├── infra/ 10 subdirs docker-compose disclosure-stack + embed-service + supabase migrations
|
||||
└── web/ Next.js 15 / React 19 app
|
||||
```
|
||||
|
||||
**Regra de ouro respeitada:** nenhum script escreve em `raw/<arquivo>.pdf` — só em `raw/<doc-id>--subagent/`.
|
||||
|
||||
## 1.3 Quantificação
|
||||
|
||||
### 1.3.1 Frontend Next.js (`/Users/guto/ufo/web/`)
|
||||
|
||||
| Métrica | Valor |
|
||||
|---|---|
|
||||
| LOC TypeScript/TSX (sem `node_modules`, `.next`) | **12.072** |
|
||||
| Rotas API (`route.ts`) | **19** |
|
||||
| Páginas (`page.tsx`) | **12** |
|
||||
| Componentes (`components/*.tsx`) | **29** |
|
||||
| Módulos de biblioteca (`lib/**/*.ts`) | **19** |
|
||||
| Stack runtime | Next.js 15.1 · React 19 · Tailwind 3.4 · TypeScript 5.6 |
|
||||
| Dependências de produção | 23 (24 com `@assistant-ui/react` instalado mas **não usado em runtime**) |
|
||||
|
||||
### 1.3.2 Pipeline Python (`/Users/guto/ufo/scripts/`)
|
||||
|
||||
| Métrica | Valor |
|
||||
|---|---|
|
||||
| LOC Python/Shell/JS | **25.298** |
|
||||
| Scripts numerados canônicos | 34 (`00-…` a `34-…`) |
|
||||
| Scripts ad-hoc de "rebuild doc X" | 28+ (`rebuild_doc65_*`, `rebuild_doc_d48.py`, etc.) — sinal de pipeline reativo |
|
||||
| Documentos processados pelo orquestrador `28-batch-rebuild-all.py` | 116/116 |
|
||||
| Chunks `c*.md` no disco | **20.974** |
|
||||
| Custo cumulativo declarado (`CORPUS-SNAPSHOT.md`) | ~$409 USD |
|
||||
|
||||
### 1.3.3 Infraestrutura (`/Users/guto/ufo/infra/`)
|
||||
|
||||
| Componente | Versão | Status |
|
||||
|---|---|---|
|
||||
| `db` (Supabase Postgres) | 15.8.1.060 | self-hosted, pgvector + pg_trgm + unaccent |
|
||||
| `auth` (GoTrue) | v2.170.0 | `MAILER_AUTOCONFIRM=true` (SMTP não configurado) |
|
||||
| `rest` (PostgREST) | v12.2.8 | OK |
|
||||
| `realtime` (Supabase) | v2.30.34 | OK |
|
||||
| `storage` (storage-api) | v1.14.3 | OK |
|
||||
| `imgproxy` | v3.8.0 | thumbnail/crop server |
|
||||
| `meta` (postgres-meta) | v0.83.2 | OK |
|
||||
| `studio` | 20241202 | admin UI |
|
||||
| `kong` | 2.8.1 | API gateway (corrigido para chat) |
|
||||
| `meilisearch` | v1.10 | provisionado, **não cabeado ainda ao web** |
|
||||
| `embed` (FastAPI) | local | BGE-M3 + BGE-Reranker-v2-M3 CPU |
|
||||
| `web` | Next 15.1 | Dockerfile próprio |
|
||||
| `traefik-public` | externo | reverse proxy TLS |
|
||||
|
||||
11 serviços + 1 rede externa + 4 volumes. Compose tem **367 linhas**.
|
||||
|
||||
### 1.3.4 Banco de dados — schema observado
|
||||
|
||||
Migrations em `/Users/guto/ufo/infra/supabase/migrations/`:
|
||||
|
||||
| Migration | Tabelas/funções |
|
||||
|---|---|
|
||||
| `0001_chat_schema.sql` (221 linhas) | `profiles`, `chat_sessions`, `messages`, `usage_events` + trigger `handle_new_user` |
|
||||
| `0002_chunks_retrieval.sql` (253 linhas) | `documents`, `chunks` (50 colunas, 8 índices, HNSW vector + 2 GIN tsvector + 2 GIN trgm), `entities`, `entity_mentions`, função `hybrid_search_chunks` (RRF server-side) |
|
||||
|
||||
**8 tabelas públicas + 1 RPC RRF**. RLS habilitada em todas as tabelas de retrieval (`SELECT` público, escrita só via `service_role`).
|
||||
|
||||
### 1.3.5 Corpus — números
|
||||
|
||||
| Indicador | Valor |
|
||||
|---|---|
|
||||
| PDFs originais (`raw/*.pdf`) | **116** |
|
||||
| Páginas (chunks `wiki/pages/<doc>/p*.md`) | **3.435** |
|
||||
| Diretórios `raw/<doc>--subagent/` | 116 (100% cobertura) |
|
||||
| Chunks bilíngues `c*.md` | **20.974** |
|
||||
| Imagens cropadas (`raw/*/images/`) | 752 |
|
||||
| Entidades únicas (`wiki/entities/**/*.md`) | **34.370** |
|
||||
| ↳ pessoas | 6.297 |
|
||||
| ↳ organizações | 3.747 |
|
||||
| ↳ locais | 4.969 |
|
||||
| ↳ eventos | **7.091** |
|
||||
| ↳ uap-objects | 1.775 |
|
||||
| ↳ veículos | 2.174 |
|
||||
| ↳ operações | 2.723 |
|
||||
| ↳ conceitos | 5.594 |
|
||||
| UFO anomaly chunks | 3.020 (14.4%) |
|
||||
| Cryptid anomaly chunks | 21 (0.1%) |
|
||||
|
||||
### 1.3.6 Stubs — o problema que enfurece o usuário
|
||||
|
||||
| Métrica | Valor |
|
||||
|---|---|
|
||||
| Arquivos `wiki/` com `Will be enriched in Phase X` | **22.096** |
|
||||
| Eventos com `narrative_summary: _Stub.` | **7.090 / 7.091 (99.99%)** |
|
||||
| Eventos com summary real | **1** |
|
||||
| Cobertura agregada de entidades com summary não-stub | ~36% |
|
||||
|
||||
A timeline em `/timeline` lê esses 7.091 eventos via `/api/timeline/route.ts` e os ordena por `date_start`. **O eixo do tempo funciona; o conteúdo não existe.** Cada card mostra `_Stub. Will be enriched in Phase 7._` — exatamente o que o usuário denuncia.
|
||||
|
||||
## 1.4 Arquitetura observada (não a alvo)
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────────────────────────────────────┐
|
||||
│ raw/*.pdf (imutável) │
|
||||
└──────────────────────┬─────────────────────────────────────────────────┘
|
||||
pdftoppm 72dpi │ pdftotext -layout
|
||||
▼
|
||||
┌────────────────────────────────────────────────────────────────────────┐
|
||||
│ processing/{png,ocr} → scripts/02-vision-page.py (Haiku 4.5) │
|
||||
│ → scripts/26-chunk-harness.py (Sonnet 4.6) │
|
||||
│ → scripts/28-batch-rebuild-all.py (orchestr.) │
|
||||
└──────────────────────┬─────────────────────────────────────────────────┘
|
||||
▼
|
||||
┌────────────────────────────────────────────────────────────────────────┐
|
||||
│ raw/<doc>--subagent/{chunks,images,tables}/ │
|
||||
│ wiki/{documents,pages,entities,images-direct}/ (markdown SoT) │
|
||||
│ case/{evidence,witnesses,hypotheses,…}/ ← VAZIO │
|
||||
└──────────────────────┬─────────────────────────────────────────────────┘
|
||||
scripts/30 (BGE-M3 1024d) + scripts/31 (entity_mentions ILIKE)
|
||||
▼
|
||||
┌────────────────────────────────────────────────────────────────────────┐
|
||||
│ Postgres 15.8 + pgvector + pg_trgm + tsvector (pt_unaccent/en_unaccent)│
|
||||
│ public.hybrid_search_chunks(text, vec, lang, …) → RRF top-k │
|
||||
└──────────────────────┬─────────────────────────────────────────────────┘
|
||||
▼ Stage-2 rerank (BGE-Reranker via FastAPI :8000)
|
||||
┌────────────────────────────────────────────────────────────────────────┐
|
||||
│ Next.js 15 App Router │
|
||||
│ ├─ Server: lib/wiki.ts (fs MD) + lib/retrieval/hybrid.ts (pg+embed) │
|
||||
│ ├─ Chat: lib/chat/openrouter.ts (Pattern C SSE + tool loop) + │
|
||||
│ │ lib/chat/claude-code.ts (OAuth subprocess, no tools) │
|
||||
│ └─ Auth: middleware.ts (Supabase SSR) + Kong gateway │
|
||||
└────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 1.4.1 Camadas de retrieval
|
||||
|
||||
Três camadas independentes coexistem e **não são unificadas**:
|
||||
|
||||
1. **Filesystem markdown** (`lib/wiki.ts`, 182 LOC) — leitura de `wiki/*.md` via `gray-matter`. Caminho rápido para document, page, entity. Usada pelo SSR das páginas e pelos tools `read_page`, `read_document`, `read_entity`.
|
||||
2. **Postgres hybrid** (`lib/retrieval/hybrid.ts`, 170 LOC) — RPC `hybrid_search_chunks` + rerank cliente. Usada pelo tool `hybrid_search` no chat e por `/api/search/hybrid`.
|
||||
3. **Graph** (`lib/retrieval/graph.ts`, 204 LOC) — `entity_mentions` materializada por scripts/31 via ILIKE. Usada pelos tools `entity_neighbors`, `entity_path`, `co_mention_chunks`.
|
||||
|
||||
Não há cache compartilhado entre elas. O grafo Sigma.js carrega o `wiki/graph.json` de **9.2 MB** estático — trava em corpus grande (confirmado pelo gap "Grafo trava em corpus grande" no brief).
|
||||
|
||||
### 1.4.2 Chat — Pattern C ad-hoc
|
||||
|
||||
`web/lib/chat/` (1.922 LOC) implementa um "AG-UI-style" próprio (`agui.ts`, 65 LOC), não o protocolo AG-UI oficial. Eventos SSE: `text_delta`, `tool_start`, `tool_result`, `navigate`, `done`, `error`. O frontend (`chat-bubble.tsx`, 507 LOC) consome SSE manualmente. **Sem retomada de stream, sem persistência intermediária, sem cancelamento limpo.**
|
||||
|
||||
Provedores:
|
||||
|
||||
- `openrouter.ts` (341 LOC) — default. Modelos free DeepSeek v4 / Nemotron. Loop de até 5 turnos de tool-calling client-side.
|
||||
- `claude-code.ts` (105 LOC) — `spawn("claude", ["-p", "--output-format", "json", …])` em subprocess, sem tools, timeout 90s. Lê `CLAUDE_CODE_OAUTH_TOKEN` do env. **Ainda não tem streaming nem tool calling.**
|
||||
|
||||
12 tools registradas em `tools.ts` (663 LOC), cobrindo retrieval, leitura, navegação. Sólido em escopo, mas o handler `search_corpus` ainda enumera `wiki/entities/*` por `fs.readdir` em loop — barato para 34k arquivos hoje, **não escala** para multi-tenant.
|
||||
|
||||
### 1.4.3 Auth & multi-tenancy
|
||||
|
||||
- GoTrue self-hosted com `GOTRUE_MAILER_AUTOCONFIRM=true` (SMTP não conectado). **Em produção sem SMTP, qualquer e-mail vira conta ativa imediatamente.** Risco aceito mas não trackeado.
|
||||
- `chat_sessions.user_id` referencia `auth.users(id)`. Sem `tenant_id`, sem `corpus_id`. **Multi-tenant = refactor de schema.**
|
||||
- RLS em `chunks`/`entities` é `SELECT … USING (TRUE)` — corpus público por design.
|
||||
|
||||
## 1.5 Qualidade do código
|
||||
|
||||
### 1.5.1 Pontos fortes
|
||||
|
||||
- **Comentários cabeçalho ricos em todos os módulos relevantes** (`web/lib/chat/openrouter.ts`, `web/lib/retrieval/hybrid.ts`, scripts numerados). Sinal de cuidado de design.
|
||||
- **Tipagem TS estrita** com interfaces explícitas em todas as fronteiras (`ChunkHit`, `ToolDefinition`, `AGUIEvent`, …).
|
||||
- **Idempotência declarada** em scripts críticos (`30-index-chunks-to-db.py --skip-existing`).
|
||||
- **Bilíngue por construção** desde o vision pass (campo `content_pt`/`content_en` espelhado em todo lugar; tsvector próprio para cada idioma).
|
||||
- **`CLAUDE.md` + `CLAUDE-schema-full.md` formam um contrato vinculante** auto-suficiente — Fowler aprovaria. Schema das 24 entidades, regex de IDs, naming canônico, idempotência: tudo escrito.
|
||||
- **Configuração de busca textual cuidadosa**: `pt_unaccent` e `en_unaccent` com `unaccent + portuguese_stem` — não é "ts_default" preguiçoso.
|
||||
- **`outputFileTracingIncludes` em `next.config.ts`** garante o bundle Docker carregar `wiki/`, `processing/`, `raw/*--subagent/`. Detalhe operacional não-trivial resolvido.
|
||||
|
||||
### 1.5.2 Pontos fracos
|
||||
|
||||
- **Zero testes automatizados.** `find /Users/guto/ufo -name "*.test.*" -o -name "*_test.py"` retorna 0 hits (excluindo `node_modules`). Sem regression suite, refactors são apostas.
|
||||
- **Sprawl de scripts ad-hoc**: 28+ arquivos `rebuild_doc<NN>*.py` no diretório `scripts/`. O ideal seria um único `rebuild.py --doc <id> --phase <n>`. Sinal de pipeline que era para ser robusto mas teve que ser remendado por documento.
|
||||
- **Acoplamento direto ao filesystem do host**: `UFO_ROOT = process.env.UFO_ROOT ?? "/Users/guto/ufo"` espalhado em `lib/wiki.ts`. O Docker mitiga via volume mount mas o código carrega a premissa de monorepo.
|
||||
- **`@assistant-ui/react` no `package.json` mas não importado em runtime** (`grep -r "assistant-ui" /Users/guto/ufo/web/{app,components,lib}` não retorna nada relevante). Dependência fantasma.
|
||||
- **`scripts/03-dedup-entities.py` é o vilão dos stubs.** Linhas 167–169 escrevem literalmente `narrative_summary: _Stub. Will be enriched in Phase 7._` em todo entity sintetizada. Phase 7 nunca foi implementada como passe sistemático.
|
||||
- **Grafo client-side custoso**: `wiki/graph.json` = 9.2 MB carregado inteiro no browser pelo Sigma.js. Sem viewport-aware lazy load.
|
||||
- **Reranker 60s** (gap declarado no brief) — `signal: AbortSignal.timeout(60_000)` em `lib/retrieval/embed.ts` é um workaround, não solução. BGE-Reranker-v2-M3 CPU em VPS pequena tem cold start de ~30s + 200-500ms por par query/doc.
|
||||
- **Sem migrations versionadas para o schema da wiki** — a "evolução" do schema dos 24 tipos é manual via `CLAUDE-schema-full.md`. Lint é manual em `scripts/04-lint.py`.
|
||||
|
||||
### 1.5.3 Hotspots de complexidade
|
||||
|
||||
| Arquivo | LOC | Justificativa |
|
||||
|---|---|---|
|
||||
| `web/lib/chat/tools.ts` | **663** | 12 tools + handlers + schemas inline. Candidato número 1 a quebra modular. |
|
||||
| `web/components/chat-bubble.tsx` | **507** | UI + state + auth + SSE parser + tool rendering em um arquivo. |
|
||||
| `web/lib/chat/openrouter.ts` | **341** | Loop de tool-call client-side; SSE parser manual de 60+ linhas. |
|
||||
| `scripts/28-batch-rebuild-all.py` | ~700 | Orquestrador `ThreadPoolExecutor` com retries e quota; razoável dado o domínio. |
|
||||
| `infra/disclosure-stack/docker-compose.yml` | **367** | 11 serviços; longo mas plano. |
|
||||
|
||||
## 1.6 Dependências externas
|
||||
|
||||
### 1.6.1 Frontend (`web/package.json`)
|
||||
|
||||
- **Hard deps**: `next@^15.1`, `react@^19`, `@supabase/ssr@^0.10`, `@supabase/supabase-js@^2.105`, `pg@^8.13`, `gray-matter@^4.0`, `sharp@^0.33`, `react-markdown@^9`, `remark-wiki-link@^2`.
|
||||
- **Graph stack**: `sigma@^3`, `graphology@^0.25`, `graphology-layout-forceatlas2@^0.10`, `@react-sigma/core@^5`, `@react-sigma/layout-forceatlas2@^5`, `react-force-graph-2d@^1.27` — **duas libs de grafo coexistem** (sigma e react-force-graph). Decisão pendente.
|
||||
- **UI**: `@radix-ui/react-dialog@^1.1`, `@radix-ui/react-tooltip@^1.1`, `lucide-react@^0.460`, `framer-motion@^11.11`.
|
||||
- **Fantasma**: `@assistant-ui/react@^0.14` declarado e não usado.
|
||||
|
||||
### 1.6.2 Backend ML
|
||||
|
||||
- **embed-service** (`infra/embed-service/`, 148 LOC FastAPI): `FlagEmbedding` (BGE-M3 + BGE-Reranker-v2-M3). CPU-only via env `DEVICE=cpu`. Lazy load com `Lock` para thread safety. ~2.5 GB RAM resident.
|
||||
- Sem GPU declarada. Reranker quente reusa o modelo; cold start carrega ambos os modelos em sequência.
|
||||
|
||||
### 1.6.3 LLM providers
|
||||
|
||||
- **`OPENROUTER_API_KEY`** — chat default (DeepSeek v4 free, Nemotron 3 fallback).
|
||||
- **`CLAUDE_CODE_OAUTH_TOKEN`** — Claude Code CLI subprocess para scripts Python (vision, chunking, synthesis) **e** para o chat em modo `claude-code`.
|
||||
- **ANTHROPIC_API_KEY proibido** — verificado: o único hit em código vivo é uma string de aviso "NEVER use ANTHROPIC_API_KEY" em comments. Há referência stale em `infra/coolify/NEXTJS.md:30` que precisa ser removida (documentação antiga).
|
||||
|
||||
### 1.6.4 OS-level
|
||||
|
||||
- `pdftoppm` (Poppler) e `pdftotext -layout` — ambos invocados por `01-convert-pdfs.sh`.
|
||||
- `claude` CLI no PATH do container — instalação via curl no Dockerfile do web (atualmente **não está no Dockerfile do `web/Dockerfile`** — o `claude-code.ts` falha silenciosamente em container sem o binário; a fallback para OpenRouter cobre, mas a Pattern C com Claude oficial nunca rodou).
|
||||
|
||||
## 1.7 Operacional
|
||||
|
||||
- **Deploy:** Docker Compose. Roteamento via `traefik-public` externo (network já existente do host). `DEPLOY-CHECKLIST.md` em `infra/` mantém runbook.
|
||||
- **Observabilidade:** `wiki/log.md` (459 KB, append-only) é o único log estruturado da pipeline de ingestão. `raw/_batch-rebuild/progress.jsonl` é lido pela rota `/api/admin/batch`. **Sem Prometheus/Grafana, sem error tracking (Sentry), sem trace distribuído.**
|
||||
- **Backups:** não declarados. Volume `db-data` do Postgres é o único cofre — sem dump automatizado documentado.
|
||||
- **CI/CD:** ausente. Push direto via `git`; build local. Não existe `.github/workflows`.
|
||||
- **Secret hygiene:** `.env.backup.1778796747` em `infra/disclosure-stack/` contém credenciais reais (OAuth, OpenRouter, JWT). **Precisa sair do git-history.**
|
||||
|
||||
## 1.8 Documentação
|
||||
|
||||
- `CLAUDE.md` (10.5 KB) — contrato canônico, atualizado e usado.
|
||||
- `CLAUDE-schema-full.md` (36.7 KB) — schema dos 24 tipos. Vivo.
|
||||
- `README.md` (6.3 KB) — visão geral do produto, quickstart, stack. Vivo.
|
||||
- `CORPUS-SNAPSHOT.md` (3.6 KB) — última snapshot da pipeline. Vivo (gerado 2026-05-17).
|
||||
- `infra/DEPLOY-CHECKLIST.md` (6.8 KB) — runbook.
|
||||
- `infra/RETRIEVAL.md` (3.6 KB) — explicação do retrieval híbrido.
|
||||
- `wiki/log.md` (459 KB) — log append-only de operações de pipeline. Inflado.
|
||||
|
||||
A documentação é desproporcionalmente forte do lado **filosofia/contrato** e fraca do lado **ADR/decisões técnicas históricas**. Não há um ADR único no repo.
|
||||
|
||||
## 1.9 Síntese dos hotspots
|
||||
|
||||
| Hotspot | Localização | Impacto |
|
||||
|---|---|---|
|
||||
| **Timeline + 7.091 stubs** | `scripts/03-dedup-entities.py:167-169`, `wiki/entities/events/*.md` | UX bloqueada (gap #1 do brief) |
|
||||
| **Case Bureau vazio** | `case/{evidence,hypotheses,witnesses,timelines,profiles,connect-the-dots}/` (0 arquivos) | A promessa investigativa do produto não existe (gap #2) |
|
||||
| **Dashboards = admin técnico** | `web/app/admin/stats/` (272 LOC) | Não há dashboard investigativo (gap #3) |
|
||||
| **Chat sem AG-UI real** | `web/lib/chat/agui.ts` (65 LOC) | "AG-UI style" caseiro, sem retomada, sem artefato tipado (gap #4) |
|
||||
| **Sigma.js graph.json 9.2 MB** | `wiki/graph.json` | Trava no browser (gap #5 médio) |
|
||||
| **Reranker cold start** | `infra/embed-service/app.py` (lazy load) + `web/lib/retrieval/embed.ts` timeout 60s | UX search lenta em primeira query |
|
||||
| **Stubs em 22.096 arquivos** | `wiki/entities/**/*.md` | Erosão de confiança total |
|
||||
| **Sem testes** | repo todo | Risco de regressão a cada refactor |
|
||||
| **Sprawl `rebuild_doc*.py`** | `scripts/rebuild_doc*.py` (28+) | Manutenibilidade do pipeline |
|
||||
| **`@assistant-ui/react` fantasma** | `web/package.json` | Decisão técnica adiada |
|
||||
| **`.env.backup.*` versionada** | `infra/disclosure-stack/.env.backup.1778796747` | Secret leak |
|
||||
|
||||
## 1.10 O que está bom e deve ser preservado
|
||||
|
||||
1. **Markdown como source-of-truth** com IDs canônicos regex-validados. O contrato `CLAUDE.md` é uma alavanca de longo prazo (Larson).
|
||||
2. **Bilíngue desde o vision pass** — não é tradução tardia, é produto.
|
||||
3. **Hybrid search RRF em SQL pure**: `public.hybrid_search_chunks` evita orquestração de duas queries no app — boa decisão.
|
||||
4. **OpenRouter + Claude Code OAuth como única política de LLM** — alinhado à restrição inegociável do projeto.
|
||||
5. **Self-hosted no data plane** (Postgres, pgvector, BGE-M3 local, GoTrue, PostgREST, Storage). Sem SaaS no caminho dos dados.
|
||||
6. **Bbox-aware chunking**: cada chunk carrega `bbox` JSONB → permite citações com crop visual.
|
||||
|
||||
## 1.11 O que sangra agora e precisa de bisturi
|
||||
|
||||
Em ordem de impacto sobre o usuário (não por dificuldade):
|
||||
|
||||
1. **Fase 0 — Eliminar todos os stubs "Phase X"** (entrega visível em 24h, dor zero técnica).
|
||||
2. **Timeline real** — pipeline de enrichment ou de filtro: "exibe só eventos com narrative_summary não-stub". Subentendido na fase 0 mas com produto próprio.
|
||||
3. **Case Bureau pipeline ground-zero** — hypotheses, evidence, witnesses ainda vazios; isso é a alma do produto.
|
||||
4. **AG-UI verdadeiro no chat** — substituir `agui.ts` caseiro pelo protocolo AG-UI ou por `assistant-ui/react` real (já no `package.json`).
|
||||
5. **Dashboards investigativos** — clusters, geografia, atores, padrões temporais. Não admin técnico.
|
||||
6. **Grafo `graph.json` 9.2 MB** — paginar / lazy ou trocar Sigma por Cytoscape com server-side hub-and-spoke.
|
||||
|
||||
Os detalhes de priorização, esforço e fases vão no entregável **04-system-architecture-doc.md**. Os trade-offs vão nos ADRs em **05-adrs/**.
|
||||
|
||||
---
|
||||
|
||||
_Análise feita em commit `19d0678` baseline. Sem mudanças no código._
|
||||
|
|
@ -0,0 +1,391 @@
|
|||
---
|
||||
title: "Tech Debt Assessment — The Disclosure Bureau"
|
||||
project: disclosure-bureau
|
||||
business: systems-atelier
|
||||
author: sa-principal (Principal Architect)
|
||||
baseline_commit: 19d0678
|
||||
generated_at: 2026-05-17
|
||||
schema_version: 0.1.0
|
||||
---
|
||||
|
||||
# 2. Tech Debt Assessment — priorizado
|
||||
|
||||
Cada débito carrega: **severidade** (`high` / `med` / `low`), **categoria** (UX | Pipeline | Arch | Sec | Ops | DevEx), **localização concreta no repo**, **esforço estimado em homem-dias** (`d`) por engenheiro sênior, e **bloqueio percebido** (o que ele impede).
|
||||
|
||||
A precificação é baseada na escala observada (116 docs, 20.974 chunks, 34.370 entidades) e na ausência de testes — qualquer refactor exige escrever o teste antes.
|
||||
|
||||
## 2.1 Sumário
|
||||
|
||||
| # | Severidade | Débito | Esforço |
|
||||
|---|---|---|---|
|
||||
| 1 | **HIGH** | Stubs "Phase X" em 22.096 arquivos de wiki | 3d (saneamento) + 8d (enrichment real, opcional) |
|
||||
| 2 | **HIGH** | Case Bureau (Tetlock+Locard) nunca rodou | 12d |
|
||||
| 3 | **HIGH** | Timeline mostra 7.090 eventos com `_Stub. Will be enriched in Phase 7._` | 4d (filtra+narrar) ou rola na fase 0 |
|
||||
| 4 | **HIGH** | Chat "AG-UI style" caseiro, sem padrão | 5d (adotar AG-UI ou assistant-ui) |
|
||||
| 5 | **HIGH** | Dashboards investigativos inexistentes (só /admin/stats técnico) | 8d |
|
||||
| 6 | **HIGH** | Zero testes automatizados | 4d (smoke + retrieval golden set) + ongoing |
|
||||
| 7 | **MED** | Reranker cold start ~30s + 60s timeout client-side | 3d (warm-pool + queue) |
|
||||
| 8 | **MED** | Sigma.js consome `graph.json` 9.2 MB inteiro | 4d (server-side neighbors) |
|
||||
| 9 | **MED** | `entity_mentions` materializada por ILIKE (pode duplicar/perder) | 3d |
|
||||
| 10 | **MED** | 28+ scripts `rebuild_doc<NN>*.py` ad-hoc | 4d (unificar em `rebuild.py --doc`) |
|
||||
| 11 | **MED** | GoTrue com `MAILER_AUTOCONFIRM=true` sem SMTP | 1d (configurar SMTP) ou 0.5d (gate por allowlist temporária) |
|
||||
| 12 | **MED** | `.env.backup.1778796747` com credenciais reais commitada | 0.5d + rotação dos segredos |
|
||||
| 13 | **MED** | `@assistant-ui/react` no `package.json` e não usado | 1d (decisão + remoção ou adoção) |
|
||||
| 14 | **MED** | Schema sem `tenant_id`/`corpus_id` (multi-tenant futuro) | 4d (schema) + 6d (UI/refactor) |
|
||||
| 15 | **LOW** | `wiki/log.md` 459 KB monolítico | 1d (rotacionar) |
|
||||
| 16 | **LOW** | Dois libs de grafo coexistem (sigma + react-force-graph) | 1d (escolher uma, remover outra) |
|
||||
| 17 | **LOW** | `outputFileTracingIncludes` cobre paths absolutos do dev | 1d (consolidar via env) |
|
||||
| 18 | **LOW** | Falta CI/CD | 2d (GitHub Actions: lint + build + smoke) |
|
||||
| 19 | **LOW** | Sem observabilidade (logs, metrics, error tracking) | 3d (Loki + Grafana + Sentry self-hosted) |
|
||||
| 20 | **LOW** | Documentação ADR ausente | coberto neste deliverable + onging |
|
||||
|
||||
**Total estimado:** ~75 homem-dias para zerar o débito high/med a profundidade média. Quanto disso fica em fases vai no entregável 04.
|
||||
|
||||
---
|
||||
|
||||
## 2.2 Detalhamento
|
||||
|
||||
### 2.2.1 [HIGH] Stubs "Phase X" — erosão de confiança total
|
||||
|
||||
**Fonte do mal:** `scripts/03-dedup-entities.py`, linhas 167-169, escreve literalmente:
|
||||
|
||||
```python
|
||||
"narrative_summary": "_Stub. Will be enriched in Phase 7._",
|
||||
"narrative_summary_pt_br": "_Stub. Será enriquecido na Fase 7._",
|
||||
"_Stub generated by entity dedup. Will be enriched in Phase 6._\n\n"
|
||||
```
|
||||
|
||||
**Distribuição atual:**
|
||||
|
||||
- 22.096 arquivos em `wiki/entities/**/*.md` carregam "Will be enriched"
|
||||
- 7.090 / 7.091 events com `narrative_summary: _Stub.`
|
||||
- ~64% das 34.370 entidades sem narrativa real
|
||||
|
||||
**Por que fere o usuário:** o produto promete "wiki investigativa". O leitor entra em qualquer entidade não-trivial (`/e/event/EV-1492-…`) e lê _"Será enriquecido na Fase 7"_. A mensagem comunica: **isto é alpha incompleto**. O usuário declarou furor explícito no brief.
|
||||
|
||||
**Esforço:**
|
||||
|
||||
- **Saneamento básico — 3d.** Reescrever `03-dedup-entities.py` para nunca emitir stub textual; entidades sem narrativa exibem badge "sem síntese ainda" no UI e ficam fora dos listings públicos por default. Rodar um script `scripts/35-strip-stubs.py` que substitui o texto pelo campo vazio + booleano `needs_synthesis: true`.
|
||||
- **Enrichment real — 8d (opcional, pode ser fila contínua).** Pipeline `scripts/36-enrich-entities.py` que itera entidades com `total_mentions ≥ N`, recupera chunks via `entity_mentions`, sintetiza com Claude Code OAuth, escreve `narrative_summary` real. Custo: ~$80-150 dependendo do limiar.
|
||||
|
||||
**Categoria:** UX + Pipeline. **Bloqueia:** Timeline, Entity pages, Case Bureau.
|
||||
|
||||
---
|
||||
|
||||
### 2.2.2 [HIGH] Case Bureau — schema definido, pipeline jamais rodou
|
||||
|
||||
**Estado físico:**
|
||||
|
||||
```
|
||||
case/
|
||||
├── connect-the-dots/ 0 files
|
||||
├── evidence/ 0 files
|
||||
├── gaps/ (only 2 placeholder gaps from initial bootstrap)
|
||||
├── hypotheses/ 0 files
|
||||
├── profiles/ 0 files
|
||||
├── timelines/ 0 files
|
||||
└── witnesses/ 0 files
|
||||
```
|
||||
|
||||
**O que o CLAUDE.md promete:** 12 tipos investigativos (evidence, witness, hypothesis, gap, relation, timeline, actor_profile, case_report, residual_uncertainty, …) com chain-of-custody Locard, hypothesis tournament Tetlock, ≥3 hipóteses por caso, posteriores bayesianos, 6 quality rubrics threshold 0.85.
|
||||
|
||||
**O que existe:** nada. Os 8 detetives (Holmes/Poirot/Dupin/Locard + Schneier/Tetlock/Taleb) são copy do README; não há agente rodando, não há output.
|
||||
|
||||
**Esforço — 12d quebrado em 3 etapas:**
|
||||
|
||||
1. **3d — Define-and-seed.** Escrever `scripts/40-case-seed.py` que cria 5 casos âncora (Roswell, Nimitz Tic-Tac, Phoenix Lights, FBI Vault Hottel memo, NASA Apollo radio anomaly). Cada caso = 1 timeline, 3-5 evidence cards com `verbatim_excerpt` + `source_page` + `bbox`, 2-3 witness_analysis com `verdict`, ≥3 hipóteses concorrentes com priors Tetlock.
|
||||
2. **4d — Hypothesis engine.** `scripts/41-hypothesis-tournament.py` lê evidências, gera 3+ hipóteses via LLM, calcula posterior via Bayes simples (prior × likelihood), ranqueia, escreve `case/hypotheses/H-NNNN.md` + atualiza `case-report.md`. Modelo: Claude Code OAuth, Sonnet para case-writer / red-team review.
|
||||
3. **5d — UI surface.** Novas rotas Next.js: `/case`, `/case/[id]`, `/case/[id]/hypotheses`, `/case/[id]/evidence`. Componentes: `CaseTimelineLane`, `HypothesisTournamentCard`, `EvidenceChainCustody`, `WitnessVerdictBadge`. Linka para `[[evidence/E-0042]]` resolvido pelo router.
|
||||
|
||||
**Categoria:** Arch + UX + Pipeline. **Bloqueia:** posicionamento como "super centro de investigação" (visão do brief).
|
||||
|
||||
---
|
||||
|
||||
### 2.2.3 [HIGH] Timeline — funciona mas mostra 99.99% de stubs
|
||||
|
||||
**Localização:**
|
||||
|
||||
- `web/app/api/timeline/route.ts` (119 LOC) — lê `wiki/entities/events/*.md`, ordena por `date_start`. Funciona.
|
||||
- `web/components/timeline-view.tsx` (143 LOC) — agrupa por década, lista cards. Funciona.
|
||||
- **Dados:** 7.090 dos 7.091 events com `narrative_summary: _Stub.`.
|
||||
|
||||
**O bug não é a Timeline; é o conteúdo.** Mas o usuário vê a Timeline. Daí ela vira o sintoma.
|
||||
|
||||
**Esforço — 4d (curto-prazo) ou rola junto com 2.2.1:**
|
||||
|
||||
1. **1d** — Filtro server-side em `/api/timeline`: por default só devolve eventos com `narrative_summary` ausente do regex `/^_Stub/`. Query param `?include_stubs=true` para o caso real (ainda 1 evento; vai aumentar conforme enrichment roda).
|
||||
2. **1d** — UI: separar "Eventos consolidados" (com narrativa) e "Eventos catalogados" (count agregado por década) em duas faixas da Timeline.
|
||||
3. **2d** — Camada de canonização: 50-100 eventos curados manualmente (Roswell, Nimitz, Phoenix, Aurora, Foo-fighters, Operation Mainbrace, AATIP, etc.) com narrativa rica escrita pelo case-writer. Esses ficam pinned no topo de cada década.
|
||||
|
||||
**Categoria:** UX. **Bloqueia:** primeira impressão da feature.
|
||||
|
||||
---
|
||||
|
||||
### 2.2.4 [HIGH] Chat — "AG-UI style" caseiro
|
||||
|
||||
**Localização:**
|
||||
|
||||
- `web/lib/chat/agui.ts` (65 LOC) — eventos `text_delta`, `tool_start`, `tool_result`, `navigate`, `done`, `error`.
|
||||
- `web/lib/chat/openrouter.ts` (341 LOC) — SSE parser + loop de tool-call manual.
|
||||
- `web/components/chat-bubble.tsx` (507 LOC) — UI consumindo SSE manualmente.
|
||||
|
||||
**Limites:**
|
||||
|
||||
- Sem retomada de stream em queda de conexão.
|
||||
- Sem persistência intermediária — se o usuário fecha o modal no meio, o assistant message é perdido.
|
||||
- Sem cancelamento limpo (abort do `fetch` não chega ao backend, que segue executando tools).
|
||||
- Sem artefatos tipados — citações são strings markdown parseadas no client.
|
||||
- Sem crops bbox inline no chat (citação aponta para `/d/<doc>#<chunk>` mas o crop só renderiza fora do chat).
|
||||
|
||||
**Esforço — 5d:**
|
||||
|
||||
1. **2d** — Decidir entre adotar [AG-UI](https://github.com/ag-ui-protocol/ag-ui) puro (mais alinhado ao trace "/state/run" do protocolo) **ou** adotar `@assistant-ui/react` (já no `package.json`, ainda não usado). ADR-001 cobre.
|
||||
2. **2d** — Refactor `chat-bubble.tsx` → componentes pequenos (`ChatComposer`, `ChatStream`, `ToolBlock`, `CitationCrop`, `NavigationOffer`). Cada citação `chunk_id` vira `<Citation/>` com bbox crop inline via `/api/crop`.
|
||||
3. **1d** — Persistência incremental: cada `text_delta` flush no `messages.content` via Realtime, não ao final. Aborto via `AbortController` chega ao loop server-side via header `x-chat-cancel: <session_id>`.
|
||||
|
||||
**Categoria:** UX + Arch. **Bloqueia:** experiência conversacional sólida.
|
||||
|
||||
---
|
||||
|
||||
### 2.2.5 [HIGH] Dashboards investigativos inexistentes
|
||||
|
||||
**Estado:** existe `/admin/stats` que mostra contadores técnicos (docs/chunks/entities/redactions). Não existe **dashboard investigativo**.
|
||||
|
||||
**Falta:**
|
||||
|
||||
- **Clusters** de eventos por proximidade temporal+geográfica (KMeans sobre `date_start` + `primary_location` geocoded).
|
||||
- **Mapa**: choropleth/scatter geográfico de sightings (precisa geocode em `primary_location`).
|
||||
- **Atores recorrentes**: top-50 pessoas/organizações por menções, com sparkline temporal.
|
||||
- **Padrões**: redaction-heavy ratio por collection; UFO anomaly heatmap por ano; cryptid hotspots.
|
||||
- **Connect-the-dots view**: para um caso aberto, qual o subgrafo mínimo de entidades conectadas.
|
||||
|
||||
**Esforço — 8d:**
|
||||
|
||||
1. **2d** — Geocoding pipeline (`scripts/42-geocode-locations.py`) usando Nominatim self-host (Docker) — sem SaaS. Persistir lat/lon em `entities` via colunas `lat`, `lon`, `geo_confidence`.
|
||||
2. **3d** — Rotas e componentes Next: `/dashboard/clusters`, `/dashboard/map`, `/dashboard/actors`, `/dashboard/patterns`. Lib: `react-simple-maps` ou MapLibre GL (open-source, self-hosted tiles).
|
||||
3. **3d** — Conteúdo: queries Postgres dedicadas (views materializadas atualizadas em incremental), componentes de gráfico (Recharts ou Visx). Linka tudo de volta para entities/chunks.
|
||||
|
||||
**Categoria:** UX + Arch. **Bloqueia:** posicionamento "super centro de investigação".
|
||||
|
||||
---
|
||||
|
||||
### 2.2.6 [HIGH] Zero testes automatizados
|
||||
|
||||
`find /Users/guto/ufo -name "*.test.*" -o -name "*_test.py" -o -name "*.spec.*"` → 0 hits (excluindo `node_modules`).
|
||||
|
||||
**Risco:**
|
||||
|
||||
- Refactors em `scripts/03-dedup-entities.py` (vilão dos stubs) podem corromper 22k arquivos.
|
||||
- Mudança no schema do chat (`agui.ts`) sem regression test quebra o SSE no browser.
|
||||
- Migration `0003_*` futura sem teste pode dropar coluna usada por tool handler.
|
||||
|
||||
**Esforço — 4d para um piso mínimo:**
|
||||
|
||||
1. **1d** — Vitest no `web/`, smoke de cada `route.ts` (`/api/timeline`, `/api/search/hybrid`, `/api/chunk`, …). Fixture: subset de 3 docs com 10 chunks.
|
||||
2. **1d** — Golden retrieval set: 30 queries PT+EN com top-5 chunks esperados; CI valida recall@5 ≥ 0.7.
|
||||
3. **1d** — pytest para scripts críticos (`03-dedup-entities`, `30-index-chunks-to-db`, `04-lint`). Dataset: 1 PDF de 3 páginas com fixture YAML pronta.
|
||||
4. **1d** — `.github/workflows/ci.yml`: `pnpm lint && pnpm build && pytest && curl /api/admin/stats`.
|
||||
|
||||
**Categoria:** DevEx + Quality. **Bloqueia:** velocidade de mudança segura.
|
||||
|
||||
---
|
||||
|
||||
### 2.2.7 [MED] Reranker cold start + timeout 60s
|
||||
|
||||
**Localização:** `web/lib/retrieval/embed.ts:16` (`signal: AbortSignal.timeout(60_000)`). `infra/embed-service/app.py` faz lazy load com `Lock`; primeira chamada após reinício baixa ~2.5 GB de modelo.
|
||||
|
||||
**Sintoma:** primeira query do dia leva 30-60s. Brief lista isso como gap médio.
|
||||
|
||||
**Esforço — 3d:**
|
||||
|
||||
1. **1d** — Warm-pool: container do embed-service ganha um `healthcheck` que dispara `/embed` com `["warmup"]` no startup. `restart: unless-stopped` mantém quente.
|
||||
2. **1d** — Mover rerank para fila assíncrona com SSE event `rerank_progress` (`text_delta` no chat enquanto roda). Pré-render de top-20 RRF, rerank atualiza inline.
|
||||
3. **1d** — Cache de rerank: hash `(query_norm, candidate_ids)` → score, TTL 24h. Postgres tabela `rerank_cache(hash TEXT PK, scores REAL[], created_at)`.
|
||||
|
||||
**Categoria:** Performance. **Bloqueia:** UX de busca.
|
||||
|
||||
---
|
||||
|
||||
### 2.2.8 [MED] Sigma.js `graph.json` 9.2 MB
|
||||
|
||||
**Localização:** `wiki/graph.json` (9.2 MB), carregado por `web/components/sigma-graph-client.tsx` e `force-graph-canvas.tsx` no cliente.
|
||||
|
||||
**Esforço — 4d:**
|
||||
|
||||
1. **2d** — Endpoint server-side `/api/graph/neighbors?node=<id>&depth=1&limit=50`: BFS no `entity_mentions` retorna só vizinhança da query, ~30 KB.
|
||||
2. **1d** — Carregar inicial: hub-and-spoke dos 100 entities com `total_mentions > N` (≤200 KB), expand-on-click busca vizinhos.
|
||||
3. **1d** — Decidir uma lib: ADR-006 (sigma vs react-force-graph). Remover a outra.
|
||||
|
||||
**Categoria:** Performance + DevEx. **Bloqueia:** /graph escalar para mais corpora.
|
||||
|
||||
---
|
||||
|
||||
### 2.2.9 [MED] `entity_mentions` via ILIKE
|
||||
|
||||
**Localização:** `scripts/31-populate-entity-mentions.py`. A materialização do grafo usa ILIKE de `canonical_name` e `aliases` contra `content_en/pt` — frágil para nomes ambíguos ("Hoover", "Park") e perde menções com variações ortográficas.
|
||||
|
||||
**Esforço — 3d:**
|
||||
|
||||
1. **1d** — Substituir ILIKE por `tsvector` match com `pt_unaccent/en_unaccent`, ranqueado.
|
||||
2. **1d** — Adicionar coluna `mention_confidence REAL` baseado em (alias match exato vs canonical match vs fuzzy via pg_trgm `%`).
|
||||
3. **1d** — Pipeline diff: nova execução compara contagens com versão anterior e reporta variações > 10% como red flag.
|
||||
|
||||
**Categoria:** Pipeline + Quality. **Bloqueia:** grafo confiável.
|
||||
|
||||
---
|
||||
|
||||
### 2.2.10 [MED] Sprawl `rebuild_doc<NN>*.py`
|
||||
|
||||
**Lista parcial:** `rebuild_doc65.py`, `rebuild_doc65_v2.py`, `rebuild_doc65_full.py`, `rebuild_doc65_gemini.py`, `rebuild_doc65_page_rebuilder.py`, `rebuild_doc65_s2_v2.py`, `rebuild_doc65_s8.py`, `rebuild_doc65_section2..8.py`, `rebuild_doc65_serial130_resume.py`, `rebuild_doc65_suba_final.py`, `rebuild_doc_d48.py`, `rebuild_doc_section3.py`, `rebuild_doc255.py`, `rebuild_doc38.py`, … 28+ arquivos.
|
||||
|
||||
**Esforço — 4d:**
|
||||
|
||||
1. **2d** — Consolidar em `scripts/rebuild.py --doc <doc-id> --phase <vision|chunk|enrich|index>` com flags por seção. Parâmetros que hoje viram nomes de arquivo viram args.
|
||||
2. **1d** — Mover os 28 arquivos para `scripts/_archive/` antes de remover (preservar histórico de hot-fix).
|
||||
3. **1d** — README dos scripts canônicos: cada `NN-...` ganha purpose, inputs, outputs.
|
||||
|
||||
**Categoria:** DevEx. **Bloqueia:** novos engenheiros entendendo o pipeline.
|
||||
|
||||
---
|
||||
|
||||
### 2.2.11 [MED] GoTrue `MAILER_AUTOCONFIRM=true` sem SMTP
|
||||
|
||||
**Localização:** `infra/disclosure-stack/docker-compose.yml:81`.
|
||||
|
||||
**Risco:** qualquer e-mail (inclusive throwaway) vira conta ativa. Sem rate-limit no signup. Em produção pública, isso é vetor de spam/abuso e infla `chat_sessions` com lixo.
|
||||
|
||||
**Esforço:**
|
||||
|
||||
- **0.5d** — allowlist temporária: trigger `BEFORE INSERT ON auth.users` rejeita domínios fora de `('disclosure.top', 'plataformainfraredmed.org', allowed_domains_table)`.
|
||||
- **1d** — Configurar SMTP (Postmark / Mailgun / SES — escolha de provedor é decisão de operação, não data plane). `GOTRUE_MAILER_AUTOCONFIRM=false`. Tela de "confirme seu e-mail" no signup.
|
||||
|
||||
**Categoria:** Sec + Ops. **Bloqueia:** abertura pública controlada.
|
||||
|
||||
---
|
||||
|
||||
### 2.2.12 [MED] `.env.backup.*` versionado com credenciais
|
||||
|
||||
**Localização:** `infra/disclosure-stack/.env.backup.1778796747` contém `CLAUDE_CODE_OAUTH_TOKEN`, `OPENROUTER_API_KEY`, `POSTGRES_PASSWORD`, `JWT_SECRET`.
|
||||
|
||||
**Esforço — 0.5d + rotação:**
|
||||
|
||||
1. Adicionar `.env.backup.*` ao `.gitignore`.
|
||||
2. Remover do histórico via `git filter-repo` ou `bfg-repo-cleaner` (operação destrutiva — requer alinhamento com user).
|
||||
3. **Rotacionar TODOS os segredos vazados** (OAuth token, OpenRouter key, JWT secret, postgres password). Sem essa rotação, o passo 1+2 é teatro.
|
||||
4. Documentar a rotação no `infra/DEPLOY-CHECKLIST.md`.
|
||||
|
||||
**Categoria:** Sec. **Bloqueia:** higiene básica de produção.
|
||||
|
||||
> **NOTA:** Este débito tem veto implícito de segurança. Não é só "ruim" — é tóxico. Deve ser fixado **antes** de abrir o produto para sign-ups externos.
|
||||
|
||||
---
|
||||
|
||||
### 2.2.13 [MED] `@assistant-ui/react` fantasma
|
||||
|
||||
**Localização:** `web/package.json:13` declara `"@assistant-ui/react": "^0.14.0"`. `grep -r "@assistant-ui" web/{app,components,lib}` retorna **0 hits** em código importado.
|
||||
|
||||
**Esforço — 1d:**
|
||||
|
||||
- Decisão técnica em ADR-001. Se a escolha for adotar, planeja-se o uso. Se não, remove do `package.json` e do `package-lock.json`.
|
||||
|
||||
**Categoria:** DevEx. **Bloqueia:** clareza de decisão de chat.
|
||||
|
||||
---
|
||||
|
||||
### 2.2.14 [MED] Schema sem `tenant_id` / `corpus_id`
|
||||
|
||||
**Localização:** todas as tabelas em `infra/supabase/migrations/0002_chunks_retrieval.sql` (`documents`, `chunks`, `entities`, `entity_mentions`) e em `0001_chat_schema.sql` (`chat_sessions`, `messages`).
|
||||
|
||||
**Estado:** monolítico para um único corpus (war.gov/ufo). A visão de longo prazo é multi-tenant / multi-corpus (gap declarado no brief: "Multi-tenant/multi-corpus").
|
||||
|
||||
**Esforço — 4d (schema) + 6d (UI + refactor):**
|
||||
|
||||
1. **2d** — Migration `0003_multitenant.sql`: `corpora(corpus_id TEXT PK, ...)`, `documents` ganha FK `corpus_id`. RLS policy passa de `USING (TRUE)` para `USING (corpus_id IN (current_user_corpora()))`.
|
||||
2. **2d** — Wiki paths: `wiki/<corpus_id>/documents/...` (refactor estrutura) **ou** rotear via env `CORPUS_ID` (single-corpus por deploy — mais simples; perde "single pane of glass").
|
||||
3. **6d (UI)** — Routes Next ganham `[corpusId]` segment. Chat sessions vinculam corpus. Switcher de corpus na navbar para usuários com mais de um.
|
||||
|
||||
Trade-offs em **ADR-005**.
|
||||
|
||||
**Categoria:** Arch. **Bloqueia:** visão de longo prazo.
|
||||
|
||||
---
|
||||
|
||||
### 2.2.15 [LOW] `wiki/log.md` 459 KB monolítico
|
||||
|
||||
`wiki/log.md` é append-only desde o início e está em 459 KB. Eventualmente passa do tamanho razoável de um arquivo MD navegado por humano.
|
||||
|
||||
**Esforço — 1d:**
|
||||
|
||||
- Rotacionar por mês: `wiki/log.md` (current) + `wiki/log/<YYYY-MM>.md` (rotated). Script `scripts/33-compact-progress-log.py` já existe — adaptar para rotação.
|
||||
|
||||
---
|
||||
|
||||
### 2.2.16 [LOW] Duas libs de grafo
|
||||
|
||||
**Localização:** `web/package.json` declara `sigma`, `graphology`, `@react-sigma/*` **e** `react-force-graph-2d`.
|
||||
|
||||
**Esforço — 1d:** decisão + remoção da não-escolhida (ADR-006).
|
||||
|
||||
---
|
||||
|
||||
### 2.2.17 [LOW] `outputFileTracingIncludes` com paths relativos absolutos
|
||||
|
||||
`web/next.config.ts` usa `"../wiki/**"`, `"../processing/**"` — assume o layout do monorepo. Em Docker, `WORKDIR /app/web` e o COPY ajusta, mas o desenvolvedor que clona não-monorepo quebra.
|
||||
|
||||
**Esforço — 1d:** envelopar com `process.env.UFO_ROOT_TRACE || "../"`.
|
||||
|
||||
---
|
||||
|
||||
### 2.2.18 [LOW] Sem CI/CD
|
||||
|
||||
Esforço — 2d:
|
||||
|
||||
1. **1d** — `.github/workflows/ci.yml`: lint + build + smoke.
|
||||
2. **1d** — Deploy: SSH para VPS, `docker-compose pull && up -d web` orquestrado por workflow `release.yml` em push de tag.
|
||||
|
||||
---
|
||||
|
||||
### 2.2.19 [LOW] Sem observabilidade
|
||||
|
||||
Esforço — 3d:
|
||||
|
||||
1. **1d** — Loki + Promtail recebem stdout dos serviços compose.
|
||||
2. **1d** — Grafana dashboard básico: latência hybrid_search, error rate, signup rate.
|
||||
3. **1d** — Sentry self-hosted (ou GlitchTip open-source) capturando exceptions.
|
||||
|
||||
---
|
||||
|
||||
### 2.2.20 [LOW] ADRs ausentes
|
||||
|
||||
Este deliverable inicia a prática. Continuação como hábito: cada decisão type-1 (stack, contrato público, fronteira de serviço) escreve um ADR. Pasta `docs/adr/`. Convenção `NNNN-titulo.md`.
|
||||
|
||||
---
|
||||
|
||||
## 2.3 Mapa débito × arquivos concretos
|
||||
|
||||
| Débito | Arquivo principal |
|
||||
|---|---|
|
||||
| Stubs Phase X | `scripts/03-dedup-entities.py:167-169` + `wiki/entities/**/*.md` |
|
||||
| Case Bureau vazio | `case/{evidence,hypotheses,witnesses,timelines,profiles,connect-the-dots}/` |
|
||||
| Timeline stubs | `web/app/api/timeline/route.ts` + `wiki/entities/events/EV-*.md` |
|
||||
| Chat AG-UI caseiro | `web/lib/chat/agui.ts` (65 LOC) |
|
||||
| Dashboards | `web/app/admin/stats/page.tsx` (único existente) |
|
||||
| Testes | repo todo |
|
||||
| Reranker cold | `infra/embed-service/app.py` + `web/lib/retrieval/embed.ts:16` |
|
||||
| Grafo 9.2 MB | `wiki/graph.json` + `web/components/sigma-graph-client.tsx` |
|
||||
| Entity mentions ILIKE | `scripts/31-populate-entity-mentions.py` |
|
||||
| Sprawl rebuild | `scripts/rebuild_doc*.py` (28+) |
|
||||
| SMTP / autoconfirm | `infra/disclosure-stack/docker-compose.yml:81` |
|
||||
| .env backup vazado | `infra/disclosure-stack/.env.backup.1778796747` |
|
||||
| assistant-ui fantasma | `web/package.json:13` |
|
||||
| Multi-tenant | todas as migrations |
|
||||
| log.md gigante | `wiki/log.md` |
|
||||
| Sigma vs force-graph | `web/package.json` |
|
||||
| Trace includes | `web/next.config.ts:6-13` |
|
||||
| CI/CD | (ausente) `.github/workflows/` |
|
||||
| Observabilidade | (ausente) |
|
||||
| ADRs | `docs/adr/` (a criar) |
|
||||
|
||||
---
|
||||
|
||||
_Esforços calibrados para 1 engenheiro sênior + 1 IA-pair (Claude Code OAuth ou Cursor) em ritmo zero-human, com a arquitetura alvo do entregável 04 como roadmap._
|
||||
|
|
@ -0,0 +1,224 @@
|
|||
---
|
||||
title: "Product Discovery Brief — The Disclosure Bureau"
|
||||
project: disclosure-bureau
|
||||
business: systems-atelier
|
||||
author: sa-principal (Principal Architect — DNA Cagan)
|
||||
baseline_commit: 19d0678
|
||||
generated_at: 2026-05-17
|
||||
schema_version: 0.1.0
|
||||
---
|
||||
|
||||
# 3. Product Discovery — por que, para quem, e com que valor
|
||||
|
||||
> **Cagan's first rule:** comece pelo problema e pelo usuário, não pela solução. Antes de escrever uma linha de roadmap, este documento responde: por que o Disclosure Bureau deve existir, quem o usa, o que tentam fazer com ele e onde os riscos de valor / viabilidade / utilizabilidade moram.
|
||||
|
||||
## 3.1 Frase-âncora
|
||||
|
||||
> **O Disclosure Bureau existe para transformar o ruído documental dos arquivos UAP/FOIA em uma investigação navegável, citada e auditável — usado por pesquisadores, jornalistas, ufólogos sérios e cidadãos curiosos —, e tem sucesso quando uma claim feita no chat vem com a página exata, o crop bbox da fonte, e a cadeia de custódia visível.**
|
||||
|
||||
Tudo decorre disso. Se uma feature não serve a esta frase, ela é decoração.
|
||||
|
||||
## 3.2 Por que isso precisa existir
|
||||
|
||||
O domínio UAP/UFO desclassificado tem três problemas estruturais:
|
||||
|
||||
1. **Volume vs leitura humana.** Milhares de páginas FOIA com OCR ruim, layouts inconsistentes, redactions opacas. O leitor médio nunca chega ao parágrafo relevante.
|
||||
2. **Ruído epistemológico.** Toda fonte secundária (blogs, vídeos, podcasts) cita seletivamente, sem mostrar o documento. O leitor não consegue distinguir fato verificável de inferência criativa.
|
||||
3. **Falta de cadeia de custódia.** Uma claim viral ("o memo Hottel diz X") raramente vem com a página, o crop, o stamp de classificação. A investigação é estruturalmente irreproduzível.
|
||||
|
||||
O Disclosure Bureau resolve os três simultaneamente:
|
||||
|
||||
| Problema | Solução desenhada |
|
||||
|---|---|
|
||||
| Volume | Vision LLM + chunking bbox-aware + retrieval híbrido BGE-M3 |
|
||||
| Ruído | Sistema de evidence/witness/hypothesis (Locard + Tetlock); confidence bands; verbatim quotes em source-lang |
|
||||
| Cadeia de custódia | Cada chunk carrega `source_png`, `bbox`, `chain_of_custody[]`, `redaction_code`. Citações no chat abrem crop inline. |
|
||||
|
||||
## 3.3 Quem usa — três personas reais
|
||||
|
||||
### Persona A — **Marina, jornalista investigativa** (35a, fluente PT+EN)
|
||||
|
||||
- Trabalha em série de reportagem longa sobre UAP. Já sabe que existem 116 PDFs no `war.gov/ufo`. Não vai ler tudo.
|
||||
- Quer: "me mostre todos os encontros do tipo 'Tic-Tac' entre 2004 e 2015 e me dê o documento exato pra cada um".
|
||||
- Mede sucesso: artigo publicado com 12 citações primárias verificáveis. Sem retração por má-leitura.
|
||||
- Hoje: faz `Ctrl+F` em PDFs, mantém planilha Excel, perde 4 horas por sessão.
|
||||
|
||||
### Persona B — **Pedro, pesquisador acadêmico** (Sociologia da ciência)
|
||||
|
||||
- Tese sobre "construção institucional do UAP discourse". Precisa de network de atores, cronologia, traçar mudanças de vocabulário.
|
||||
- Quer: "extraia para mim o grafo de organizações mencionadas em todos os documentos da década de 1950, agrupado por padrão de classificação (SECRET vs CONFIDENTIAL)".
|
||||
- Mede sucesso: figura publicável + dataset CSV para revisão por pares.
|
||||
- Hoje: trabalha à mão. Citações no LaTeX vão para `\cite[p.~37]{fbi-vault-1947}`. Sem ferramentas.
|
||||
|
||||
### Persona C — **Carla, cidadã curiosa séria** (público alvo da landing public)
|
||||
|
||||
- Não é especialista. Sabe que algo nesses arquivos importa. Quer entender o argumento sem precisar virar especialista.
|
||||
- Quer: chat onde pergunta "o que aconteceu em Roswell de verdade?" e recebe uma resposta com 3 fontes primárias, um crop da carta, e um "este é o ponto onde a evidência fica fraca — veja a hipótese alternativa".
|
||||
- Mede sucesso: confiança no que leu; capacidade de explicar para um amigo.
|
||||
- Hoje: YouTube, podcasts, posts. Não tem cadeia de custódia.
|
||||
|
||||
### Persona D (latente, fase 2) — **Operador de outro corpus**
|
||||
|
||||
- Pesquisador de outro tema (JFK files, Stargate Project, MK Ultra, OVNIs brasileiros) que quer trazer seus PDFs para o Bureau e ter o mesmo tratamento. **Esta é a expansão multi-tenant / multi-corpus da visão.**
|
||||
|
||||
## 3.4 Jobs-to-be-done
|
||||
|
||||
Em linguagem JTBD ("when … I want to … so that …"):
|
||||
|
||||
1. **Quando** vejo uma claim viral sobre UAP em outro meio, **quero** chegar à fonte primária com bbox crop **para que** eu possa decidir se a claim sustenta a citação.
|
||||
2. **Quando** estou estudando um período (ex.: 1947-1952), **quero** uma cronologia consolidada de eventos+sightings+operações **para que** eu veja padrões temporais sem montar planilha à mão.
|
||||
3. **Quando** investigo um ator (pessoa, agência), **quero** ver todos os documentos onde ele aparece, com quem é co-mencionado, e em qual classificação **para que** eu mapeie sua rede de operações.
|
||||
4. **Quando** leio uma hipótese sobre um caso, **quero** ver hipóteses concorrentes com priors Bayes calibrados **para que** eu evite âncora cognitiva.
|
||||
5. **Quando** uso o chat, **quero** que cada afirmação venha com `[[doc/p007#c0042]]` clicável → crop inline **para que** eu nunca aceite resposta sem verificar.
|
||||
6. **Quando** encontro um gap na evidência (página redacted, custody quebrada), **quero** o sistema explicitar `residual_uncertainty` **para que** eu saiba o que falta saber.
|
||||
|
||||
## 3.5 Riscos — taxonomia Cagan
|
||||
|
||||
Risco bom de produto é risco enfrentado cedo. Os quatro:
|
||||
|
||||
### 3.5.1 Risco de **valor** (eles vão usar?)
|
||||
|
||||
| Hipótese | Status | Como derisk |
|
||||
|---|---|---|
|
||||
| "Jornalistas vão preferir esta UX a Ctrl+F no PDF" | **Não validado** | 5 entrevistas + landing com waitlist; meta: ≥40% conversão pra signup |
|
||||
| "Cidadão curioso vai voltar uma segunda vez" | **Não validado** | Cookie de retorno + email opt-in; meta: ≥25% retention semana-1 |
|
||||
| "Pesquisador acadêmico vai citar o Bureau em paper" | **Aspiracional** | Página `/cite` com BibTeX por chunk; tracking de citações |
|
||||
|
||||
### 3.5.2 Risco de **viabilidade** (faz sentido para a casa?)
|
||||
|
||||
| Variável | Status atual |
|
||||
|---|---|
|
||||
| Custo de pipeline | ~$409 acumulado para 116 docs; ~$3.50/doc; escalável |
|
||||
| Custo de chat run-time | OpenRouter free models default; OAuth do Max 20x absorvido pelo plano |
|
||||
| Custo de embed-service | $0 (self-hosted CPU); VPS já está rodando |
|
||||
| Custo de armazenamento | 634 MB raw chunks + 9 MB graph.json — trivial |
|
||||
| Custo marginal de novo corpus | ~$3-10/doc dependendo do tamanho + algumas horas de operador |
|
||||
|
||||
**Conclusão de viabilidade:** estrutura de custo está calibrada. Risco maior é tempo de engenharia (escopo grande, sem CI, sem testes). Não é dinheiro.
|
||||
|
||||
### 3.5.3 Risco de **utilizabilidade** (eles conseguem usar?)
|
||||
|
||||
| Sintoma observado | Risco |
|
||||
|---|---|
|
||||
| Stubs "Phase X" em 22k arquivos | **Alto** — destrói confiança imediatamente |
|
||||
| Chat sem AG-UI real | **Médio** — falta de feedback streaming faz parecer "travado" |
|
||||
| Mobile responsivo precário | **Médio** — gap declarado; jornalista lê no celular |
|
||||
| Grafo 9.2 MB | **Médio** — trava em corpus grande, lá vem o "site quebrado" |
|
||||
| Sem onboarding | **Médio** — usuário não sabe pedir "list_anomalies" via chat |
|
||||
| Sem export PDF/CSV/PNG | **Baixo-médio** — pesquisador precisa exportar pra paper |
|
||||
| Geocoding ausente | **Baixo** — desbloqueia mapa, mas mapa é nice-to-have |
|
||||
|
||||
### 3.5.4 Risco de **utilidade investigativa** (specifically Cagan-adjacent)
|
||||
|
||||
A categoria que mais distingue este produto de "outro chat sobre PDFs":
|
||||
|
||||
| Hipótese | Status |
|
||||
|---|---|
|
||||
| "Tetlock-style hypothesis tournament melhora confiança vs LLM-só" | **Não testado** — case bureau ainda vazio |
|
||||
| "Locard-style chain of custody + bbox crops reduz fabricação" | **Em parte** — citações funcionam; chain_of_custody[] não preenchido |
|
||||
| "Red-team review é diferencial" | **Não testado** — chief-detective não rodou |
|
||||
|
||||
**Conclusão:** o diferencial do Disclosure Bureau não está no chat. Está no **case bureau**. Se a fase 2 do roadmap (Case Bureau) não rodar, o produto é "Wiki UFO bonita com chat". Comoditizável.
|
||||
|
||||
## 3.6 Posicionamento — "super centro de investigação"
|
||||
|
||||
O brief usa essa frase. Vou traduzi-la em coordenadas de competição:
|
||||
|
||||
### 3.6.1 O que **não** competimos
|
||||
|
||||
- **Não somos** Perplexity / ChatGPT — não fazemos web search ao vivo. Nosso corpus é fixo, curado, auditado.
|
||||
- **Não somos** Obsidian / Roam — não somos editor pessoal. Somos investigação pública.
|
||||
- **Não somos** Elicit / Consensus — não somos paper-summarizer. Lidamos com fonte primária bruta, não literatura científica.
|
||||
- **Não somos** Internet Archive — não somos depositário neutro. Temos curadoria, hipóteses e veredictos.
|
||||
|
||||
### 3.6.2 O que **somos** (em uma linha)
|
||||
|
||||
> **Karpathy LLM-Wiki + Investigation Bureau Locard/Tetlock para arquivos declassificados, com chat AG-UI nativamente citado e auditável.**
|
||||
|
||||
### 3.6.3 Vetor de diferenciação
|
||||
|
||||
| Eixo | Disclosure Bureau | Concorrente médio (Perplexity, Sherlock-no-PDF) |
|
||||
|---|---|---|
|
||||
| Citações com bbox visual | **Sim** (`/api/crop`) | Não |
|
||||
| Bilíngue EN+PT desde ingest | Sim | Tradução tardia, perde precisão |
|
||||
| Hypothesis tournament Tetlock | Sim (planejado) | Não |
|
||||
| Chain of custody Locard | Sim (planejado) | Não |
|
||||
| Red-team review chief-detective | Sim (planejado) | Não |
|
||||
| Source-of-truth markdown puro | Sim | Banco proprietário |
|
||||
| Self-hosted, sem SaaS no data plane | Sim | Vendor lock-in |
|
||||
| Open-source amigável (planejado) | Sim | Não |
|
||||
|
||||
Os "Sim (planejado)" são o roadmap. **Se planejado vira "Sim entregue", o produto fica defensável.**
|
||||
|
||||
## 3.7 Mudanças de visão refinadas a partir do brief
|
||||
|
||||
O brief lista "Visão longo prazo: multi-tenant/multi-corpus · API pública REST+GraphQL · MCP server · Visualização temporal-geo". Reflexão sobre cada uma:
|
||||
|
||||
### 3.7.1 Multi-tenant / multi-corpus — **YES, fase 4**
|
||||
|
||||
- Permite Persona D (operadores de outros corpora).
|
||||
- Trade-off: explode complexidade de RLS, billing, isolamento. Ver **ADR-005**.
|
||||
- Estratégia recomendada: começar com **multi-corpus single-tenant** (vários corpora dentro de um Bureau, todos curados pela mesma equipe). Multi-tenant verdadeiro vem depois.
|
||||
|
||||
### 3.7.2 API pública REST + GraphQL — **YES, fase 5**
|
||||
|
||||
- Habilita pesquisadores acadêmicos a integrar em fluxos próprios.
|
||||
- REST puro via PostgREST já existe (parte do Supabase stack). Pode ser exposto seletivamente.
|
||||
- GraphQL é luxo. Postpor. REST cobre 90%.
|
||||
|
||||
### 3.7.3 MCP server — **YES, fase 5**
|
||||
|
||||
- Tornar o Bureau acessível como ferramenta para outros agentes (Claude Code, Cursor, Codex). Forte alavanca de adoção.
|
||||
- Custo: ~3d para expor um MCP server compatível com o protocolo, expondo os mesmos tools do chat (`hybrid_search`, `read_chunk`, `entity_neighbors`, …).
|
||||
- ROI alto. **Prioridade dentro da fase 5.**
|
||||
|
||||
### 3.7.4 Visualização temporal-geo — **YES, fase 3**
|
||||
|
||||
- Adianta o "super centro de investigação".
|
||||
- Dependências: geocoding pipeline + timeline real (não-stub).
|
||||
- Stack open-source: MapLibre GL JS + Nominatim self-host + tiles do MapTiler ou Stamen.
|
||||
|
||||
## 3.8 Critérios de sucesso mensuráveis (1 ano)
|
||||
|
||||
| Métrica | Linha de base atual | Meta 12 meses |
|
||||
|---|---|---|
|
||||
| Docs catalogados | 116 | 500+ (com onboard de 3 corpora além de war.gov) |
|
||||
| Sessões de chat / mês | n.d. (presumir <200) | 5.000+ |
|
||||
| Citações com crop inline | 100% (já) | manter 100% |
|
||||
| Eventos com narrativa real (não stub) | 1/7.091 | ≥2.000 (28%+) curados |
|
||||
| Casos com hypothesis tournament rodado | 0 | ≥15 |
|
||||
| Hypothesis tournaments com red-team pass | 0 | ≥10 |
|
||||
| Latência hybrid_search p50 | 1.3s | <800ms |
|
||||
| Latência rerank p95 (warm) | 60s timeout | <3s |
|
||||
| Mobile usable score Lighthouse | n.d. (suspeitamente baixo) | ≥85 |
|
||||
| Retention semana-1 | n.d. | ≥25% |
|
||||
|
||||
## 3.9 Aposta de design — bilíngue PT-BR como ativo
|
||||
|
||||
Decisão estratégica não-óbvia: investir em **PT-BR de primeira linha** (não tradução tardia) é vantagem competitiva sustentável, não custo.
|
||||
|
||||
**Por que:**
|
||||
|
||||
- Mercado lusófono de pesquisa UAP é underserved. Não há concorrente nativo PT-BR.
|
||||
- O usuário-fundador é brasileiro. Insights de produto vêm em PT-BR primeiro.
|
||||
- Documentos brasileiros futuros (Operação Prato 1977, Caso Varginha 1996, Marinha-Soure 1959) são corpus natural de expansão de fase 4.
|
||||
|
||||
**Implicação técnica:** já está feito (`pt_unaccent` tsvector, `content_pt` em todo chunk, vision pass dual). Manter.
|
||||
|
||||
## 3.10 Anti-personas — quem não atendemos
|
||||
|
||||
- **Crentes dogmáticos** (de qualquer lado): o produto cita evidência e mostra hipóteses concorrentes; não apoia narrativa fechada. Quem busca confirmação, sai irritado. Aceitável.
|
||||
- **Conspiracionista de TikTok**: o produto é texto+gráfico, não vídeo curto. Sem TikTok-mode. Aceitável.
|
||||
- **Pesquisador de domínio adjacente** (cripto-zoologia generalista, paranormal solto): cobrimos `cryptid_anomaly` mas o foco é UAP/UFO. Cryptid é subproduto. Não desviamos roadmap por ele.
|
||||
|
||||
## 3.11 Sinais para validar/ajustar a visão
|
||||
|
||||
Em 90 dias após o roadmap começar, três sinais decidem se a visão de "super centro de investigação" cola:
|
||||
|
||||
1. **Engajamento qualitativo no Case Bureau**: ≥3 cases com ≥30 visitas únicas e tempo médio >3min. Se cair abaixo, o "tournament Tetlock" é teatro, não valor.
|
||||
2. **Citações externas**: alguém citar `disclosure.top/d/<doc>#c0042` em um artigo público ou tweet. Se não acontecer em 6 meses, o produto não atingiu Persona A/B.
|
||||
3. **Onboard de segundo corpus**: alguém pedir "quero processar meu corpus aqui". Se em 12 meses não acontecer, a aposta multi-corpus é prematura — refoca em war.gov/ufo profundo.
|
||||
|
||||
---
|
||||
|
||||
_Esta discovery foi conduzida lendo CLAUDE.md (contrato), o brief, o estado atual do repo, MEMORY.md (vinte feedbacks de dev acumulados) e o user-fundador (brasileiro, plano Claude Max 20x). É premissa para o entregável 04._
|
||||
|
|
@ -0,0 +1,450 @@
|
|||
---
|
||||
title: "System Architecture Doc — Target State + Phased Roadmap"
|
||||
project: disclosure-bureau
|
||||
business: systems-atelier
|
||||
author: sa-principal (Principal Architect — DNA Fowler + Larson)
|
||||
baseline_commit: 19d0678
|
||||
generated_at: 2026-05-17
|
||||
schema_version: 0.1.0
|
||||
---
|
||||
|
||||
# 4. Arquitetura alvo + roadmap em fases priorizadas
|
||||
|
||||
> Fowler: arquitetura evolutiva, patterns como vocabulário. Larson: alavancas de longo prazo, decisões escritas. Cagan: comece pelo problema. Este documento descreve **a arquitetura alvo** (não a atual — essa está no entregável 01) e propõe um **roadmap em 7 fases** que entrega valor incrementalmente, com critérios de aceite verificáveis.
|
||||
|
||||
## 4.1 Princípios de arquitetura
|
||||
|
||||
São restrições de projeto, não aspirações:
|
||||
|
||||
1. **Markdown é a fonte da verdade.** Postgres é cache estruturado; pode ser reconstruído de zero a partir de `wiki/` + `raw/`.
|
||||
2. **Open-source no data plane.** Postgres, BGE-M3, Sigma/Cytoscape, Nominatim, MapLibre. Sem SaaS proprietário onde dado bruto passa.
|
||||
3. **Self-hosted via Docker Compose em VPS.** Sem Kubernetes prematuro. Decisão se reabre se chegarmos a multi-tenant pesado (ADR-005).
|
||||
4. **LLM via Claude Code OAuth ou OpenRouter.** Proibido `ANTHROPIC_API_KEY`. Não-negociável.
|
||||
5. **Bilíngue EN+PT-BR desde o ingest.** Não tradução tardia. Postgres tem `pt_unaccent`/`en_unaccent`. Frontend tem locale toggle persistente.
|
||||
6. **Cada claim é citada.** Chunk com `bbox` JSONB resolve para crop visual via `/api/crop`. Sem citação = não vai pro chat.
|
||||
7. **Procedência Locard sempre.** `chain_of_custody[]` em evidence, `mentioned_in[]` materializado em entities, `source_page` + `bbox` em chunks. Lint bloqueante.
|
||||
8. **Confidence bands Tetlock.** Toda claim de sumário tem `confidence_band` (high/medium/low/speculation). Linguagem permitida calibrada.
|
||||
9. **Idempotência.** Reprocessar o mesmo PDF mantém IDs, atualiza `last_ingest`, preserva `created_at`.
|
||||
10. **Segurança como restrição de projeto.** Veto do sa-security-engineer prevalece. Sem segredo no git. SMTP real antes de signup público.
|
||||
|
||||
## 4.2 Arquitetura alvo (diagrama)
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Edge: Traefik + Let's Encrypt + WAF leve (modsecurity-crs) │
|
||||
└────┬──────────────────────────────────────────────────────────────────┬────┘
|
||||
│ TLS │
|
||||
▼ ▼
|
||||
┌──────────────────────────┐ ┌──────────────────────────┐
|
||||
│ Next.js 15 web (web) │◄─SSE───────────────────────────┤ AG-UI chat client │
|
||||
│ ─ App Router │ │ (browser, mobile-ready) │
|
||||
│ ─ Pages SSR de wiki │ └──────────────────────────┘
|
||||
│ ─ API routes │
|
||||
│ ─ Middleware Supabase │
|
||||
└──┬───────────────────────┘
|
||||
│
|
||||
├──► Postgres 15 + pgvector + pg_trgm + unaccent
|
||||
│ schemas: public (corpora, documents, chunks, entities, mentions, cases,
|
||||
│ hypotheses, evidence, witnesses)
|
||||
│ auth (gotrue)
|
||||
│ storage (storage-api)
|
||||
│ tenants (opcional fase 4)
|
||||
│
|
||||
├──► embed-service (FastAPI) — BGE-M3 + BGE-Reranker-v2-M3 — warm-pool, queue
|
||||
│
|
||||
├──► chat-runtime
|
||||
│ ─ provider OpenRouter (Pattern C streaming, tool loop)
|
||||
│ ─ provider Claude Code OAuth (subprocess streaming, tool loop) ← Fase 5
|
||||
│ ─ contrato AG-UI v1 (events: text_delta, tool_call, tool_result, state,
|
||||
│ artifact, navigate, done, error)
|
||||
│
|
||||
├──► case-runtime (cron + workers Python)
|
||||
│ ─ hypothesis-tournament
|
||||
│ ─ evidence-officer (chain-of-custody)
|
||||
│ ─ red-team review (chief-detective)
|
||||
│ ─ output: case/**/*.md → indexed in DB
|
||||
│
|
||||
├──► geocoding-service (Nominatim self-host)
|
||||
│
|
||||
└──► observability stack:
|
||||
Loki + Promtail + Grafana + Sentry (self-host) + Uptime Kuma
|
||||
|
||||
Auth: Supabase GoTrue + PostgREST + Realtime + Studio (Kong gateway)
|
||||
Storage: Supabase storage-api + Imgproxy
|
||||
Backups: pg_dump diário + restic para S3-compatible (Backblaze B2 / Wasabi)
|
||||
```
|
||||
|
||||
## 4.3 Fronteiras de serviço
|
||||
|
||||
| Serviço | Responsabilidade | Linguagem | Interface |
|
||||
|---|---|---|---|
|
||||
| `web` | UI + SSR + chat orquestração + crop on-demand | TS/Next 15 | HTTPS + SSE |
|
||||
| `db` | Source of truth runtime (cache do MD) | SQL | TCP 5432 internal |
|
||||
| `embed` | Embedding + rerank | Python/FastAPI | HTTP 8000 internal |
|
||||
| `case-runtime` | Hypothesis tournament + red team | Python | Cron + Postgres queue |
|
||||
| `geocoding` | Nominatim self-host | C++ image | HTTP internal |
|
||||
| `ingest-pipeline` | PDF → chunks → DB index | Python | CLI, manual ou cron |
|
||||
| `chat-runtime` | Tool-loop server-side (subset de `web/lib/chat/`) | TS, dentro do `web` | API route + SSE |
|
||||
| `auth/rest/realtime/storage/imgproxy/studio/kong/meilisearch` | Supabase | mixed | internal |
|
||||
| `observability` | Logs, metrics, errors | Go/Erlang | self-host |
|
||||
| `traefik` | Edge TLS + roteamento | Go | edge |
|
||||
|
||||
**Princípio:** o `web` é o orquestrador, não o trabalhador pesado. Ingest, rerank, case tournament rodam em processos próprios.
|
||||
|
||||
## 4.4 Modelo de dados — evolução proposta
|
||||
|
||||
### 4.4.1 Tabelas novas (fases 2 e 3)
|
||||
|
||||
```sql
|
||||
-- Casos investigativos (Tetlock + Locard)
|
||||
CREATE TABLE public.cases (
|
||||
case_id TEXT PRIMARY KEY, -- e.g. 'CASE-NIMITZ-TIC-TAC'
|
||||
canonical_name TEXT NOT NULL,
|
||||
scope_summary TEXT,
|
||||
status TEXT NOT NULL DEFAULT 'open' CHECK (status IN ('open','closed','dormant')),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
last_review TIMESTAMPTZ
|
||||
);
|
||||
|
||||
CREATE TABLE public.hypotheses (
|
||||
hypothesis_id TEXT PRIMARY KEY, -- 'H-0042'
|
||||
case_id TEXT REFERENCES public.cases(case_id) ON DELETE CASCADE,
|
||||
statement_en TEXT NOT NULL,
|
||||
statement_pt TEXT NOT NULL,
|
||||
prior REAL NOT NULL CHECK (prior BETWEEN 0 AND 1),
|
||||
likelihood REAL CHECK (likelihood BETWEEN 0 AND 1),
|
||||
posterior REAL CHECK (posterior BETWEEN 0 AND 1),
|
||||
evidence_for TEXT[], -- evidence_ids
|
||||
evidence_against TEXT[],
|
||||
red_team_pass BOOLEAN DEFAULT FALSE,
|
||||
confidence_band TEXT NOT NULL CHECK (confidence_band IN ('high','medium','low','speculation')),
|
||||
authored_by TEXT, -- 'holmes' | 'poirot' | 'dupin' | 'locard'
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE public.evidence (
|
||||
evidence_id TEXT PRIMARY KEY, -- 'E-0042'
|
||||
case_id TEXT REFERENCES public.cases(case_id) ON DELETE CASCADE,
|
||||
doc_id TEXT NOT NULL,
|
||||
page INT,
|
||||
bbox JSONB,
|
||||
verbatim_excerpt TEXT NOT NULL,
|
||||
evidence_grade CHAR(1) CHECK (evidence_grade IN ('A','B','C')),
|
||||
chain_of_custody JSONB NOT NULL, -- [{step, actor, timestamp}, ...]
|
||||
custody_gaps TEXT[],
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- ... witnesses, gaps, relations, case_reports analogamente
|
||||
```
|
||||
|
||||
### 4.4.2 Mudança crítica em `entities`
|
||||
|
||||
Adicionar:
|
||||
|
||||
```sql
|
||||
ALTER TABLE public.entities
|
||||
ADD COLUMN narrative_summary_en TEXT,
|
||||
ADD COLUMN narrative_summary_pt TEXT,
|
||||
ADD COLUMN summary_confidence TEXT CHECK (summary_confidence IN ('high','medium','low','none')),
|
||||
ADD COLUMN summary_status TEXT NOT NULL DEFAULT 'none'
|
||||
CHECK (summary_status IN ('none','synthesized','curated','red_teamed')),
|
||||
ADD COLUMN lat DOUBLE PRECISION, -- fase 3
|
||||
ADD COLUMN lon DOUBLE PRECISION,
|
||||
ADD COLUMN geo_confidence TEXT CHECK (geo_confidence IN ('exact','city','region','unknown'));
|
||||
```
|
||||
|
||||
A coluna `summary_status` substitui o stub textual. Fora do filtro de listings públicos, qualquer linha com `summary_status='none'`.
|
||||
|
||||
### 4.4.3 Multi-corpus (fase 4)
|
||||
|
||||
```sql
|
||||
CREATE TABLE public.corpora (
|
||||
corpus_id TEXT PRIMARY KEY, -- 'war-gov-ufo', 'jfk-files', 'arquivo-nacional-br'
|
||||
display_name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
visibility TEXT NOT NULL CHECK (visibility IN ('public','unlisted','private')),
|
||||
owner_id UUID REFERENCES auth.users(id),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
ALTER TABLE public.documents ADD COLUMN corpus_id TEXT REFERENCES public.corpora(corpus_id);
|
||||
-- RLS pass de USING (TRUE) para USING (
|
||||
-- corpus_id IN (SELECT corpus_id FROM corpora WHERE visibility='public')
|
||||
-- OR auth.uid() IN (...)
|
||||
-- )
|
||||
```
|
||||
|
||||
ADR-005 documenta a decisão.
|
||||
|
||||
## 4.5 Fronteira do chat — contrato AG-UI
|
||||
|
||||
ADR-001 cobre a decisão. Resumo do **contrato alvo**:
|
||||
|
||||
- Servidor expõe `POST /api/chat/run` com body `{session_id, message, ctx}`.
|
||||
- Response = SSE com eventos AG-UI v1: `state.start` → `text.delta` (n×) → `tool.call` → `tool.result` → `artifact.created` (citações, crops, navegação) → `state.end`.
|
||||
- Cliente assina via `EventSource` ou `fetch+reader.read()`. Reconnect com `Last-Event-ID`.
|
||||
- Persistência incremental: cada `text.delta` flush num buffer; ao `state.end`, materializa `messages` row.
|
||||
- Cancelamento: `POST /api/chat/cancel` com `session_id` flagga a sessão; loop checa a cada turno.
|
||||
- **Artifact tipado**: `citation` (chunk_id+bbox), `crop_image`, `entity_card`, `case_card`, `evidence_card`, `hypothesis_card`, `navigation_offer`. Cada um tem React component dedicado.
|
||||
|
||||
## 4.6 Modelo de processamento — pipeline reorganizada
|
||||
|
||||
A pipeline atual (`scripts/00–34`) será reorganizada em **estágios canônicos**:
|
||||
|
||||
```
|
||||
Stage 1 — INGEST (scripts/ingest/)
|
||||
01_extract.sh PDF → PNG@72 + OCR
|
||||
02_vision.py Haiku 4.5 page-level vision pass (EN+PT)
|
||||
03_chunk.py Sonnet 4.6 chunking via subagent (bbox-aware)
|
||||
04_synthesize.py master doc.md assembly per doc
|
||||
|
||||
Stage 2 — INDEX (scripts/index/)
|
||||
10_embed.py BGE-M3 batch embed → chunks.embedding
|
||||
11_mentions.py tsvector-based entity match (substitui ILIKE)
|
||||
12_graph_export.py graph.json (paginado)
|
||||
|
||||
Stage 3 — SYNTHESIZE (scripts/synthesize/)
|
||||
20_entity_summary.py iterate entities with N+ mentions, write narrative_summary
|
||||
21_doc_pitch.py short pitch per doc for listings (já existe — 34-generate-doc-pitches.py)
|
||||
|
||||
Stage 4 — CASE (scripts/case/)
|
||||
30_seed.py create case skeletons from canonical events
|
||||
31_hypothesis.py Tetlock tournament loop
|
||||
32_evidence.py Locard chain-of-custody check
|
||||
33_red_team.py chief-detective review
|
||||
|
||||
Stage 5 — MAINTAIN (scripts/maintain/)
|
||||
40_lint.py full lint (substitui 04-lint.py)
|
||||
41_dedup.py entity dedup (substitui 03 → sem stubs textuais)
|
||||
42_geocode.py Nominatim lookup para entities.lat/lon
|
||||
43_log_rotate.py rotaciona wiki/log.md por mês
|
||||
```
|
||||
|
||||
Cada stage tem `make stage1` / `make stage2` / ... e cada script tem testes pytest.
|
||||
|
||||
## 4.7 Roadmap em 7 fases priorizadas
|
||||
|
||||
Fases são paralelizáveis dentro do limite de 1 engenheiro sênior + 1 IA-pair. Esforços em homem-dias (`d`).
|
||||
|
||||
### **Fase 0 — Limpeza de stubs "Phase X"** (3-4d)
|
||||
|
||||
> Esta fase existe porque o usuário está furioso. Não há valor em construir a fase 2 se a fase 0 não roda.
|
||||
|
||||
**Objetivo:** zerar a presença textual de "Phase X" / "Will be enriched" no UI.
|
||||
|
||||
**Escopo:**
|
||||
|
||||
1. Migrar `scripts/03-dedup-entities.py` para nunca emitir stub textual. `narrative_summary` fica `NULL`, `summary_status='none'`.
|
||||
2. Script de migração `scripts/maintain/41_dedup.py` reescreve os 22.096 arquivos existentes: remove o texto stub, seta `summary_status: none`, mantém YAML válido.
|
||||
3. UI: páginas de entidade exibem badge "sem síntese ainda — `[Sintetizar]`" em vez do texto stub. `/timeline` exclui eventos com `summary_status='none'` do default (param `?include_unsynthesized=true` força).
|
||||
4. `wiki/log.md` registra a operação.
|
||||
|
||||
**Critério de aceite:**
|
||||
|
||||
- `grep -r "Will be enriched in Phase" wiki/` retorna **0**.
|
||||
- `grep -r "_Stub" wiki/entities/` retorna **0**.
|
||||
- Página `/timeline` carrega ≥1 evento por default (estado mínimo) — após fase 2 essa lista cresce.
|
||||
- Página `/e/event/EV-1492-…` mostra layout limpo, sem "Phase 7".
|
||||
|
||||
**Esforço:** 3d (1 sênior).
|
||||
|
||||
### **Fase 1 — Cleanup técnico inegociável** (5-6d)
|
||||
|
||||
> Higiene mínima antes de construir features. Larson: alavancas que se pagam todo dia.
|
||||
|
||||
**Objetivo:** secret hygiene, CI, observabilidade mínima.
|
||||
|
||||
**Escopo:**
|
||||
|
||||
1. **Rotação de segredos vazados** + remoção do `.env.backup.*` do git-history. Documentação no DEPLOY-CHECKLIST.
|
||||
2. **CI/CD GitHub Actions**: `lint + tsc --noEmit + build + smoke /api/admin/stats`.
|
||||
3. **Testes piso mínimo**: 30 testes Vitest cobrindo as 19 API routes (fixture com 3 docs). 10 testes pytest cobrindo scripts críticos.
|
||||
4. **Observabilidade**: Loki + Promtail no compose; dashboard Grafana básico (latência hybrid_search, error rate); GlitchTip (Sentry open-source self-host) para exceptions.
|
||||
5. **Consolidar scripts ad-hoc**: 28 `rebuild_doc<NN>*.py` → mover para `scripts/_archive/`. README dos canônicos.
|
||||
6. **GoTrue SMTP** real configurado (provedor à escolha do user; data plane segue intocado). `MAILER_AUTOCONFIRM=false`.
|
||||
|
||||
**Critério de aceite:**
|
||||
|
||||
- `git log --all -- .env.backup.1778796747` retorna 0 commits (após filter-repo).
|
||||
- CI passa em PR. Falha bloqueia merge.
|
||||
- Grafana mostra request rate e error rate em tempo real.
|
||||
- Signup novo envia e-mail real.
|
||||
- `scripts/` sem `rebuild_doc<NN>*.py` no root.
|
||||
|
||||
**Esforço:** 5d.
|
||||
|
||||
### **Fase 2 — Case Bureau MVP (Locard + Tetlock real)** (10-12d)
|
||||
|
||||
> O diferencial. Sem isso o produto é "wiki bonita com chat".
|
||||
|
||||
**Objetivo:** primeiros 5 casos canônicos rodando com hipóteses concorrentes, evidence chain-of-custody, red-team pass.
|
||||
|
||||
**Escopo:**
|
||||
|
||||
1. **Schema**: migration `0003_case_bureau.sql` com `cases`, `hypotheses`, `evidence`, `witnesses`, `gaps`, `relations`, `case_reports`, `residual_uncertainty`.
|
||||
2. **Seed**: `scripts/case/30_seed.py` cria 5 casos âncora — Roswell 1947, Nimitz Tic-Tac 2004, Phoenix Lights 1997, FBI Hottel memo 1950, Apollo Skylab radio anomaly 1973.
|
||||
3. **Hypothesis tournament**: `scripts/case/31_hypothesis.py` lê evidences, gera 3+ hipóteses via Claude Code OAuth (Sonnet), calcula posterior simples (prior × likelihood normalizado), escreve `H-NNNN.md` + DB row.
|
||||
4. **Chain of custody**: `scripts/case/32_evidence.py` valida `chain_of_custody[]` ≥ N steps por grade. Grade A precisa ≥3, B ≥2, C ≥1.
|
||||
5. **Red-team review**: `scripts/case/33_red_team.py` chama Sonnet em modo adversarial: "encontre furos nas premissas das hipóteses dominantes". Output vai para `residual_uncertainty.md`.
|
||||
6. **UI**: `/case`, `/case/[id]`, `/case/[id]/hypotheses`, `/case/[id]/evidence`. Componentes `CaseTimelineLane`, `HypothesisTournamentCard`, `EvidenceChainCustody`.
|
||||
7. **Chat tools novos**: `read_case`, `read_hypothesis`, `read_evidence`. Chat passa a citar `[[hypothesis/H-0042]]` resolvido inline.
|
||||
|
||||
**Critério de aceite:**
|
||||
|
||||
- 5 cases em `case/case-report.md` com quality rubrics threshold 0.85 satisfeitos (chain_of_custody_completeness, confidence_calibration_match, hypothesis_tournament_discipline ≥3, residual_uncertainty_presence, audit_trail_per_claim, red_team_pass).
|
||||
- Página `/case/case-nimitz-tic-tac` renderiza timeline + 3 hipóteses + 5 evidences com bbox crop inline + red-team box.
|
||||
- Chat responde "como foi o Nimitz Tic-Tac?" com citações para hypothesis IDs + evidence IDs.
|
||||
|
||||
**Esforço:** 12d.
|
||||
|
||||
### **Fase 3 — Timeline real + Dashboards investigativos + Mapa** (10-12d)
|
||||
|
||||
> Os dashboards faltantes do brief. Aqui o produto começa a parecer "super centro".
|
||||
|
||||
**Objetivo:** timeline com narrativas reais; clusters; mapa; padrões.
|
||||
|
||||
**Escopo:**
|
||||
|
||||
1. **Pipeline de entity summary**: `scripts/synthesize/20_entity_summary.py` itera entidades com `total_mentions ≥ 5`, recupera chunks via `entity_mentions`, escreve `narrative_summary` real. Custo estimado: ~$80-150 corpus inteiro.
|
||||
2. **Geocoding**: Nominatim self-host no docker-compose. `scripts/maintain/42_geocode.py` popula `entities.lat/lon` para todas as `locations`.
|
||||
3. **Curadoria timeline**: 50-100 eventos canônicos com narrativa rica escrita pelo case-writer.
|
||||
4. **Mapa**: rota `/dashboard/map` com MapLibre GL + tiles open-source. Markers de sightings por densidade. Clica → entity card.
|
||||
5. **Clusters**: `/dashboard/clusters` agrupa eventos por proximidade temporal+geográfica (DBSCAN sobre lat/lon/date_start). Lista clusters como cards.
|
||||
6. **Atores**: `/dashboard/actors` lista top-50 entities (pessoas + organizações) por menções, sparkline temporal de aparições por década.
|
||||
7. **Padrões**: `/dashboard/patterns` mostra UFO anomaly por ano, classification heatmap, redaction ratio por collection.
|
||||
|
||||
**Critério de aceite:**
|
||||
|
||||
- `/timeline` mostra ≥2.000 eventos com narrative_summary real, agrupados por década, com filtro funcionando.
|
||||
- `/dashboard/map` carrega <2s, marca >500 locations com lat/lon.
|
||||
- `/dashboard/clusters` mostra ≥20 clusters identificados, cada um com mínimo 3 eventos.
|
||||
- `/dashboard/actors` lista top-50 com sparkline.
|
||||
- `/dashboard/patterns` tem 3 gráficos funcionais.
|
||||
|
||||
**Esforço:** 12d.
|
||||
|
||||
### **Fase 4 — AG-UI nativo + Mobile responsivo + Performance** (8-10d)
|
||||
|
||||
> A experiência conversacional vira diferencial.
|
||||
|
||||
**Objetivo:** AG-UI v1 real no chat; artefatos tipados; mobile usable; reranker rápido.
|
||||
|
||||
**Escopo:**
|
||||
|
||||
1. **AG-UI v1**: substituir `web/lib/chat/agui.ts` por implementação AG-UI canônica. Eventos `state.start/end`, `text.delta`, `tool.call`, `tool.result`, `artifact.created`. Last-Event-ID reconnect. ADR-001.
|
||||
2. **Artefatos tipados**: `citation`, `crop_image`, `entity_card`, `case_card`, `evidence_card`, `hypothesis_card`, `navigation_offer`. Cada um vira `Artifact` clicável no chat.
|
||||
3. **Persistência incremental**: cada `text.delta` flush em `messages` via Realtime. Aborto via header `x-chat-cancel: <session_id>`.
|
||||
4. **Refactor `chat-bubble.tsx`** (507 LOC) → quebra em `ChatComposer`, `ChatStream`, `ToolBlock`, `Citation`, `NavigationOffer`. Aumenta testabilidade.
|
||||
5. **Mobile**: passar Lighthouse mobile ≥85. Bento → grid responsivo. Modais ↔ bottom-sheets. Touch targets ≥44px.
|
||||
6. **Reranker warm-pool**: ADR-002 — health check warm; cache de rerank `(query_norm, candidate_ids)→scores`; rerank em background com SSE event `rerank_progress`.
|
||||
7. **Grafo paginado**: endpoint `/api/graph/neighbors?node=&depth=&limit=`. Carregar inicial só hub-and-spoke. Expand-on-click.
|
||||
|
||||
**Critério de aceite:**
|
||||
|
||||
- Chat aceita reconnect em queda de rede sem perder mensagem.
|
||||
- Cada citação no chat renderiza crop inline `<img>` (via `/api/crop`).
|
||||
- Lighthouse mobile ≥85 em 5 páginas-chave.
|
||||
- p95 hybrid_search warm <800ms; p95 rerank warm <3s.
|
||||
- `/graph` carrega <1s com hub-and-spoke; expand busca via API.
|
||||
|
||||
**Esforço:** 10d.
|
||||
|
||||
### **Fase 5 — Multi-corpus (single-tenant) + API REST + MCP server** (12-14d)
|
||||
|
||||
> Abertura. Bureau como plataforma.
|
||||
|
||||
**Objetivo:** suportar 2-3 corpora além de war.gov/ufo; expor API REST estável; expor MCP server compatível com Claude Code/Cursor.
|
||||
|
||||
**Escopo:**
|
||||
|
||||
1. **Migration multi-corpus**: tabela `corpora`; FK `corpus_id` em `documents`/`chunks`/`entities`. RLS policy passa a filtrar por `corpus_id`. ADR-005.
|
||||
2. **Path layout**: `wiki/<corpus_id>/documents/...` (refactor estrutura) **ou** deploy-por-corpus (ADR-005). Recomendação atual: começar com pasta única `wiki/` e coluna `corpus_id` em DB, refactor de paths fica para fase 6.
|
||||
3. **Onboard de 2 corpora-piloto**: JFK files (pequeno, alto interesse) + Operação Prato 1977 (PT-BR, validação bilíngue real).
|
||||
4. **Corpus switcher na UI**: navbar tem dropdown; `/c/<corpus_id>` rotas; chat session vincula corpus.
|
||||
5. **API REST pública**: `/api/v1/{documents,chunks,entities,events,cases,hypotheses}` (read-only) via PostgREST com rate-limit por API key. Auth = Bearer token gerado em `/account/api-keys`.
|
||||
6. **MCP server**: `apps/mcp-server/` (Node TS) que expõe os mesmos 12 tools do chat como ferramentas MCP. Roda em container próprio na compose.
|
||||
7. **Documentação**: `/api/docs` (OpenAPI) + `/mcp/docs` (server card).
|
||||
|
||||
**Critério de aceite:**
|
||||
|
||||
- 3 corpora visíveis no switcher; cada um com >10 docs.
|
||||
- API key gera; `GET /api/v1/documents?corpus_id=jfk-files&limit=10` retorna 200.
|
||||
- Claude Code remoto consegue listar tools do MCP server e fazer uma `hybrid_search`.
|
||||
|
||||
**Esforço:** 14d.
|
||||
|
||||
### **Fase 6 — Hardening + Backups + Multi-tenant verdadeiro (opcional)** (8-15d)
|
||||
|
||||
> Operação adulta.
|
||||
|
||||
**Objetivo:** sistema sobrevive a queda do VPS, falhas humanas e abertura externa.
|
||||
|
||||
**Escopo:**
|
||||
|
||||
1. **Backups**: `pg_dump` diário → restic → S3-compatible (Backblaze B2 / Wasabi — self-host adjacent, não SaaS no data plane bruto). Restore testado mensal.
|
||||
2. **Snapshot do `wiki/` + `raw/`**: rsync mensal para B2.
|
||||
3. **Disaster recovery runbook** em `infra/DR.md`.
|
||||
4. **Rate-limit + abuse detection**: nginx-style limits no Kong; Postgres usage_events alimenta heuristic.
|
||||
5. **Multi-tenant verdadeiro** (opcional): se a fase 5 mostrar sinal de Persona D, escalar. RLS pesado, billing, isolamento. ADR-005 reaberto.
|
||||
|
||||
**Critério de aceite:**
|
||||
|
||||
- Restore test no staging restaura a infra a partir de zero em <1h.
|
||||
- Rate-limit aciona em 100 req/min por IP.
|
||||
- (Opcional) primeiro tenant externo onboarda sem suporte.
|
||||
|
||||
**Esforço:** 8-15d (conforme escopo multi-tenant).
|
||||
|
||||
## 4.8 Síntese — sequenciamento sugerido
|
||||
|
||||
```
|
||||
Mês 1 [█████░░░] Fase 0 (3-4d) + Fase 1 (5-6d)
|
||||
Mês 2 [████████] Fase 2 Case Bureau MVP (10-12d)
|
||||
Mês 3 [████████] Fase 3 Timeline+Dashboards+Mapa (10-12d)
|
||||
Mês 4 [██████░░] Fase 4 AG-UI + Mobile + Perf (8-10d)
|
||||
Mês 5 [████████] Fase 5 Multi-corpus + API + MCP (12-14d)
|
||||
Mês 6 [██████░░] Fase 6 Hardening + Backups (8-10d)
|
||||
```
|
||||
|
||||
Total: ~60d de engenharia sênior + IA-pair em ~6 meses. Espaço para 15-20d de imprevisto e iteração de design.
|
||||
|
||||
## 4.9 Decisões type-1 — ADRs documentados
|
||||
|
||||
Cada decisão estratégica vira ADR em `/05-adrs/`:
|
||||
|
||||
| ADR | Decisão | Documento |
|
||||
|---|---|---|
|
||||
| 001 | Pattern do chat: AG-UI v1 vs assistant-ui/react vs continuar com agui.ts caseiro | `05-adrs/ADR-001-chat-pattern-ag-ui.md` |
|
||||
| 002 | Caching e serving do reranker BGE-Reranker-v2-M3 | `05-adrs/ADR-002-reranker-caching.md` |
|
||||
| 003 | Pipeline de geração da Timeline (eliminar stubs) | `05-adrs/ADR-003-timeline-no-stubs.md` |
|
||||
| 004 | Pipeline Case Bureau (Tetlock + Locard) | `05-adrs/ADR-004-case-bureau-pipeline.md` |
|
||||
| 005 | Multi-tenant vs multi-corpus single-tenant | `05-adrs/ADR-005-multi-tenant-refactor.md` |
|
||||
|
||||
(ADRs 6+ ficam para o time de leads conforme avançam — por exemplo, "sigma vs cytoscape", "Nominatim vs Geoapify self-host", "MapLibre tiles source".)
|
||||
|
||||
## 4.10 O que **não** fazer agora (anti-roadmap)
|
||||
|
||||
Por proporcionalidade Fowler — coisas que tentam o atalho da complexidade e ainda não se pagam:
|
||||
|
||||
1. **Kubernetes.** Docker Compose escala para 10x o tráfego atual. Não migrar antes de ≥20k MAU sustentados.
|
||||
2. **GraphQL.** REST + tipos pré-formados em SQL cobrem 95% dos casos de integração. Reabrir após 3 integradores externos pedirem.
|
||||
3. **Vector DB dedicado (Pinecone, Qdrant Cloud)** — pgvector + HNSW sustenta 100k chunks tranquilamente. Reabrir se latência p99 dense >500ms.
|
||||
4. **CDN proprietário (Cloudflare Pro+)** — Imgproxy + Next.js image optimization + Traefik com cache header cobrem. Reabrir após picos sustentados de tráfego.
|
||||
5. **Re-embed para dimensão maior (BGE-M3 → outro 4096-d)** — não há sinal de que recall esteja teto.
|
||||
6. **Migrar pipeline para Airflow / Prefect** — `scripts/` com Makefile + cron resolvem. Reabrir após 3 pipelines concorrentes em produção.
|
||||
7. **Microservizar o `web`** — monólito Next.js é certo para este tamanho. Não fragmentar antes de gargalo medido.
|
||||
|
||||
## 4.11 Riscos do roadmap
|
||||
|
||||
| Risco | Probabilidade | Mitigação |
|
||||
|---|---|---|
|
||||
| Enrichment de 22k entidades estoura orçamento OAuth Max 20x | Média | Limiar por `total_mentions ≥ 5` reduz para ~5-8k. Fila de 200/dia. |
|
||||
| Case Bureau red-team gera disputa de hipóteses pesada e demora | Baixa | Limita a 3 hipóteses por caso na fase 2. Quality gate threshold relaxado para 0.80 inicialmente. |
|
||||
| Multi-corpus refactor toca todo o web | Alta | Fase 5 isolada; flag `MULTI_CORPUS=on` permite voltar. |
|
||||
| MapLibre tiles open-source ficam lentas | Média | Fallback: MapTiler key (não-SaaS no data plane — só tiles). |
|
||||
| Rotação dos segredos vazados quebra deploy | Baixa | Staged: trocar em staging primeiro, depois prod, com runbook. |
|
||||
|
||||
---
|
||||
|
||||
_Esta arquitetura alvo e este roadmap são vinculantes para os leads (sa-architecture-lead, sa-engineering-lead, sa-platform-lead, sa-security-engineer, sa-qa-engineer) e ao gate de aprovação do sa-principal. Cada fase abre com um delegation; cada fase fecha com handoff_artifact + self_score._
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
---
|
||||
adr: 001
|
||||
title: "Chat pattern — substituir 'AG-UI style' caseiro pelo AG-UI v1 + artefatos tipados"
|
||||
status: Proposed
|
||||
date: 2026-05-17
|
||||
owner: sa-principal
|
||||
related_phase: Fase 4
|
||||
---
|
||||
|
||||
# ADR-001 — Chat pattern: AG-UI v1 + artefatos tipados
|
||||
|
||||
## Status
|
||||
|
||||
Proposed — aguarda aprovação do `sa-principal` antes da Fase 4 iniciar.
|
||||
|
||||
## Context
|
||||
|
||||
O chat do Disclosure Bureau é o canal primário de uso do produto. Hoje a implementação está dividida entre três módulos:
|
||||
|
||||
- `/Users/guto/ufo/web/lib/chat/agui.ts` (65 LOC) — eventos SSE caseiros (`text_delta`, `tool_start`, `tool_result`, `navigate`, `done`, `error`).
|
||||
- `/Users/guto/ufo/web/lib/chat/openrouter.ts` (341 LOC) — Pattern C (streaming + tool-call loop client-side) sobre OpenAI-compat da OpenRouter.
|
||||
- `/Users/guto/ufo/web/components/chat-bubble.tsx` (507 LOC) — UI consumindo SSE manualmente.
|
||||
|
||||
O comentário no topo do `agui.ts` é explícito: _"We don't implement the full AG-UI protocol — we use a simplified event set"_. Isso traz limites concretos:
|
||||
|
||||
1. **Sem reconnect com Last-Event-ID** — queda de rede perde a mensagem inteira.
|
||||
2. **Sem persistência incremental** — `text_delta` só vira `messages` row após `done`.
|
||||
3. **Sem cancelamento limpo** — `AbortController` no client não chega ao server; tools continuam executando.
|
||||
4. **Sem artefatos tipados** — citações são strings markdown parseadas no front, fáceis de errar.
|
||||
5. **Sem crops bbox inline** — citação aponta para `/d/<doc>#<chunk>`, mas o crop só renderiza fora do chat.
|
||||
|
||||
Adicionalmente, `@assistant-ui/react@^0.14.0` está no `package.json` e **não é importado em runtime** (`grep -r "@assistant-ui" web/{app,components,lib}` → 0 hits). Decisão técnica adiada.
|
||||
|
||||
O brief lista esse problema como gap crítico #4: "A2UI / AG-UI no chat — streaming tool-calls, artefatos clicáveis, crops bbox inline, multi-turn persistente."
|
||||
|
||||
## Opções consideradas
|
||||
|
||||
### Opção A — Continuar com `agui.ts` caseiro, adicionar features
|
||||
|
||||
- Manter o protocolo proprietário, adicionar reconnect, artefatos tipados, cancelamento.
|
||||
- **Custo:** ~6d.
|
||||
- **Prós:** zero migração; controle total.
|
||||
- **Contras:** reinventa AG-UI v1 (que já é open-spec e bem documentado); custo de manutenção cresce; ecossistema não conecta.
|
||||
|
||||
### Opção B — Adotar AG-UI v1 (protocolo aberto)
|
||||
|
||||
- Substituir o protocolo SSE proprietário pela spec [AG-UI v1](https://github.com/ag-ui-protocol/ag-ui): eventos `state.start`, `text.delta`, `tool.call`, `tool.result`, `artifact.created`, `state.end`, `error`. Reconnect via `Last-Event-ID`.
|
||||
- Client: implementação manual (~150 LOC) ou biblioteca compatível (`@ag-ui/client` em early stage).
|
||||
- **Custo:** ~5d.
|
||||
- **Prós:** spec aberta e versionada; outros sistemas (Claude Code, Codex) podem virar clients do mesmo backend; alavanca pra MCP server na Fase 5.
|
||||
- **Contras:** AG-UI v1 ainda é jovem; spec pode mudar (versionamos a versão exata no chat-runtime).
|
||||
|
||||
### Opção C — Adotar `@assistant-ui/react` + LangGraph runtime
|
||||
|
||||
- Usar a lib já no `package.json`. Trade-off: o `@assistant-ui/react` ofusca o protocolo SSE sob abstrações React; persistência fica acoplada à lib.
|
||||
- **Custo:** ~4d.
|
||||
- **Prós:** UI pronta (composer, mensagens, tool blocks); animações grátis.
|
||||
- **Contras:** opinião pesada na arquitetura; integração com nossos tools custom + Pattern C custa; menos controle do SSE protocol.
|
||||
|
||||
## Decisão
|
||||
|
||||
**Adotar Opção B — AG-UI v1 com client implementado manualmente.**
|
||||
|
||||
Justificativas, em ordem de peso:
|
||||
|
||||
1. **Spec aberta.** AG-UI v1 é versionado e públicó. Não comemos ofuscação proprietária de UI lib.
|
||||
2. **Alavanca para Fase 5 (MCP server).** Os mesmos artefatos tipados (`citation`, `crop_image`, `entity_card`) são reutilizados como artifacts MCP. Um único contrato serve chat e agent integrations.
|
||||
3. **Backwards-compatível com nosso `tools.ts`.** Não muda a forma como tools são definidos, só como o stream é emitido.
|
||||
4. **Controle do SSE protocol.** Reconnect, cancelamento, persistência incremental ficam em nosso código, não em deps.
|
||||
5. **Remove `@assistant-ui/react` do `package.json`** — débito 2.2.13 fechado.
|
||||
|
||||
## Implementação
|
||||
|
||||
Esquema do contrato (versão 1.0):
|
||||
|
||||
```typescript
|
||||
type AGUIEvent =
|
||||
| { kind: "state.start"; session_id: string; turn_id: string }
|
||||
| { kind: "text.delta"; turn_id: string; delta: string }
|
||||
| { kind: "tool.call"; turn_id: string; id: string; name: string; args: Record<string, unknown> }
|
||||
| { kind: "tool.result"; turn_id: string; id: string; result: unknown; duration_ms: number }
|
||||
| { kind: "artifact.created"; turn_id: string; artifact: Artifact }
|
||||
| { kind: "state.end"; turn_id: string; usage: { tokens_in: number; tokens_out: number } }
|
||||
| { kind: "error"; turn_id: string; code: string; message: string };
|
||||
|
||||
type Artifact =
|
||||
| { type: "citation"; chunk_id: string; doc_id: string; page: number; bbox: BBox }
|
||||
| { type: "crop_image"; src: string; alt_en: string; alt_pt: string }
|
||||
| { type: "entity_card"; class: EntityClass; id: string }
|
||||
| { type: "case_card"; case_id: string }
|
||||
| { type: "evidence_card"; evidence_id: string }
|
||||
| { type: "hypothesis_card"; hypothesis_id: string }
|
||||
| { type: "navigation_offer"; target: string; label_en: string; label_pt: string };
|
||||
```
|
||||
|
||||
Persistência incremental: cada `text.delta` apenda em buffer; ao `state.end`, materializa em `messages`. Reconnect: `EventSource` com header `Last-Event-ID: <event_seq>`; server replay desde o último seq.
|
||||
|
||||
Cancelamento: `POST /api/chat/cancel { session_id }` flag em memória + `chat_sessions.cancel_requested=true`; loop de tool-call checa antes de cada turno.
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positivas
|
||||
|
||||
- Reconnect resiliente em redes móveis ruins (Persona A/C frequentemente lê no celular).
|
||||
- Artefatos tipados → menos bug de parsing de citação markdown.
|
||||
- Crops inline no chat (`crop_image` artifact gerado quando tool retorna chunk com bbox).
|
||||
- Mensagens "salvas em progresso" mesmo se o usuário fecha o modal.
|
||||
- MCP server (Fase 5) usa o mesmo contrato de artifacts — economia de design.
|
||||
|
||||
### Negativas
|
||||
|
||||
- ~5d de engenharia (versus 0d se ficássemos no caseiro).
|
||||
- AG-UI v1 spec pode evoluir; precisamos versionar `chat-runtime` com pinning de versão.
|
||||
- `@assistant-ui/react` removido do package.json — qualquer demo que importou via copy/paste de docs externos quebra (provavelmente nenhum, mas verificar).
|
||||
|
||||
### Neutras
|
||||
|
||||
- Não muda `tools.ts` (12 handlers permanecem). Não muda `OpenRouter`/`Claude Code` providers — só muda o "frame" de eventos emitidos.
|
||||
|
||||
## Validação
|
||||
|
||||
- Smoke test: usuário envia "/test/disconnect" durante streaming → desconecta rede 5s → reconecta → mensagem continua sem perda.
|
||||
- E2E: tool `hybrid_search` retorna 5 hits → 5 `artifact.created` `citation` aparecem inline no chat; cada citação clica → `/d/.../p007#c0042` com crop visível.
|
||||
|
||||
## Referências
|
||||
|
||||
- AG-UI spec: <https://github.com/ag-ui-protocol/ag-ui>
|
||||
- Pattern C streaming: anthropic + openai cookbooks
|
||||
- Issue: gap #4 do brief original
|
||||
|
|
@ -0,0 +1,169 @@
|
|||
---
|
||||
adr: 002
|
||||
title: "Caching e serving do reranker BGE-Reranker-v2-M3"
|
||||
status: Proposed
|
||||
date: 2026-05-17
|
||||
owner: sa-principal
|
||||
related_phase: Fase 4
|
||||
---
|
||||
|
||||
# ADR-002 — Caching e serving do reranker
|
||||
|
||||
## Status
|
||||
|
||||
Proposed.
|
||||
|
||||
## Context
|
||||
|
||||
O `embed-service` (`/Users/guto/ufo/infra/embed-service/app.py`, FastAPI 148 LOC) carrega BGE-M3 + BGE-Reranker-v2-M3 em CPU. Lazy load via `Lock`. Primeira chamada após restart custa ~30s para carregar ~2.5GB em RAM.
|
||||
|
||||
O cliente em `/Users/guto/ufo/web/lib/retrieval/embed.ts:16` usa `AbortSignal.timeout(60_000)` — workaround, não solução. O brief lista isso como gap médio: "Reranker 60s".
|
||||
|
||||
Cenários observados:
|
||||
|
||||
- **Primeira query do dia:** 30-60s (cold start). UX inaceitável.
|
||||
- **Query repetida quente:** 200-500ms por par query/doc. Para top-100 candidatos → 20-50s. Inaceitável também.
|
||||
- **Query única quente, candidatos novos:** ~3-8s para top-20 candidates.
|
||||
|
||||
A causa raiz não é o tamanho do modelo. É (a) cold start sem warm-up, (b) reranker é chamado síncrono dentro do hybrid_search request, (c) nenhum cache compartilha resultados entre usuários.
|
||||
|
||||
## Opções consideradas
|
||||
|
||||
### Opção A — Warm-pool + cache de rerank scores
|
||||
|
||||
- `healthcheck` do `embed` dispara `/embed` no startup (warm-up).
|
||||
- `restart: unless-stopped` no docker mantém quente.
|
||||
- Tabela Postgres `rerank_cache(hash TEXT PK, scores REAL[], created_at TIMESTAMPTZ)` com hash = SHA-256 de `(query_normalized, candidate_ids_sorted)`.
|
||||
- TTL 24h (lru_count <100k).
|
||||
- **Custo:** 3d.
|
||||
- **Prós:** simples, sem novo serviço; ataca cold start e queries repetidas.
|
||||
- **Contras:** queries únicas com candidatos novos continuam 3-8s.
|
||||
|
||||
### Opção B — Warm-pool + cache + rerank assíncrono com SSE progress
|
||||
|
||||
- Mesmo cache da Opção A.
|
||||
- Hybrid search retorna RRF top-20 imediatamente (já ordenado mas sem rerank). UI mostra resultados.
|
||||
- Rerank roda em background, emite SSE event `rerank_progress` com top-5 reordenado. UI atualiza inline.
|
||||
- **Custo:** 5d.
|
||||
- **Prós:** UX percebida em <1s sempre; rerank vira luxo enriquecedor, não bloqueador.
|
||||
- **Contras:** complexidade de coordenação client/server; rerank ainda gasta CPU.
|
||||
|
||||
### Opção C — Migrar reranker para GPU (Modal / RunPod / local GPU box)
|
||||
|
||||
- BGE-Reranker-v2-M3 em GPU custa <50ms por par.
|
||||
- **Custo:** 4d + ongoing $40-150/mês.
|
||||
- **Prós:** latência ideal.
|
||||
- **Contras:** **VIOLA restrição "self-hosted Docker Compose em VPS"** se for SaaS; precisa de VPS GPU dedicado (e.g. Hetzner GEX44 ~€100/mês). Não cabe no orçamento atual.
|
||||
|
||||
### Opção D — Trocar o reranker por modelo menor
|
||||
|
||||
- BGE-Reranker-v2-M3 → MiniLM-L-6 (32x menor, 10x mais rápido, recall pior).
|
||||
- **Custo:** 2d.
|
||||
- **Prós:** baixa latência sem trocar de host.
|
||||
- **Contras:** **recall@5 estimado -10 a -20%**. Não validado. Risco de regressão de qualidade.
|
||||
|
||||
### Opção E — Não rerankear; usar só RRF
|
||||
|
||||
- Skip rerank. Devolver direto top-k do `hybrid_search_chunks` RPC.
|
||||
- **Custo:** 0.5d.
|
||||
- **Prós:** latência consistente <1.3s.
|
||||
- **Contras:** perde ~5-15% de precisão@5 dependendo da query (BGE-Reranker melhora especialmente queries semânticas longas).
|
||||
|
||||
## Decisão
|
||||
|
||||
**Adotar Opção B — Warm-pool + cache + rerank assíncrono com SSE progress.**
|
||||
|
||||
Fundamentos:
|
||||
|
||||
1. **Percepção de latência** é o que conta para o usuário, não latência total. Mostrar top-20 RRF em <1s e enriquecer com rerank em background dá UX excelente sem violar restrições.
|
||||
2. **Cache cobre Persona A/B (jornalistas, pesquisadores)** que repetem queries semelhantes em sessão.
|
||||
3. **Warm-pool é mandatório de qualquer forma** — o cold start em produção é defeito.
|
||||
4. **GPU (Opção C) viola política self-hosted Docker Compose** e não cabe no orçamento. Reabrir só se latência rerank ficar reportada como bloqueante mesmo com cache + async.
|
||||
|
||||
## Implementação
|
||||
|
||||
### Warm-pool
|
||||
|
||||
```yaml
|
||||
# infra/disclosure-stack/docker-compose.yml — embed:
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "-X", "POST",
|
||||
"http://localhost:8000/embed",
|
||||
"-H", "content-type: application/json",
|
||||
"-d", '{"texts":["warmup"]}']
|
||||
interval: 60s
|
||||
timeout: 30s
|
||||
retries: 3
|
||||
start_period: 90s # tolera carregamento inicial
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
E no `app.py`, um endpoint `/warmup` dedicado que invoca embed + rerank com payload trivial para ambos os modelos.
|
||||
|
||||
### Cache
|
||||
|
||||
```sql
|
||||
-- migration 0004_rerank_cache.sql
|
||||
CREATE TABLE public.rerank_cache (
|
||||
hash TEXT PRIMARY KEY,
|
||||
query_normalized TEXT NOT NULL,
|
||||
candidate_ids INT[] NOT NULL,
|
||||
scores REAL[] NOT NULL,
|
||||
hits INT NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
last_hit TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
CREATE INDEX idx_rerank_cache_last_hit ON public.rerank_cache(last_hit);
|
||||
|
||||
-- TTL job: DELETE FROM rerank_cache WHERE last_hit < NOW() - INTERVAL '24 hours';
|
||||
-- Roda via cron container leve, 1x/dia.
|
||||
```
|
||||
|
||||
`web/lib/retrieval/hybrid.ts`:
|
||||
|
||||
1. Calcula `hash = sha256(normalize(query) + sort(candidate_ids))`.
|
||||
2. `SELECT scores FROM rerank_cache WHERE hash = $1`. Se hit, devolve direto.
|
||||
3. Se miss, chama `/rerank`. Insere com `ON CONFLICT (hash) DO UPDATE SET hits = hits + 1, last_hit = NOW()`.
|
||||
|
||||
### Rerank assíncrono
|
||||
|
||||
`web/app/api/search/hybrid/route.ts` vira SSE:
|
||||
|
||||
1. Recebe query, retorna `top-20 RRF` imediatamente como `event: results.initial`.
|
||||
2. Em background, chama rerank.
|
||||
3. Emite `event: results.reranked` com nova ordem.
|
||||
4. UI: estado `results.optimistic` mostra os 20; ao receber `results.reranked`, transição animada.
|
||||
|
||||
Para chat (`hybrid_search` tool), mantém comportamento síncrono — o LLM precisa do top-5 estável. Mas o cache acelera.
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positivas
|
||||
|
||||
- Cold start eliminado pelo warm-pool.
|
||||
- p50 hybrid_search percebido <800ms.
|
||||
- p95 rerank warm <3s (com cache hit, instantâneo).
|
||||
- Cache compartilhado entre usuários — escala viral em queries trending.
|
||||
|
||||
### Negativas
|
||||
|
||||
- Complexidade de coordenação client/server no SSE inicial→reranked.
|
||||
- Cache pode entupir se `unaccent + normalization` falhar — monitorar tamanho e hits ratio.
|
||||
- ~3d a mais que a Opção A simples.
|
||||
|
||||
### Neutras
|
||||
|
||||
- Tudo se mantém em CPU local; nada vai para SaaS.
|
||||
- BGE-Reranker-v2-M3 stays — sem regressão de recall.
|
||||
|
||||
## Validação
|
||||
|
||||
- Latência: medir p50/p95 de `hybrid_search` end-to-end e de `/rerank` isolado, antes e depois.
|
||||
- Cache hit ratio após 7d de tráfego → meta ≥40%.
|
||||
- Smoke: matar o container `embed` e verificar que `healthcheck` reativa warm em <90s.
|
||||
|
||||
## Referências
|
||||
|
||||
- BGE-Reranker-v2-M3 perf: <https://huggingface.co/BAAI/bge-reranker-v2-m3>
|
||||
- Gap #5 médio do brief
|
||||
- `web/lib/retrieval/embed.ts:16` timeout 60s atual
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
---
|
||||
adr: 003
|
||||
title: "Geração da Timeline — eliminar stubs 'Phase X' e materializar narrativa real"
|
||||
status: Accepted (blocking — Phase 0)
|
||||
date: 2026-05-17
|
||||
owner: sa-principal
|
||||
related_phase: Fase 0 + Fase 3
|
||||
---
|
||||
|
||||
# ADR-003 — Timeline sem stubs
|
||||
|
||||
## Status
|
||||
|
||||
Accepted. **Bloqueante para entrega.** O usuário está furioso com os stubs "Phase X" e é a primeira coisa que cai na fase 0.
|
||||
|
||||
## Context
|
||||
|
||||
Estado atual: `/Users/guto/ufo/wiki/entities/events/` tem 7.091 eventos. Desses, **7.090 (99.99%) carregam `narrative_summary: _Stub. Will be enriched in Phase 7._`** — só 1 evento tem narrativa real.
|
||||
|
||||
Origem do mal: `/Users/guto/ufo/scripts/03-dedup-entities.py`, linhas 167-169:
|
||||
|
||||
```python
|
||||
"narrative_summary": "_Stub. Will be enriched in Phase 7._",
|
||||
"narrative_summary_pt_br": "_Stub. Será enriquecido na Fase 7._",
|
||||
# (e o body também recebe "_Stub generated by entity dedup. Will be enriched in Phase 6._")
|
||||
```
|
||||
|
||||
A página `/timeline` (web/app/timeline/page.tsx + web/components/timeline-view.tsx) carrega esses 7.091 eventos via `/api/timeline/route.ts`, agrupa por década e renderiza cards com `narrative_summary.slice(0, 280)`. Resultado visível: décadas inteiras com "_Stub. Will be enriched in Phase 7._" repetido.
|
||||
|
||||
Impacto:
|
||||
|
||||
- **Promessa de produto destruída.** Quem entra em `/timeline` vê alpha quebrado.
|
||||
- **Métrica:** 99.99% dos eventos sem narrativa real.
|
||||
- **22.096 arquivos** em `wiki/entities/**/*.md` carregam variantes desse stub textual.
|
||||
|
||||
## Opções consideradas
|
||||
|
||||
### Opção A — Apagar a Timeline temporariamente
|
||||
|
||||
- Esconde sintoma, não causa.
|
||||
- **Custo:** 0.5d.
|
||||
- **Veto:** Timeline é feature de homepage; removê-la em vez de consertar é teatro.
|
||||
|
||||
### Opção B — Filtrar stubs no `/api/timeline` por default
|
||||
|
||||
- Adicionar `WHERE narrative_summary IS NOT NULL AND narrative_summary NOT LIKE '\_Stub%'` na query (ou equivalente filesystem).
|
||||
- Restante visível com `?include_stubs=true`.
|
||||
- **Custo:** 1d.
|
||||
- **Prós:** Timeline carrega 1 evento (estado mínimo honesto) em vez de 7.091 stubs.
|
||||
- **Contras:** sozinho não resolve o problema — a UI fica vazia.
|
||||
|
||||
### Opção C — Eliminar o texto stub do source + pipeline de enrichment real
|
||||
|
||||
- Patch `03-dedup-entities.py` para nunca emitir stub textual: `narrative_summary: null`, `summary_status: 'none'`.
|
||||
- Script `scripts/maintain/41_dedup.py` migra os 22.096 arquivos existentes (strip + status field).
|
||||
- Curadoria de 50-100 eventos canônicos pelo case-writer (Holmes/Watson voz) com narrativa rica.
|
||||
- Pipeline `scripts/synthesize/20_entity_summary.py` itera eventos com `total_mentions ≥ 5`, gera narrative via Claude Code OAuth, popula DB + escreve no MD.
|
||||
- **Custo:** 3d (fase 0 — patch + migração + curadoria mínima 20 eventos) + 8d (fase 3 — pipeline enrichment 2k+).
|
||||
- **Prós:** ataca causa, não sintoma; UI fica honesta + rica.
|
||||
- **Contras:** custo maior; depende de Claude Code OAuth não estourar quota.
|
||||
|
||||
### Opção D — Auto-gerar narrative no momento da request
|
||||
|
||||
- Lazy enrichment: ao abrir o evento, dispara Claude Code; cache no DB.
|
||||
- **Custo:** 2d.
|
||||
- **Veto:** primeiro hit fica 8-20s (péssima UX); custo descontrolado (cada usuário pode bombar 7k eventos); LLM offline = página quebrada.
|
||||
|
||||
## Decisão
|
||||
|
||||
**Adotar Opção C — eliminar fonte + migrar arquivos + curadoria âncora + pipeline enrichment progressivo.**
|
||||
|
||||
Quebra em duas ondas:
|
||||
|
||||
### Onda 1 (Fase 0, 3d — bloqueante)
|
||||
|
||||
1. **Patch `scripts/03-dedup-entities.py`**: substituir as 3 linhas que emitem stub textual por campos vazios + `summary_status: 'none'`.
|
||||
|
||||
2. **Migração `scripts/maintain/41_strip_stubs.py`**: percorre `wiki/entities/**/*.md`. Para cada arquivo com stub textual:
|
||||
- YAML: seta `narrative_summary: null`, `narrative_summary_pt_br: null`, adiciona `summary_status: none`.
|
||||
- Body: remove blocos "_Stub generated by entity dedup. Will be enriched in Phase 6._" e mantém o restante.
|
||||
- Idempotente: já-migrado é detectado por presença de `summary_status` e pulado.
|
||||
- Append log em `wiki/log.md`.
|
||||
|
||||
3. **Update `/api/timeline/route.ts`**: filtro server-side; default = só eventos com `narrative_summary` não-nulo. Param `?include_unsynthesized=true` libera; UI mostra badge "sem síntese ainda".
|
||||
|
||||
4. **Update UI `timeline-view.tsx` + entity pages**: quando `narrative_summary` ausente, renderiza:
|
||||
- badge "Sem síntese ainda"
|
||||
- botão "Sintetizar agora" (visible para `role='admin'` — chama enrichment pontual via `/api/admin/synthesize/<entity_id>`)
|
||||
- linka para os chunks que mencionam a entidade (já temos `entity_mentions`).
|
||||
|
||||
5. **Curadoria âncora — 20 eventos manuais para a Timeline:** Roswell, Nimitz Tic-Tac, Phoenix Lights, Foo Fighters, Trindade Island BR, Operação Prato, Operação Mainbrace, AATIP disclosure, Belgian Wave, Rendlesham Forest, Cash-Landrum, Westall, Travis Walton, Father Gill, Lonnie Zamora, Maury Island, Kenneth Arnold, Aurora 1897, Foo lights 1944, Mantell crash. Cada um com `narrative_summary_en/pt` curados; `summary_status: 'curated'`.
|
||||
|
||||
### Onda 2 (Fase 3, 8d — enrichment progressivo)
|
||||
|
||||
1. **Pipeline `scripts/synthesize/20_entity_summary.py`**:
|
||||
- Itera entities com `total_mentions ≥ 5` ordenadas por `total_mentions DESC`.
|
||||
- Para cada uma, lê chunks via `entity_mentions`, monta prompt com top-10 menções verbatim, chama Claude Code OAuth (Sonnet para qualidade) em modo `--max-turns 1`.
|
||||
- Output: `narrative_summary_en` (3-6 frases) + `narrative_summary_pt` (3-6 frases) + `summary_status: 'synthesized'` + `summary_confidence: medium`.
|
||||
- Idempotente: `summary_status` ≠ 'none' pula.
|
||||
- Custo estimado: ~$80-150 para corpus inteiro a 5+ mentions threshold (~5-8k entidades).
|
||||
- Throttle: 200 entidades/dia para não estourar quota Max 20x.
|
||||
|
||||
2. **Quality gate**: para `summary_status='curated'`, lint exige `summary_confidence: high`. Para `summary_status='synthesized'`, default `medium` + check de heurística (não pode citar fato sem chunk_id no rationale).
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positivas
|
||||
|
||||
- `grep "Will be enriched in Phase" wiki/` → 0 hits após onda 1.
|
||||
- `/timeline` mostra ≥20 eventos com narrativa rica de imediato; cresce para ≥2k na fase 3.
|
||||
- Estado honesto: entidades sem síntese mostram badge claro, não texto enganoso.
|
||||
- Pipeline progressivo: enrichment não bloqueia release.
|
||||
|
||||
### Negativas
|
||||
|
||||
- Migração toca 22k arquivos — risco de corrupção YAML. Mitigação: dry-run + diff antes de commit; idempotência; backup do `wiki/` antes.
|
||||
- Custo OAuth ~$150 estimado se rodar enrichment completo.
|
||||
- 20 eventos curados manualmente = 1d de case-writer; precisa de revisão linguística PT-BR.
|
||||
|
||||
### Neutras
|
||||
|
||||
- Mantém estrutura de 24 tipos de markdown intacta. Só adiciona campo `summary_status` no schema YAML.
|
||||
|
||||
## Compatibilidade com CLAUDE.md
|
||||
|
||||
Schema canônico em `/Users/guto/ufo/CLAUDE-schema-full.md` precisa ganhar `summary_status` em `entity` frontmatter. Update do schema é parte da entrega da fase 0.
|
||||
|
||||
## Validação
|
||||
|
||||
- Antes da fase 0 mergear: `grep -c "Will be enriched" wiki/` precisa ser 0.
|
||||
- Após onda 1: `/timeline` carrega <2s com ≥20 cards renderizados, cada um com narrativa ≥100 chars.
|
||||
- Após onda 2: `/timeline` carrega ≥2.000 eventos com narrativa real.
|
||||
- Lint passa em todas as entidades migradas (regex IDs ainda válidos; YAML válido; backlinks consistentes).
|
||||
|
||||
## Referências
|
||||
|
||||
- `scripts/03-dedup-entities.py` (origem do stub textual).
|
||||
- `CLAUDE.md §3` (regras bilíngue) — `narrative_summary` precisa ter campos EN e PT-BR.
|
||||
- `CLAUDE.md §13` (triggers de enrichment) — `≥3 menções OR central claim` → `enrichment_status: deep`. Vamos espelhar essa lógica em `summary_status` (`none` < `synthesized` < `curated` < `red_teamed`).
|
||||
- Gap #1 do brief (timeline com stubs).
|
||||
|
|
@ -0,0 +1,218 @@
|
|||
---
|
||||
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.
|
||||
|
|
@ -0,0 +1,214 @@
|
|||
---
|
||||
adr: 005
|
||||
title: "Multi-tenant vs multi-corpus single-tenant — caminho de evolução"
|
||||
status: Proposed
|
||||
date: 2026-05-17
|
||||
owner: sa-principal
|
||||
related_phase: Fase 5 (multi-corpus) e Fase 6 (multi-tenant opcional)
|
||||
---
|
||||
|
||||
# ADR-005 — Multi-tenant vs multi-corpus single-tenant
|
||||
|
||||
## Status
|
||||
|
||||
Proposed.
|
||||
|
||||
## Context
|
||||
|
||||
A visão de longo prazo do brief lista "multi-tenant/multi-corpus". Atualmente o sistema é **single-corpus, single-tenant**:
|
||||
|
||||
- Toda tabela de retrieval (`documents`, `chunks`, `entities`, `entity_mentions`) referencia o corpus implícito war.gov/ufo.
|
||||
- `wiki/` é um único namespace; paths `wiki/documents/<doc-id>.md` colidem se onboardarmos JFK files ou Operação Prato BR.
|
||||
- `chat_sessions.user_id` aponta `auth.users(id)` direto; sem `tenant_id`.
|
||||
- RLS policy é `USING (TRUE)` — qualquer signed-up user vê tudo.
|
||||
- Frontend roteia `/d/<doc-id>` assumindo corpus único.
|
||||
|
||||
A diferença semântica importa:
|
||||
|
||||
- **Multi-corpus** = vários corpora coexistem mesma equipe os opera (war.gov/ufo + JFK + BR). Visibilidade pode variar (`public` / `unlisted` / `private`).
|
||||
- **Multi-tenant** = vários **operadores independentes** (organizações, indivíduos) com isolamento de dados, billing, gestão. Persona D do entregável 03.
|
||||
|
||||
## Opções consideradas
|
||||
|
||||
### Opção A — Pular direto para multi-tenant verdadeiro
|
||||
|
||||
- Refactor pesado: `tenant_id` em todas as tabelas; RLS por tenant; signup com tenant; billing.
|
||||
- **Custo:** 18-25d.
|
||||
- **Veto Cagan:** prematuro. Não temos demanda nem validação. Persona D é latente.
|
||||
|
||||
### Opção B — Multi-corpus single-tenant primeiro; multi-tenant só se houver sinal
|
||||
|
||||
- Adicionar `corpus_id` em `documents`/`chunks`/`entities`. Tabela `corpora` controla visibilidade.
|
||||
- Single-tenant = a equipe do Bureau opera tudo. Usuários signed-up só leem.
|
||||
- Multi-tenant fica para fase 6, **opcional**, ativado por flag.
|
||||
- **Custo:** 14d (fase 5) + 8-15d futuro condicional.
|
||||
- **Prós:** entrega valor real (3 corpora visíveis); reabre multi-tenant só com sinal.
|
||||
- **Contras:** schema decision não totalmente future-proof — algumas tabelas precisarão de `tenant_id` extra na fase 6.
|
||||
|
||||
### Opção C — Deploy por corpus (replicar a stack)
|
||||
|
||||
- Cada corpus é um deploy separado (`disclosure.top`, `jfk.disclosure.top`, `prato.disclosure.top`).
|
||||
- **Custo:** 4d por deploy.
|
||||
- **Prós:** zero refactor de schema.
|
||||
- **Contras:** **viola "single pane of glass"**; usuário não pode pesquisar cross-corpus; UX fragmentada; ops complexa (3x containers, 3x backups).
|
||||
|
||||
## Decisão
|
||||
|
||||
**Adotar Opção B — multi-corpus single-tenant na Fase 5; multi-tenant na Fase 6 só se Persona D mostrar sinal.**
|
||||
|
||||
Justificativas:
|
||||
|
||||
1. **Cagan:** valida hipótese com sinal mínimo. Multi-corpus desbloqueia Persona D-light (a equipe do Bureau adicionando corpora curados); multi-tenant verdadeiro requer demanda externa real.
|
||||
2. **Fowler:** arquitetura evolutiva — não over-engineer. `corpus_id` é refactor barato; `tenant_id` adicionado depois é refactor adicional barato. Não há "muralha" entre B e Multi-tenant na fase 6.
|
||||
3. **Larson:** decisão escrita. Reabre com critério: ≥3 organizações externas pedindo onboard antes de F6 expandir para multi-tenant.
|
||||
|
||||
## Implementação — Fase 5 (multi-corpus single-tenant)
|
||||
|
||||
### Schema
|
||||
|
||||
```sql
|
||||
-- migration 0005_multicorpus.sql
|
||||
|
||||
CREATE TABLE public.corpora (
|
||||
corpus_id TEXT PRIMARY KEY, -- 'war-gov-ufo', 'jfk-files', 'prato-1977'
|
||||
display_name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
language_primary TEXT NOT NULL DEFAULT 'en', -- 'en' | 'pt'
|
||||
visibility TEXT NOT NULL DEFAULT 'public'
|
||||
CHECK (visibility IN ('public','unlisted','private')),
|
||||
cover_image TEXT,
|
||||
doc_count INT NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Seed do corpus existente
|
||||
INSERT INTO public.corpora (corpus_id, display_name, language_primary, visibility)
|
||||
VALUES ('war-gov-ufo', 'War.gov/UFO — DoD UAP Declassified', 'en', 'public');
|
||||
|
||||
-- Adiciona FK em documents
|
||||
ALTER TABLE public.documents
|
||||
ADD COLUMN corpus_id TEXT NOT NULL DEFAULT 'war-gov-ufo'
|
||||
REFERENCES public.corpora(corpus_id) ON DELETE RESTRICT;
|
||||
ALTER TABLE public.documents ALTER COLUMN corpus_id DROP DEFAULT;
|
||||
|
||||
-- Idem chunks, entities, entity_mentions (via documents.corpus_id derivado ou denormalizado)
|
||||
-- Para queries grandes, denormalizar é melhor:
|
||||
ALTER TABLE public.chunks
|
||||
ADD COLUMN corpus_id TEXT NOT NULL DEFAULT 'war-gov-ufo';
|
||||
ALTER TABLE public.entities
|
||||
ADD COLUMN corpus_id TEXT NOT NULL DEFAULT 'war-gov-ufo';
|
||||
-- Drop defaults após backfill
|
||||
|
||||
-- Update RLS
|
||||
DROP POLICY documents_read ON public.documents;
|
||||
CREATE POLICY documents_read ON public.documents FOR SELECT USING (
|
||||
corpus_id IN (
|
||||
SELECT corpus_id FROM public.corpora WHERE visibility = 'public'
|
||||
)
|
||||
);
|
||||
-- Idem chunks, entities, mentions
|
||||
```
|
||||
|
||||
### Paths do `wiki/`
|
||||
|
||||
**Decisão dentro do ADR:** manter `wiki/` em raiz, identificar corpus via DB. Os paths físicos seguem `wiki/<corpus_id>/documents/...` mas com **link simbólico** `wiki/documents` → `wiki/war-gov-ufo/documents` por compat.
|
||||
|
||||
Trade-off: refactor de paths impacta `web/lib/wiki.ts` e `next.config.ts outputFileTracingIncludes`. Migration script `scripts/maintain/45_relayout_wiki.py` faz a movimentação atômica + cria os symlinks.
|
||||
|
||||
Para corpora futuros (JFK, Prato), paths já nascem `wiki/<corpus_id>/...`.
|
||||
|
||||
### Frontend
|
||||
|
||||
- Switcher de corpus na navbar: `<select>` listando corpora `public`. Estado em cookie `corpus_id`.
|
||||
- Rotas: `/c/<corpus_id>` (overview do corpus); `/d/<doc-id>` mantém-se mas exibe `corpus_id` como breadcrumb.
|
||||
- Search global tem checkbox "incluir todos os corpora" (default off — busca só no current).
|
||||
- Chat session vincula `corpus_id` (adicionar coluna em `chat_sessions`).
|
||||
|
||||
### Onboard de corpora-piloto
|
||||
|
||||
Para validar:
|
||||
|
||||
1. **JFK files** — corpus pequeno (~50 docs), alta visibilidade, valida pipeline single-language EN.
|
||||
2. **Operação Prato 1977** — corpus médio (~30 docs), valida bilíngue real (PT-BR como primary).
|
||||
|
||||
Cada onboard é um run da pipeline `scripts/ingest/*` com `--corpus-id <id>`. Validação: hybrid search funciona scoped ao corpus.
|
||||
|
||||
## Implementação — Fase 6 (multi-tenant verdadeiro, opcional)
|
||||
|
||||
**Critério para abrir:** ≥3 organizações externas distintas pedindo onboard com isolamento.
|
||||
|
||||
Adições:
|
||||
|
||||
```sql
|
||||
ALTER TABLE public.corpora
|
||||
ADD COLUMN owner_org_id UUID REFERENCES public.organizations(org_id);
|
||||
|
||||
CREATE TABLE public.organizations (
|
||||
org_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
name TEXT NOT NULL,
|
||||
plan TEXT NOT NULL DEFAULT 'free' CHECK (plan IN ('free','indie','team','enterprise')),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE public.memberships (
|
||||
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
|
||||
org_id UUID NOT NULL REFERENCES public.organizations(org_id) ON DELETE CASCADE,
|
||||
role TEXT NOT NULL CHECK (role IN ('owner','admin','editor','viewer')),
|
||||
PRIMARY KEY (user_id, org_id)
|
||||
);
|
||||
|
||||
-- RLS adicional
|
||||
CREATE POLICY documents_private_read ON public.documents FOR SELECT USING (
|
||||
corpus_id IN (
|
||||
SELECT c.corpus_id FROM public.corpora c
|
||||
LEFT JOIN public.memberships m ON c.owner_org_id = m.org_id
|
||||
WHERE c.visibility = 'public'
|
||||
OR (c.visibility IN ('unlisted','private') AND m.user_id = auth.uid())
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
Billing fica fora do escopo (decisão de produto, não de arquitetura). Stripe self-host via webhooks pode ser adicionado sem refactor de schema.
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positivas
|
||||
|
||||
- Multi-corpus desbloqueia Persona D-light + roadmap (JFK, Prato, etc.).
|
||||
- API REST e MCP server (Fase 5) se beneficiam — `?corpus_id=` filter natural.
|
||||
- Caminho gradual para multi-tenant verdadeiro sem retrabalho.
|
||||
|
||||
### Negativas
|
||||
|
||||
- Refactor de paths `wiki/` é tocar muita coisa (lib/wiki.ts, next.config.ts, scripts).
|
||||
- RLS policies ficam um pouco mais complexas — precisa de testes.
|
||||
- Switcher de corpus na UI = novo state model no frontend.
|
||||
|
||||
### Neutras
|
||||
|
||||
- Mantém VPS Docker Compose; não vai pra Kubernetes.
|
||||
- Bilíngue stack já pronto (cada corpus tem `language_primary`, mas chunks já são EN+PT).
|
||||
|
||||
## Validação
|
||||
|
||||
- Migration runs em <5min em staging.
|
||||
- 3 corpora visíveis na navbar; switcher troca contexto.
|
||||
- Hybrid search scoped ao corpus selecionado por default; checkbox "todos" funciona.
|
||||
- RLS test: usuário sem membership em corpus `private` recebe 0 rows.
|
||||
- Onboard de JFK: pipeline `scripts/ingest/*` com `--corpus-id jfk-files` produz `wiki/jfk-files/...` e popula DB sem afetar war-gov-ufo.
|
||||
|
||||
## Risco de abertura externa (relevante para fase 6)
|
||||
|
||||
Multi-tenant traz riscos novos:
|
||||
|
||||
- **PII em corpora privados** — política de retenção, compliance LGPD/GDPR.
|
||||
- **Custo de LLM por tenant** — quem paga o enrichment? OAuth quota não cobre N tenants.
|
||||
- **Abuse**: tenant tóxico envenenando corpus público (poisoning).
|
||||
|
||||
Esses ficam fora do escopo do ADR-005; serão tratados em ADR futuros se a fase 6 abrir.
|
||||
|
||||
## Referências
|
||||
|
||||
- Gap "multi-tenant/multi-corpus" do brief (visão longo prazo).
|
||||
- Persona D do entregável 03.
|
||||
- `/Users/guto/ufo/infra/supabase/migrations/0002_chunks_retrieval.sql` (schema atual single-corpus).
|
||||
|
|
@ -446,9 +446,24 @@ documented_in: [...]
|
|||
total_mentions: 18
|
||||
documents_count: 7
|
||||
|
||||
narrative_summary_confidence: high
|
||||
narrative_summary: |
|
||||
Em 14 de novembro de 2004, durante exercícios do CSG-11...
|
||||
narrative_summary_pt_br: |
|
||||
Em 14 de novembro de 2004, durante os exercícios do CSG-11...
|
||||
|
||||
# Synthesis pipeline status (Fase 0+3). Replaces the older
|
||||
# `narrative_summary_confidence` field which conflated provenance and quality.
|
||||
#
|
||||
# none — narrative absent. Renderer must show badge "sem síntese ainda"
|
||||
# and link to chunks via entity_mentions. NEVER emit placeholder
|
||||
# text like "_Stub. Will be enriched in Phase N._".
|
||||
# synthesized — produced automatically by scripts/synthesize/20_entity_summary.py
|
||||
# via Claude Code OAuth (Sonnet). summary_confidence default 'medium'.
|
||||
# curated — written or revised by hand (or by scripts/synthesize/01_anchor_events.py
|
||||
# for canonical anchor events). summary_confidence default 'high'.
|
||||
# red_teamed — curated + reviewed by chief-detective for factual accuracy.
|
||||
summary_status: curated # none | synthesized | curated | red_teamed
|
||||
summary_confidence: high # high | medium | low | null
|
||||
|
||||
related_events: ["[[event/EV-2015-XX-XX-gimbal]]"]
|
||||
preceded_by: []
|
||||
|
|
|
|||
|
|
@ -76,9 +76,8 @@ services:
|
|||
GOTRUE_JWT_EXP: 3600
|
||||
GOTRUE_JWT_SECRET: ${JWT_SECRET}
|
||||
GOTRUE_EXTERNAL_EMAIL_ENABLED: "true"
|
||||
# SMTP is not configured yet, so we auto-confirm signups (skips email
|
||||
# verification). Switch to "false" once SMTP_PASS is set.
|
||||
GOTRUE_MAILER_AUTOCONFIRM: "true"
|
||||
# SMTP configured (Spacemail) → email confirmation required for signups.
|
||||
GOTRUE_MAILER_AUTOCONFIRM: "false"
|
||||
GOTRUE_MAILER_OTP_EXP: 3600
|
||||
GOTRUE_SMTP_HOST: ${SMTP_HOST}
|
||||
GOTRUE_SMTP_PORT: ${SMTP_PORT}
|
||||
|
|
|
|||
|
|
@ -260,14 +260,15 @@ def collect_entities_from_pages(doc_filter: str | None = None) -> dict:
|
|||
return collected
|
||||
|
||||
|
||||
def _stub_body(entity_class: str, canonical_name: str) -> str:
|
||||
"""Standard bilingual stub body for new entities."""
|
||||
def _empty_body(entity_class: str, canonical_name: str) -> str:
|
||||
"""Header-only body for new entities; narrative is filled by the synthesis
|
||||
pipeline (scripts/synthesize/) when total_mentions ≥ 5, or by manual
|
||||
curation. We never emit placeholder text — `summary_status: none` in the
|
||||
frontmatter signals 'not yet synthesised' to the renderer."""
|
||||
return (
|
||||
f"# {canonical_name}\n\n"
|
||||
"## Description (EN)\n\n"
|
||||
"_Stub generated by entity dedup. Will be enriched in Phase 6._\n\n"
|
||||
"## Descrição (PT-BR)\n\n"
|
||||
"_Stub gerado pela deduplicação de entidades. Será enriquecido na Fase 6._\n"
|
||||
"## Descrição (PT-BR)\n"
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -421,7 +422,7 @@ def _upsert_simple_entity(
|
|||
fm["last_lint"] = None
|
||||
fm["wiki_version"] = WIKI_VERSION
|
||||
|
||||
body = _stub_body(entity_class, canonical_name)
|
||||
body = _empty_body(entity_class, canonical_name)
|
||||
write_frontmatter_and_body(path, fm, body, dry_run=dry_run)
|
||||
_register_in_index(dir_name, path, set(aliases_sorted), canonical_name)
|
||||
return ("created", True, path)
|
||||
|
|
@ -483,16 +484,17 @@ def _upsert_event(event_id: str, data: dict, dry_run: bool) -> tuple[str, bool,
|
|||
"documented_in": [],
|
||||
"total_mentions": total_mentions,
|
||||
"documents_count": len(unique_docs),
|
||||
"narrative_summary_confidence": "low",
|
||||
"narrative_summary": "_Stub. Will be enriched in Phase 7._",
|
||||
"narrative_summary_pt_br": "_Stub. Será enriquecido na Fase 7._",
|
||||
"narrative_summary": None,
|
||||
"narrative_summary_pt_br": None,
|
||||
"summary_status": "none",
|
||||
"summary_confidence": None,
|
||||
"enrichment_status": "none",
|
||||
"external_sources": [],
|
||||
"last_ingest": utc_now_iso(),
|
||||
"last_lint": None,
|
||||
"wiki_version": WIKI_VERSION,
|
||||
}
|
||||
body = _stub_body("events", canonical_name)
|
||||
body = _empty_body("events", canonical_name)
|
||||
write_frontmatter_and_body(path, fm, body, dry_run=dry_run)
|
||||
_register_in_index("events", path, set(labels), canonical_name)
|
||||
return ("created", True, path)
|
||||
|
|
@ -577,7 +579,7 @@ def _upsert_uap_object(obj_id: str, data: dict, dry_run: bool) -> tuple[str, boo
|
|||
"last_lint": None,
|
||||
"wiki_version": WIKI_VERSION,
|
||||
}
|
||||
body = _stub_body("uap_objects", canonical_name)
|
||||
body = _empty_body("uap_objects", canonical_name)
|
||||
write_frontmatter_and_body(path, fm, body, dry_run=dry_run)
|
||||
_register_in_index("uap-objects", path, set(), canonical_name)
|
||||
return ("created", True, path)
|
||||
|
|
|
|||
194
scripts/maintain/41_strip_stubs.py
Normal file
194
scripts/maintain/41_strip_stubs.py
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
41_strip_stubs.py — Eliminate "Will be enriched in Phase N" placeholders from
|
||||
wiki/entities/**/*.md.
|
||||
|
||||
For each entity markdown file:
|
||||
Frontmatter:
|
||||
- narrative_summary: "_Stub. Will be enriched in Phase 7._" → null
|
||||
- narrative_summary_pt_br: "_Stub. Será enriquecido na Fase 7._" → null
|
||||
- drop narrative_summary_confidence
|
||||
- add summary_status: 'none'
|
||||
- add summary_confidence: null
|
||||
|
||||
Body:
|
||||
- Remove paragraphs containing "_Stub generated by entity dedup. Will be
|
||||
enriched in Phase 6._" / "_Stub gerado pela deduplicação de entidades.
|
||||
Será enriquecido na Fase 6._"
|
||||
- Keep all other content untouched (curated narrative is preserved).
|
||||
|
||||
Idempotent: a file already migrated (has `summary_status` field) is skipped.
|
||||
|
||||
Usage:
|
||||
./41_strip_stubs.py # process all entities
|
||||
./41_strip_stubs.py --class events # only one class
|
||||
./41_strip_stubs.py --dry-run # report counts, don't write
|
||||
./41_strip_stubs.py --verbose # list each touched file
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import re
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
import yaml
|
||||
except ImportError:
|
||||
sys.stderr.write("pip3 install pyyaml\n")
|
||||
sys.exit(1)
|
||||
|
||||
UFO_ROOT = Path(__file__).resolve().parents[2]
|
||||
ENTITIES_BASE = UFO_ROOT / "wiki" / "entities"
|
||||
LOG_PATH = UFO_ROOT / "wiki" / "log.md"
|
||||
|
||||
STUB_EN = "_Stub. Will be enriched in Phase 7._"
|
||||
STUB_PT = "_Stub. Será enriquecido na Fase 7._"
|
||||
BODY_STUB_EN = "_Stub generated by entity dedup. Will be enriched in Phase 6._"
|
||||
BODY_STUB_PT = "_Stub gerado pela deduplicação de entidades. Será enriquecido na Fase 6._"
|
||||
|
||||
# Match any line that is exactly one of the body stubs (ignoring surrounding whitespace).
|
||||
BODY_STUB_LINE_RE = re.compile(
|
||||
r"^\s*(?:" + re.escape(BODY_STUB_EN) + r"|" + re.escape(BODY_STUB_PT) + r")\s*$",
|
||||
re.MULTILINE,
|
||||
)
|
||||
|
||||
|
||||
def utc_iso() -> str:
|
||||
return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||
|
||||
|
||||
def split_md(path: Path) -> tuple[dict, str, bool]:
|
||||
"""Return (frontmatter, body, had_frontmatter)."""
|
||||
raw = path.read_text(encoding="utf-8")
|
||||
if not raw.startswith("---"):
|
||||
return {}, raw, False
|
||||
end = raw.find("---", 4)
|
||||
if end == -1:
|
||||
return {}, raw, False
|
||||
try:
|
||||
fm = yaml.safe_load(raw[3:end].strip()) or {}
|
||||
except yaml.YAMLError:
|
||||
return {}, raw, False
|
||||
body = raw[end + 3 :].lstrip("\n")
|
||||
return fm, body, True
|
||||
|
||||
|
||||
def render_md(fm: dict, body: str) -> str:
|
||||
yaml_str = yaml.dump(fm, allow_unicode=True, sort_keys=False, default_flow_style=False)
|
||||
sep = "" if body.startswith("\n") else "\n"
|
||||
return f"---\n{yaml_str}---\n{sep}{body}"
|
||||
|
||||
|
||||
def clean_body(body: str) -> str:
|
||||
"""Drop lines that are exactly a stub paragraph; collapse 3+ blank lines to 2."""
|
||||
cleaned = BODY_STUB_LINE_RE.sub("", body)
|
||||
cleaned = re.sub(r"\n{3,}", "\n\n", cleaned)
|
||||
return cleaned
|
||||
|
||||
|
||||
def migrate_file(path: Path, dry_run: bool, verbose: bool) -> tuple[bool, bool]:
|
||||
"""Returns (changed, already_migrated)."""
|
||||
fm, body, had_fm = split_md(path)
|
||||
if not had_fm:
|
||||
return (False, False)
|
||||
|
||||
# Idempotency: presence of summary_status means already migrated.
|
||||
if "summary_status" in fm:
|
||||
return (False, True)
|
||||
|
||||
new_fm = dict(fm) # preserve insertion order
|
||||
|
||||
changed = False
|
||||
|
||||
# Clear stub textual values
|
||||
if new_fm.get("narrative_summary") == STUB_EN:
|
||||
new_fm["narrative_summary"] = None
|
||||
changed = True
|
||||
if new_fm.get("narrative_summary_pt_br") == STUB_PT:
|
||||
new_fm["narrative_summary_pt_br"] = None
|
||||
changed = True
|
||||
|
||||
# Drop old confidence key (replaced by summary_confidence)
|
||||
if "narrative_summary_confidence" in new_fm:
|
||||
new_fm.pop("narrative_summary_confidence", None)
|
||||
changed = True
|
||||
|
||||
# Always add summary_status / summary_confidence so subsequent runs detect
|
||||
# the migration as already done (even if this file had no stub fields).
|
||||
new_fm["summary_status"] = "none"
|
||||
new_fm.setdefault("summary_confidence", None)
|
||||
# Touch last_lint so downstream tools see the change
|
||||
new_fm["last_lint"] = utc_iso()
|
||||
|
||||
new_body = clean_body(body)
|
||||
if new_body != body:
|
||||
changed = True
|
||||
|
||||
if not changed:
|
||||
# No stub to strip, but we still added the status field — count as changed.
|
||||
changed = True
|
||||
|
||||
if dry_run:
|
||||
if verbose:
|
||||
print(f" (dry) {path.relative_to(UFO_ROOT)}")
|
||||
return (True, False)
|
||||
|
||||
path.write_text(render_md(new_fm, new_body), encoding="utf-8")
|
||||
if verbose:
|
||||
print(f" ✓ {path.relative_to(UFO_ROOT)}")
|
||||
return (True, False)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
p = argparse.ArgumentParser()
|
||||
p.add_argument("--class", dest="cls", default=None,
|
||||
help="restrict to one class folder under wiki/entities/")
|
||||
p.add_argument("--dry-run", action="store_true")
|
||||
p.add_argument("--verbose", action="store_true")
|
||||
args = p.parse_args()
|
||||
|
||||
if not ENTITIES_BASE.exists():
|
||||
sys.stderr.write(f"missing {ENTITIES_BASE}\n")
|
||||
return 1
|
||||
|
||||
if args.cls:
|
||||
roots = [ENTITIES_BASE / args.cls]
|
||||
else:
|
||||
roots = [d for d in ENTITIES_BASE.iterdir() if d.is_dir()]
|
||||
|
||||
total = 0
|
||||
migrated = 0
|
||||
skipped_already = 0
|
||||
for root in roots:
|
||||
for path in sorted(root.glob("*.md")):
|
||||
total += 1
|
||||
changed, already = migrate_file(path, args.dry_run, args.verbose)
|
||||
if already:
|
||||
skipped_already += 1
|
||||
elif changed:
|
||||
migrated += 1
|
||||
|
||||
print()
|
||||
print(f" total scanned: {total}")
|
||||
print(f" migrated: {migrated}")
|
||||
print(f" already migrated: {skipped_already}")
|
||||
print(f" dry-run: {args.dry_run}")
|
||||
|
||||
if not args.dry_run and migrated > 0:
|
||||
LOG_PATH.parent.mkdir(parents=True, exist_ok=True)
|
||||
with LOG_PATH.open("a", encoding="utf-8") as f:
|
||||
f.write(
|
||||
f"\n## {utc_iso()} · STRIP_STUBS\n"
|
||||
f"- script: scripts/maintain/41_strip_stubs.py\n"
|
||||
f"- scanned: {total}\n"
|
||||
f"- migrated: {migrated}\n"
|
||||
f"- already_migrated: {skipped_already}\n"
|
||||
)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
273
scripts/synthesize/01_anchor_events.py
Normal file
273
scripts/synthesize/01_anchor_events.py
Normal file
|
|
@ -0,0 +1,273 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
01_anchor_events.py — Seed the 20 anchor UAP events with curated bilingual
|
||||
narrative summaries via Claude Code OAuth subprocess (Sonnet).
|
||||
|
||||
Anchor list comes from ADR-003 (Phase 0). Each event:
|
||||
- Gets/creates wiki/entities/events/EV-<date>-<slug>.md
|
||||
- Frontmatter: summary_status=curated, summary_confidence=high,
|
||||
narrative_summary=<EN>, narrative_summary_pt_br=<PT-BR>
|
||||
- Body untouched if file already exists with manual edits.
|
||||
|
||||
Idempotent: re-run skips events where summary_status == 'curated'.
|
||||
|
||||
Usage:
|
||||
./01_anchor_events.py # all anchors
|
||||
./01_anchor_events.py --only roswell # one event (substring match)
|
||||
./01_anchor_events.py --dry-run # print prompt + would-write, no LLM call
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
import yaml
|
||||
except ImportError:
|
||||
sys.stderr.write("pip3 install pyyaml\n")
|
||||
sys.exit(1)
|
||||
|
||||
UFO_ROOT = Path(__file__).resolve().parents[2]
|
||||
EVENTS_DIR = UFO_ROOT / "wiki" / "entities" / "events"
|
||||
LOG_PATH = UFO_ROOT / "wiki" / "log.md"
|
||||
|
||||
# Each tuple = (event_id, canonical_name_en, date_start, primary_location, event_class, observers_hint)
|
||||
# date_start uses YYYY-MM-DD when known, YYYY when only year, YYYY-MM-XX if no day.
|
||||
ANCHOR_EVENTS = [
|
||||
("EV-1897-04-17-aurora-airship-crash", "Aurora Airship Crash", "1897-04-17",
|
||||
"Aurora, Texas, USA", "uap-encounter",
|
||||
"Local townspeople and newspaper reporters (Dallas Morning News, 17 abr 1897)"),
|
||||
("EV-1944-XX-XX-foo-fighters-european-theater", "Foo Fighters (European Theater)", "1944",
|
||||
"Western Front, Europe", "uap-encounter",
|
||||
"Allied bomber crews of the 415th Night Fighter Squadron and others"),
|
||||
("EV-1947-06-21-maury-island-incident", "Maury Island Incident", "1947-06-21",
|
||||
"Puget Sound, Washington, USA", "uap-encounter",
|
||||
"Harold Dahl, Fred Crisman, Kenneth Arnold (later investigator)"),
|
||||
("EV-1947-06-24-kenneth-arnold-mount-rainier", "Kenneth Arnold Mount Rainier Sighting", "1947-06-24",
|
||||
"Mount Rainier, Washington, USA", "uap-encounter",
|
||||
"Kenneth Arnold, civilian pilot"),
|
||||
("EV-1947-07-08-roswell-incident", "Roswell Incident", "1947-07-08",
|
||||
"Roswell, New Mexico, USA", "uap-encounter",
|
||||
"USAAF 509th Bombardment Group, William Brazel, Major Jesse Marcel"),
|
||||
("EV-1948-01-07-mantell-crash", "Mantell UFO Incident", "1948-01-07",
|
||||
"Franklin, Kentucky, USA", "uap-related-fatality",
|
||||
"Captain Thomas Mantell, Kentucky Air National Guard"),
|
||||
("EV-1948-07-24-chiles-whitted-encounter", "Chiles-Whitted UFO Encounter", "1948-07-24",
|
||||
"Montgomery, Alabama, USA", "uap-encounter",
|
||||
"Eastern Airlines pilots Clarence Chiles and John Whitted"),
|
||||
("EV-1952-09-XX-operation-mainbrace-sightings", "Operation Mainbrace UAP Sightings", "1952-09",
|
||||
"North Atlantic / Scandinavia", "uap-encounter",
|
||||
"NATO naval forces, RAF and USAF crews"),
|
||||
("EV-1959-06-26-father-gill-papua-encounter", "Father Gill Papua Encounter", "1959-06-26",
|
||||
"Boianai, Papua New Guinea", "uap-encounter",
|
||||
"Reverend William Gill and 37 mission staff and locals"),
|
||||
("EV-1964-04-24-lonnie-zamora-socorro", "Lonnie Zamora Socorro Landing", "1964-04-24",
|
||||
"Socorro, New Mexico, USA", "uap-encounter",
|
||||
"Police Sergeant Lonnie Zamora"),
|
||||
("EV-1966-04-06-westall-school-encounter", "Westall School Encounter", "1966-04-06",
|
||||
"Clayton South, Victoria, Australia", "uap-encounter",
|
||||
"Over 200 students and teachers of Westall High School"),
|
||||
("EV-1975-11-05-travis-walton-abduction", "Travis Walton Abduction", "1975-11-05",
|
||||
"Apache–Sitgreaves National Forest, Arizona, USA", "uap-abduction-claim",
|
||||
"Travis Walton and logging crew of six"),
|
||||
("EV-1977-09-XX-operacao-prato", "Operação Prato", "1977-09",
|
||||
"Ilha de Colares, Pará, Brasil", "uap-encounter",
|
||||
"Força Aérea Brasileira (FAB), Captain Uyrangê Hollanda and local residents"),
|
||||
("EV-1980-12-27-rendlesham-forest-incident", "Rendlesham Forest Incident", "1980-12-27",
|
||||
"Rendlesham Forest, Suffolk, UK (RAF Woodbridge / RAF Bentwaters)", "uap-encounter",
|
||||
"USAF personnel including Lt Col Charles Halt, Sgt Jim Penniston, John Burroughs"),
|
||||
("EV-1980-12-29-cash-landrum-incident", "Cash-Landrum Incident", "1980-12-29",
|
||||
"Dayton, Texas, USA", "uap-related-injury",
|
||||
"Betty Cash, Vickie Landrum, Colby Landrum"),
|
||||
("EV-1986-05-19-são-paulo-night-of-the-ufos", "São Paulo Noite Oficial dos OVNIs", "1986-05-19",
|
||||
"Costa do Brasil / São José dos Campos, SP", "uap-encounter",
|
||||
"FAB pilots flying Mirage III and F-5E intercepts, Brigadeiro Octavio Moreira Lima briefing"),
|
||||
("EV-1989-11-XX-belgian-wave", "Belgian UFO Wave", "1989-11",
|
||||
"Belgium (multiple sites)", "uap-encounter",
|
||||
"Belgian Air Force, gendarmerie and over 13,500 civilian witnesses"),
|
||||
("EV-1997-03-13-phoenix-lights", "Phoenix Lights", "1997-03-13",
|
||||
"Phoenix, Arizona, USA (and southern Arizona)", "uap-encounter",
|
||||
"Thousands of civilians, Governor Fife Symington (later)"),
|
||||
("EV-2004-11-14-nimitz-tic-tac", "Nimitz Tic Tac Incident", "2004-11-14",
|
||||
"USS Nimitz Carrier Strike Group, off San Diego coast", "uap-encounter",
|
||||
"USN F/A-18F crews Cdr David Fravor, Lt Cdr Jim Slaight, Lt Cdr Chad Underwood; USS Princeton radar (Senior Chief Kevin Day)"),
|
||||
("EV-2017-12-16-aatip-disclosure", "AATIP Public Disclosure", "2017-12-16",
|
||||
"New York / Washington, D.C., USA", "uap-disclosure-event",
|
||||
"New York Times reporting; Luis Elizondo, Harry Reid, Robert Bigelow"),
|
||||
]
|
||||
|
||||
# Voice rules (Holmes–Watson, fact-dense, no hype).
|
||||
PROMPT_TEMPLATE = """You are writing a curated encyclopedic event card for an investigative UAP/UFO wiki ("The Disclosure Bureau"). Voice rules:
|
||||
|
||||
- Holmes–Watson narrator: precise, fact-dense, no hype, no breathless language.
|
||||
- Open with what happened, where, when. Then who observed it. Then what made it remarkable. Optionally, what the official record / later investigations concluded.
|
||||
- 3–6 sentences. No editorial speculation beyond what is well-documented.
|
||||
- Use the *original language for verbatim quotes*; otherwise English for the EN summary and Brazilian Portuguese (pt-br with full UTF-8 accents) for the PT-BR summary. Do NOT translate already-Portuguese proper names ("Operação Prato" stays as-is in EN too).
|
||||
- Avoid the words "alegadamente"/"allegedly" unless it's genuinely contested. Be honest about uncertainty when warranted.
|
||||
- Never include sentences like "Will be enriched in Phase N" or any placeholder — this is the final text.
|
||||
|
||||
EVENT TO DOCUMENT:
|
||||
- ID: {event_id}
|
||||
- Canonical name: {name}
|
||||
- Date: {date}
|
||||
- Primary location: {location}
|
||||
- Class: {cls}
|
||||
- Known observers / parties: {observers}
|
||||
|
||||
OUTPUT (STRICT JSON, no markdown fences, no commentary):
|
||||
{{
|
||||
"narrative_summary": "<EN, 3-6 sentences>",
|
||||
"narrative_summary_pt_br": "<PT-BR brasileiro, 3-6 sentences>"
|
||||
}}"""
|
||||
|
||||
|
||||
def utc_iso() -> str:
|
||||
return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||
|
||||
|
||||
def call_sonnet(prompt: str, dry_run: bool = False) -> dict:
|
||||
"""Spawn `claude -p` subprocess (uses CLAUDE_CODE_OAUTH_TOKEN env) and return parsed JSON."""
|
||||
if dry_run:
|
||||
return {"narrative_summary": "[dry-run placeholder]", "narrative_summary_pt_br": "[dry-run placeholder]"}
|
||||
try:
|
||||
res = subprocess.run(
|
||||
["claude", "-p", "--model", "sonnet", "--output-format", "json"],
|
||||
input=prompt,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=180,
|
||||
check=False,
|
||||
)
|
||||
except subprocess.TimeoutExpired:
|
||||
raise RuntimeError("claude subprocess timed out after 180s")
|
||||
if res.returncode != 0:
|
||||
raise RuntimeError(f"claude exit {res.returncode}: {res.stderr[:300]}")
|
||||
# claude --output-format json returns wrapped envelope; extract `result`.
|
||||
try:
|
||||
env = json.loads(res.stdout)
|
||||
except json.JSONDecodeError as e:
|
||||
raise RuntimeError(f"unparseable claude stdout: {e} :: {res.stdout[:300]}")
|
||||
txt = env.get("result") or env.get("response") or env.get("content") or ""
|
||||
# Strip code fences if any
|
||||
txt = re.sub(r"^```(?:json)?\s*|\s*```$", "", txt.strip(), flags=re.MULTILINE).strip()
|
||||
# Try direct parse; on fail, extract first {...} block
|
||||
try:
|
||||
return json.loads(txt)
|
||||
except json.JSONDecodeError:
|
||||
m = re.search(r"\{[^{}]*\"narrative_summary\".*?\}", txt, flags=re.DOTALL)
|
||||
if not m:
|
||||
raise RuntimeError(f"no JSON object in claude output: {txt[:300]}")
|
||||
return json.loads(m.group(0))
|
||||
|
||||
|
||||
def load_yaml_body(path: Path) -> tuple[dict, str]:
|
||||
raw = path.read_text(encoding="utf-8")
|
||||
if not raw.startswith("---"):
|
||||
return {}, raw
|
||||
end = raw.find("---", 4)
|
||||
fm = yaml.safe_load(raw[3:end].strip()) or {}
|
||||
body = raw[end + 3:].lstrip("\n")
|
||||
return fm, body
|
||||
|
||||
|
||||
def write_yaml_body(path: Path, fm: dict, body: str) -> None:
|
||||
yaml_str = yaml.dump(fm, allow_unicode=True, sort_keys=False, default_flow_style=False)
|
||||
sep = "" if body.startswith("\n") else "\n"
|
||||
path.write_text(f"---\n{yaml_str}---\n{sep}{body}", encoding="utf-8")
|
||||
|
||||
|
||||
def upsert_anchor(event_id: str, name: str, date: str, location: str, cls: str, observers: str,
|
||||
dry_run: bool, only: str | None) -> tuple[str, bool]:
|
||||
if only and only.lower() not in (event_id.lower() + " " + name.lower()):
|
||||
return ("skipped (not matched by --only)", False)
|
||||
|
||||
path = EVENTS_DIR / f"{event_id}.md"
|
||||
existing_fm: dict = {}
|
||||
existing_body: str = ""
|
||||
if path.exists():
|
||||
existing_fm, existing_body = load_yaml_body(path)
|
||||
if existing_fm.get("summary_status") == "curated":
|
||||
return ("skipped (already curated)", False)
|
||||
|
||||
prompt = PROMPT_TEMPLATE.format(
|
||||
event_id=event_id, name=name, date=date, location=location, cls=cls, observers=observers,
|
||||
)
|
||||
print(f" → calling sonnet for {event_id} ...", flush=True)
|
||||
out = call_sonnet(prompt, dry_run=dry_run)
|
||||
narr_en = (out.get("narrative_summary") or "").strip()
|
||||
narr_pt = (out.get("narrative_summary_pt_br") or "").strip()
|
||||
if not narr_en or not narr_pt:
|
||||
return (f"empty output (en={len(narr_en)}, pt={len(narr_pt)})", False)
|
||||
|
||||
# Build/refresh frontmatter
|
||||
fm = {
|
||||
"schema_version": "0.1.0",
|
||||
"type": "entity",
|
||||
"entity_class": "event",
|
||||
"event_id": event_id,
|
||||
"canonical_name": name,
|
||||
"aliases": existing_fm.get("aliases") or [name],
|
||||
"event_class": cls,
|
||||
"date_start": date,
|
||||
"date_end": existing_fm.get("date_end") or date,
|
||||
"date_confidence": "high",
|
||||
"primary_location": location,
|
||||
"observers": existing_fm.get("observers") or [],
|
||||
"uap_objects": existing_fm.get("uap_objects") or [],
|
||||
"documented_in": existing_fm.get("documented_in") or [],
|
||||
"total_mentions": existing_fm.get("total_mentions") or 0,
|
||||
"documents_count": existing_fm.get("documents_count") or 0,
|
||||
"narrative_summary": narr_en,
|
||||
"narrative_summary_pt_br": narr_pt,
|
||||
"summary_status": "curated",
|
||||
"summary_confidence": "high",
|
||||
"enrichment_status": existing_fm.get("enrichment_status") or "none",
|
||||
"external_sources": existing_fm.get("external_sources") or [],
|
||||
"last_ingest": existing_fm.get("last_ingest") or utc_iso(),
|
||||
"last_lint": utc_iso(),
|
||||
"wiki_version": "0.1.0",
|
||||
}
|
||||
body = existing_body if existing_body.strip() else (
|
||||
f"# {name}\n\n## Description (EN)\n\n{narr_en}\n\n## Descrição (PT-BR)\n\n{narr_pt}\n"
|
||||
)
|
||||
if dry_run:
|
||||
return ("ok (dry)", True)
|
||||
EVENTS_DIR.mkdir(parents=True, exist_ok=True)
|
||||
write_yaml_body(path, fm, body)
|
||||
return ("ok", True)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
p = argparse.ArgumentParser()
|
||||
p.add_argument("--only", default=None, help="filter anchor events by substring match")
|
||||
p.add_argument("--dry-run", action="store_true")
|
||||
args = p.parse_args()
|
||||
|
||||
print(f"Anchor events: {len(ANCHOR_EVENTS)}")
|
||||
done = 0
|
||||
for ev in ANCHOR_EVENTS:
|
||||
msg, ok = upsert_anchor(*ev, dry_run=args.dry_run, only=args.only)
|
||||
sign = "✓" if ok else "·"
|
||||
print(f" {sign} {ev[0]} — {msg}")
|
||||
if ok:
|
||||
done += 1
|
||||
|
||||
if not args.dry_run and done > 0:
|
||||
with LOG_PATH.open("a", encoding="utf-8") as f:
|
||||
f.write(
|
||||
f"\n## {utc_iso()} · CURATE_ANCHOR_EVENTS\n"
|
||||
f"- script: scripts/synthesize/01_anchor_events.py\n"
|
||||
f"- curated: {done}/{len(ANCHOR_EVENTS)}\n"
|
||||
f"- model: claude-sonnet (via CLAUDE_CODE_OAUTH_TOKEN)\n"
|
||||
)
|
||||
print(f"\nCurated: {done}/{len(ANCHOR_EVENTS)}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
|
|
@ -32,6 +32,8 @@ interface TimelineEntry {
|
|||
date_end: string | null;
|
||||
primary_location?: string | null;
|
||||
narrative_summary?: string | null;
|
||||
summary_status: "none" | "synthesized" | "curated" | "red_teamed";
|
||||
total_mentions?: number;
|
||||
href: string;
|
||||
}
|
||||
|
||||
|
|
@ -67,6 +69,9 @@ export async function GET(req: Request) {
|
|||
const to = u.searchParams.get("to") ?? "";
|
||||
const q = (u.searchParams.get("q") ?? "").toLowerCase().trim();
|
||||
const limit = Math.min(Math.max(Number(u.searchParams.get("limit") ?? 200), 1), 500);
|
||||
// Default: only show events with curated/synthesised narrative — never stubs.
|
||||
// Opt-in `?include_unsynthesized=1` returns everything (admin / debug).
|
||||
const includeUnsynthesized = u.searchParams.get("include_unsynthesized") === "1";
|
||||
|
||||
const dir = path.join(WIKI, "entities", folder);
|
||||
let files: string[] = [];
|
||||
|
|
@ -88,7 +93,16 @@ export async function GET(req: Request) {
|
|||
if (from && sortable < dateSortable(from)) continue;
|
||||
if (to && sortable > dateSortable(to)) continue;
|
||||
const canonical = String(fm.canonical_name ?? f.replace(/\.md$/, ""));
|
||||
const narrative = String(fm.narrative_summary ?? "");
|
||||
const narrativeRaw = fm.narrative_summary;
|
||||
const narrative = typeof narrativeRaw === "string" ? narrativeRaw : "";
|
||||
const statusRaw = String(fm.summary_status ?? (narrative ? "synthesized" : "none"));
|
||||
const summary_status = (
|
||||
["none", "synthesized", "curated", "red_teamed"].includes(statusRaw)
|
||||
? statusRaw
|
||||
: "none"
|
||||
) as TimelineEntry["summary_status"];
|
||||
// Default: hide events without a real narrative.
|
||||
if (!includeUnsynthesized && summary_status === "none") continue;
|
||||
if (q && !canonical.toLowerCase().includes(q) && !narrative.toLowerCase().includes(q)) {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -99,7 +113,9 @@ export async function GET(req: Request) {
|
|||
date_start,
|
||||
date_end: (fm.date_end as string) ?? null,
|
||||
primary_location: (fm.primary_location as string) ?? null,
|
||||
narrative_summary: narrative.slice(0, 280) || null,
|
||||
narrative_summary: narrative ? narrative.slice(0, 280) : null,
|
||||
summary_status,
|
||||
total_mentions: typeof fm.total_mentions === "number" ? fm.total_mentions : undefined,
|
||||
href: `/e/${folder}/${f.replace(/\.md$/, "")}`,
|
||||
});
|
||||
} catch {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ interface TimelineEntry {
|
|||
date_end: string | null;
|
||||
primary_location: string | null;
|
||||
narrative_summary: string | null;
|
||||
summary_status?: "none" | "synthesized" | "curated" | "red_teamed";
|
||||
total_mentions?: number;
|
||||
href: string;
|
||||
}
|
||||
|
||||
|
|
@ -115,6 +117,11 @@ export function TimelineView({ initialSearch }: { initialSearch?: string }) {
|
|||
<span className="text-[#c8d4e6] font-bold flex-1 truncate">
|
||||
{e.canonical_name}
|
||||
</span>
|
||||
{e.summary_status === "curated" && (
|
||||
<span className="px-1.5 py-0.5 rounded border border-[#00ff9c] text-[#00ff9c] text-[9px]" title="curado manualmente">
|
||||
curado
|
||||
</span>
|
||||
)}
|
||||
{e.primary_location && (
|
||||
<span className="text-[#3fde6a] text-[10px]">{e.primary_location}</span>
|
||||
)}
|
||||
|
|
|
|||
67
wiki/entities/events/EV-1897-04-17-aurora-airship-crash.md
Normal file
67
wiki/entities/events/EV-1897-04-17-aurora-airship-crash.md
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
---
|
||||
schema_version: 0.1.0
|
||||
type: entity
|
||||
entity_class: event
|
||||
event_id: EV-1897-04-17-aurora-airship-crash
|
||||
canonical_name: Aurora Airship Crash
|
||||
aliases:
|
||||
- Aurora Airship Crash
|
||||
event_class: uap-encounter
|
||||
date_start: '1897-04-17'
|
||||
date_end: '1897-04-17'
|
||||
date_confidence: high
|
||||
primary_location: Aurora, Texas, USA
|
||||
observers: []
|
||||
uap_objects: []
|
||||
documented_in: []
|
||||
total_mentions: 0
|
||||
documents_count: 0
|
||||
narrative_summary: On 17 April 1897, at the height of the American 'mystery airship'
|
||||
wave, Dallas Morning News correspondent S.E. Haydon reported that an unidentified
|
||||
airship struck a windmill on the property of Judge J.S. Proctor in Aurora, Texas,
|
||||
scattering wreckage across several acres. Haydon's dispatch described a small humanoid
|
||||
occupant recovered from the debris, characterized by a local U.S. Army signal officer
|
||||
as 'not of this world,' and stated that townspeople buried the body in the Aurora
|
||||
Cemetery with Christian rites. No physical wreckage was preserved or independently
|
||||
catalogued beyond Haydon's account, and no corroborating eyewitnesses are identified
|
||||
by name in any contemporaneous record. MUFON investigators probed the alleged grave
|
||||
in the 1970s and reported anomalous metal fragments, but subsequent metallurgical
|
||||
analysis found the samples consistent with common iron alloys; the grave marker
|
||||
has since disappeared. The narrative shares structural features with dozens of contemporaneous
|
||||
'airship crash' dispatches from the same 1896–97 wave—several of which were later
|
||||
confirmed as journalistic fabrications—and the cumulative evidentiary record leaves
|
||||
the event's authenticity unresolved.
|
||||
narrative_summary_pt_br: Em 17 de abril de 1897, no auge da onda americana de 'navios
|
||||
aéreos misteriosos', o correspondente S.E. Haydon publicou no Dallas Morning News
|
||||
que uma aeronave não identificada colidiu com um moinho de vento na propriedade
|
||||
do juiz J.S. Proctor em Aurora, Texas, espalhando destroços por vários acres. O
|
||||
despacho de Haydon descrevia um pequeno ocupante humanoide recuperado dos escombros,
|
||||
caracterizado por um oficial do Exército norte-americano como 'not of this world',
|
||||
e afirmava que os moradores enterraram o corpo no Cemitério de Aurora com ritos
|
||||
cristãos. Nenhum destroço físico foi preservado ou catalogado de forma independente
|
||||
além do relato de Haydon, e nenhuma testemunha ocular é identificada pelo nome em
|
||||
qualquer registro contemporâneo. Investigadores da MUFON examinaram o suposto túmulo
|
||||
nos anos 1970 e relataram fragmentos metálicos anômalos, mas análises metalúrgicas
|
||||
posteriores indicaram amostras consistentes com ligas de ferro comuns; a lápide
|
||||
do túmulo desapareceu desde então. A narrativa compartilha características estruturais
|
||||
com dezenas de despachos contemporâneos de 'queda de navio aéreo' da onda de 1896–97
|
||||
— vários deles posteriormente confirmados como fabricações jornalísticas — e o conjunto
|
||||
do registro investigativo deixa a autenticidade do evento sem resolução.
|
||||
summary_status: curated
|
||||
summary_confidence: high
|
||||
enrichment_status: none
|
||||
external_sources: []
|
||||
last_ingest: '2026-05-18T03:30:25Z'
|
||||
last_lint: '2026-05-18T03:30:25Z'
|
||||
wiki_version: 0.1.0
|
||||
---
|
||||
|
||||
# Aurora Airship Crash
|
||||
|
||||
## Description (EN)
|
||||
|
||||
On 17 April 1897, at the height of the American 'mystery airship' wave, Dallas Morning News correspondent S.E. Haydon reported that an unidentified airship struck a windmill on the property of Judge J.S. Proctor in Aurora, Texas, scattering wreckage across several acres. Haydon's dispatch described a small humanoid occupant recovered from the debris, characterized by a local U.S. Army signal officer as 'not of this world,' and stated that townspeople buried the body in the Aurora Cemetery with Christian rites. No physical wreckage was preserved or independently catalogued beyond Haydon's account, and no corroborating eyewitnesses are identified by name in any contemporaneous record. MUFON investigators probed the alleged grave in the 1970s and reported anomalous metal fragments, but subsequent metallurgical analysis found the samples consistent with common iron alloys; the grave marker has since disappeared. The narrative shares structural features with dozens of contemporaneous 'airship crash' dispatches from the same 1896–97 wave—several of which were later confirmed as journalistic fabrications—and the cumulative evidentiary record leaves the event's authenticity unresolved.
|
||||
|
||||
## Descrição (PT-BR)
|
||||
|
||||
Em 17 de abril de 1897, no auge da onda americana de 'navios aéreos misteriosos', o correspondente S.E. Haydon publicou no Dallas Morning News que uma aeronave não identificada colidiu com um moinho de vento na propriedade do juiz J.S. Proctor em Aurora, Texas, espalhando destroços por vários acres. O despacho de Haydon descrevia um pequeno ocupante humanoide recuperado dos escombros, caracterizado por um oficial do Exército norte-americano como 'not of this world', e afirmava que os moradores enterraram o corpo no Cemitério de Aurora com ritos cristãos. Nenhum destroço físico foi preservado ou catalogado de forma independente além do relato de Haydon, e nenhuma testemunha ocular é identificada pelo nome em qualquer registro contemporâneo. Investigadores da MUFON examinaram o suposto túmulo nos anos 1970 e relataram fragmentos metálicos anômalos, mas análises metalúrgicas posteriores indicaram amostras consistentes com ligas de ferro comuns; a lápide do túmulo desapareceu desde então. A narrativa compartilha características estruturais com dezenas de despachos contemporâneos de 'queda de navio aéreo' da onda de 1896–97 — vários deles posteriormente confirmados como fabricações jornalísticas — e o conjunto do registro investigativo deixa a autenticidade do evento sem resolução.
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
---
|
||||
schema_version: 0.1.0
|
||||
type: entity
|
||||
entity_class: event
|
||||
event_id: EV-1944-XX-XX-foo-fighters-european-theater
|
||||
canonical_name: Foo Fighters (European Theater)
|
||||
aliases:
|
||||
- Foo Fighters (European Theater)
|
||||
event_class: uap-encounter
|
||||
date_start: '1944'
|
||||
date_end: '1944'
|
||||
date_confidence: high
|
||||
primary_location: Western Front, Europe
|
||||
observers: []
|
||||
uap_objects: []
|
||||
documented_in: []
|
||||
total_mentions: 0
|
||||
documents_count: 0
|
||||
narrative_summary: From late 1944 onward, aircrews of the 415th Night Fighter Squadron
|
||||
and other Allied units flying night sorties over the Rhine Valley began reporting
|
||||
clusters of luminous orange-red spheres that paced their aircraft with precision
|
||||
before breaking away at high speed. The phenomenon was first formally recorded on
|
||||
27 November 1944 by Lieutenant Edward Schlueter near Strasbourg; the objects were
|
||||
consistently non-hostile — they never opened fire, produced no exhaust trail, and
|
||||
any instrument interference was irregular and unconfirmed. Allied intelligence initially
|
||||
hypothesized German secret weapons or psychological-warfare drones, but post-war
|
||||
debriefings of Luftwaffe pilots established that German aircrews had filed identical
|
||||
sightings on the same nights, eliminating all Allied-side explanations. A U.S. Army
|
||||
Air Forces inquiry closed without a conclusive finding; the phenomena remain unattributed.
|
||||
The label 'foo fighter' was coined by the squadron's crews, adapted from the Smokey
|
||||
Stover comic-strip expression 'where there's foo, there's fire.'
|
||||
narrative_summary_pt_br: 'A partir do final de 1944, tripulações da 415ª Esquadrilha
|
||||
de Caça Noturno e de outras unidades aliadas em missões noturnas sobre o Vale do
|
||||
Reno passaram a relatar grupos de esferas luminosas laranja-avermelhadas que acompanhavam
|
||||
suas aeronaves com precisão antes de se afastarem em alta velocidade. O fenômeno
|
||||
foi registrado formalmente pela primeira vez em 27 de novembro de 1944 pelo tenente
|
||||
Edward Schlueter nas proximidades de Estrasburgo; os objetos eram consistentemente
|
||||
não hostis — nunca abriram fogo, não deixavam rastro de exaustão e qualquer interferência
|
||||
nos instrumentos era irregular e não confirmada. A inteligência aliada inicialmente
|
||||
levantou a hipótese de armas secretas alemãs ou drones de guerra psicológica, mas
|
||||
os interrogatórios pós-guerra de pilotos da Luftwaffe estabeleceram que tripulações
|
||||
alemãs haviam registrado avistamentos idênticos nas mesmas noites, eliminando todas
|
||||
as explicações do lado aliado. Uma investigação da Força Aérea do Exército dos EUA
|
||||
foi encerrada sem conclusão definitiva; o fenômeno permanece sem atribuição. O termo
|
||||
''foo fighter'' foi cunhado pelas tripulações da esquadrilha, adaptado da expressão
|
||||
da tirinha Smokey Stover: ''where there''s foo, there''s fire.'''
|
||||
summary_status: curated
|
||||
summary_confidence: high
|
||||
enrichment_status: none
|
||||
external_sources: []
|
||||
last_ingest: '2026-05-18T03:31:08Z'
|
||||
last_lint: '2026-05-18T03:31:08Z'
|
||||
wiki_version: 0.1.0
|
||||
---
|
||||
|
||||
# Foo Fighters (European Theater)
|
||||
|
||||
## Description (EN)
|
||||
|
||||
From late 1944 onward, aircrews of the 415th Night Fighter Squadron and other Allied units flying night sorties over the Rhine Valley began reporting clusters of luminous orange-red spheres that paced their aircraft with precision before breaking away at high speed. The phenomenon was first formally recorded on 27 November 1944 by Lieutenant Edward Schlueter near Strasbourg; the objects were consistently non-hostile — they never opened fire, produced no exhaust trail, and any instrument interference was irregular and unconfirmed. Allied intelligence initially hypothesized German secret weapons or psychological-warfare drones, but post-war debriefings of Luftwaffe pilots established that German aircrews had filed identical sightings on the same nights, eliminating all Allied-side explanations. A U.S. Army Air Forces inquiry closed without a conclusive finding; the phenomena remain unattributed. The label 'foo fighter' was coined by the squadron's crews, adapted from the Smokey Stover comic-strip expression 'where there's foo, there's fire.'
|
||||
|
||||
## Descrição (PT-BR)
|
||||
|
||||
A partir do final de 1944, tripulações da 415ª Esquadrilha de Caça Noturno e de outras unidades aliadas em missões noturnas sobre o Vale do Reno passaram a relatar grupos de esferas luminosas laranja-avermelhadas que acompanhavam suas aeronaves com precisão antes de se afastarem em alta velocidade. O fenômeno foi registrado formalmente pela primeira vez em 27 de novembro de 1944 pelo tenente Edward Schlueter nas proximidades de Estrasburgo; os objetos eram consistentemente não hostis — nunca abriram fogo, não deixavam rastro de exaustão e qualquer interferência nos instrumentos era irregular e não confirmada. A inteligência aliada inicialmente levantou a hipótese de armas secretas alemãs ou drones de guerra psicológica, mas os interrogatórios pós-guerra de pilotos da Luftwaffe estabeleceram que tripulações alemãs haviam registrado avistamentos idênticos nas mesmas noites, eliminando todas as explicações do lado aliado. Uma investigação da Força Aérea do Exército dos EUA foi encerrada sem conclusão definitiva; o fenômeno permanece sem atribuição. O termo 'foo fighter' foi cunhado pelas tripulações da esquadrilha, adaptado da expressão da tirinha Smokey Stover: 'where there's foo, there's fire.'
|
||||
65
wiki/entities/events/EV-1947-06-21-maury-island-incident.md
Normal file
65
wiki/entities/events/EV-1947-06-21-maury-island-incident.md
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
---
|
||||
schema_version: 0.1.0
|
||||
type: entity
|
||||
entity_class: event
|
||||
event_id: EV-1947-06-21-maury-island-incident
|
||||
canonical_name: Maury Island Incident
|
||||
aliases:
|
||||
- Maury Island Incident
|
||||
event_class: uap-encounter
|
||||
date_start: '1947-06-21'
|
||||
date_end: '1947-06-21'
|
||||
date_confidence: high
|
||||
primary_location: Puget Sound, Washington, USA
|
||||
observers: []
|
||||
uap_objects: []
|
||||
documented_in: []
|
||||
total_mentions: 0
|
||||
documents_count: 0
|
||||
narrative_summary: On 21 June 1947 — three days before Kenneth Arnold's Cascade Mountains
|
||||
sighting that gave rise to the term 'flying saucers' — Harold Dahl reported observing
|
||||
six large donut-shaped objects hovering over Puget Sound near Maury Island, Washington,
|
||||
while conducting salvage work with his son and a crewman. He claimed one of the
|
||||
objects ejected a shower of metallic slag and a lighter material that damaged his
|
||||
boat, injured his son, and killed his dog. Fred Crisman, Dahl's supervisor, subsequently
|
||||
handled samples of the recovered material and reported the incident to pulp editor
|
||||
Ray Palmer, who dispatched investigator Kenneth Arnold to the site. Army Air Force
|
||||
intelligence officers Captain William Davidson and Lieutenant Frank Brown flew to
|
||||
Tacoma to collect the samples; both died on 1 August 1947 when their B-25 crashed
|
||||
near Kelso, Washington, on the return flight. Dahl later told investigators the
|
||||
story was a fabrication; the FBI and Army Air Force concluded the incident was a
|
||||
hoax, and the recovered material was identified as slag consistent with industrial
|
||||
waste.
|
||||
narrative_summary_pt_br: Em 21 de junho de 1947 — três dias antes do avistamento de
|
||||
Kenneth Arnold nas Cascades que deu origem ao termo 'discos voadores' — Harold Dahl
|
||||
relatou observar seis grandes objetos em formato de rosca pairando sobre o Puget
|
||||
Sound próximo à Ilha Maury, Washington, enquanto realizava trabalhos de salvamento
|
||||
com seu filho e um tripulante. Segundo seu relato, um dos objetos ejetou uma chuva
|
||||
de escória metálica e material mais leve que danificou seu barco, feriu seu filho
|
||||
e matou seu cão. Fred Crisman, supervisor de Dahl, obteve amostras do material recuperado
|
||||
e reportou o incidente ao editor Ray Palmer, que enviou o investigador Kenneth Arnold
|
||||
ao local. Os oficiais de inteligência da Força Aérea do Exército, capitão William
|
||||
Davidson e tenente Frank Brown, voaram até Tacoma para recolher as amostras; ambos
|
||||
morreram em 1.º de agosto de 1947 quando seu B-25 caiu próximo a Kelso, Washington,
|
||||
no voo de retorno. Dahl posteriormente declarou aos investigadores que a história
|
||||
era uma fabricação; o FBI e a Força Aérea do Exército concluíram que o incidente
|
||||
foi uma farsa, e o material recuperado foi identificado como escória compatível
|
||||
com resíduos industriais.
|
||||
summary_status: curated
|
||||
summary_confidence: high
|
||||
enrichment_status: none
|
||||
external_sources: []
|
||||
last_ingest: '2026-05-18T03:31:42Z'
|
||||
last_lint: '2026-05-18T03:31:42Z'
|
||||
wiki_version: 0.1.0
|
||||
---
|
||||
|
||||
# Maury Island Incident
|
||||
|
||||
## Description (EN)
|
||||
|
||||
On 21 June 1947 — three days before Kenneth Arnold's Cascade Mountains sighting that gave rise to the term 'flying saucers' — Harold Dahl reported observing six large donut-shaped objects hovering over Puget Sound near Maury Island, Washington, while conducting salvage work with his son and a crewman. He claimed one of the objects ejected a shower of metallic slag and a lighter material that damaged his boat, injured his son, and killed his dog. Fred Crisman, Dahl's supervisor, subsequently handled samples of the recovered material and reported the incident to pulp editor Ray Palmer, who dispatched investigator Kenneth Arnold to the site. Army Air Force intelligence officers Captain William Davidson and Lieutenant Frank Brown flew to Tacoma to collect the samples; both died on 1 August 1947 when their B-25 crashed near Kelso, Washington, on the return flight. Dahl later told investigators the story was a fabrication; the FBI and Army Air Force concluded the incident was a hoax, and the recovered material was identified as slag consistent with industrial waste.
|
||||
|
||||
## Descrição (PT-BR)
|
||||
|
||||
Em 21 de junho de 1947 — três dias antes do avistamento de Kenneth Arnold nas Cascades que deu origem ao termo 'discos voadores' — Harold Dahl relatou observar seis grandes objetos em formato de rosca pairando sobre o Puget Sound próximo à Ilha Maury, Washington, enquanto realizava trabalhos de salvamento com seu filho e um tripulante. Segundo seu relato, um dos objetos ejetou uma chuva de escória metálica e material mais leve que danificou seu barco, feriu seu filho e matou seu cão. Fred Crisman, supervisor de Dahl, obteve amostras do material recuperado e reportou o incidente ao editor Ray Palmer, que enviou o investigador Kenneth Arnold ao local. Os oficiais de inteligência da Força Aérea do Exército, capitão William Davidson e tenente Frank Brown, voaram até Tacoma para recolher as amostras; ambos morreram em 1.º de agosto de 1947 quando seu B-25 caiu próximo a Kelso, Washington, no voo de retorno. Dahl posteriormente declarou aos investigadores que a história era uma fabricação; o FBI e a Força Aérea do Exército concluíram que o incidente foi uma farsa, e o material recuperado foi identificado como escória compatível com resíduos industriais.
|
||||
34
wiki/entities/events/EV-1947-06-21-maury-island-sighting.md
Normal file
34
wiki/entities/events/EV-1947-06-21-maury-island-sighting.md
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
schema_version: 0.1.0
|
||||
type: entity
|
||||
entity_class: event
|
||||
event_id: EV-1947-06-21-maury-island-sighting
|
||||
canonical_name: Maury Island Sighting
|
||||
aliases:
|
||||
- Maury Island Sighting
|
||||
event_class: uap-encounter
|
||||
date_start: '1947-06-21'
|
||||
date_end: '1947-06-21'
|
||||
date_confidence: low
|
||||
primary_location: null
|
||||
observers: []
|
||||
uap_objects: []
|
||||
documented_in: []
|
||||
total_mentions: 1
|
||||
documents_count: 1
|
||||
narrative_summary: null
|
||||
narrative_summary_pt_br: null
|
||||
enrichment_status: none
|
||||
external_sources: []
|
||||
last_ingest: '2026-05-15T18:57:03Z'
|
||||
last_lint: '2026-05-18T03:21:56Z'
|
||||
wiki_version: 0.1.0
|
||||
summary_status: none
|
||||
summary_confidence: null
|
||||
---
|
||||
|
||||
# Maury Island Sighting
|
||||
|
||||
## Description (EN)
|
||||
|
||||
## Descrição (PT-BR)
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
---
|
||||
schema_version: 0.1.0
|
||||
type: entity
|
||||
entity_class: event
|
||||
event_id: EV-1947-06-24-kenneth-arnold-mount-rainier
|
||||
canonical_name: Kenneth Arnold Mount Rainier Sighting
|
||||
aliases:
|
||||
- Kenneth Arnold Mount Rainier Sighting
|
||||
event_class: uap-encounter
|
||||
date_start: '1947-06-24'
|
||||
date_end: '1947-06-24'
|
||||
date_confidence: high
|
||||
primary_location: Mount Rainier, Washington, USA
|
||||
observers: []
|
||||
uap_objects: []
|
||||
documented_in: []
|
||||
total_mentions: 0
|
||||
documents_count: 0
|
||||
narrative_summary: On June 24, 1947, civilian pilot Kenneth Arnold was flying his
|
||||
CallAir A-2 near Mount Rainier, Washington, searching for a downed Marine transport
|
||||
aircraft, when he observed nine bright, crescent-shaped objects traveling in a loose
|
||||
chain formation at speeds he timed—against known landmarks across roughly 50 miles—at
|
||||
in excess of 1,200 miles per hour, far beyond any aircraft of the era. Arnold described
|
||||
their motion as like a saucer skipping across water; newspaper reporters rendered
|
||||
this as "flying saucers," inadvertently coining the term that would define the emerging
|
||||
field of UFO investigation. He reported the incident to an FBI office in Pendleton,
|
||||
Oregon, and was subsequently interviewed by Army Air Forces investigators, who were
|
||||
unable to identify the objects. The case is recognized as the catalytic event of
|
||||
the modern UFO era and established the template for both civilian and military investigation
|
||||
of aerial anomalies in the United States.
|
||||
narrative_summary_pt_br: Em 24 de junho de 1947, o piloto civil Kenneth Arnold sobrevoava
|
||||
o Monte Rainier, em Washington, à procura de uma aeronave militar desaparecida,
|
||||
quando avistou nove objetos brilhantes de formato crescente deslocando-se em formação
|
||||
encadeada. Arnold cronometrou sua passagem entre pontos de referência ao longo de
|
||||
aproximadamente 80 km e estimou a velocidade superior a 1.900 km/h — muito além
|
||||
de qualquer aeronave conhecida da época. Ele descreveu o movimento dos objetos como
|
||||
semelhante ao de um pires deslizando sobre a água; jornalistas reinterpretaram a
|
||||
expressão como "pratos voadores" (flying saucers), cunhando o termo que definiria
|
||||
o fenômeno nas décadas seguintes. Arnold relatou o avistamento ao escritório do
|
||||
FBI em Pendleton, Oregon, e foi posteriormente entrevistado por investigadores das
|
||||
Forças Aéreas do Exército, que não conseguiram identificar os objetos. O episódio
|
||||
é reconhecido como o evento catalisador da era moderna dos OVNIs e estabeleceu o
|
||||
precedente para investigações civis e militares de anomalias aéreas nos Estados
|
||||
Unidos.
|
||||
summary_status: curated
|
||||
summary_confidence: high
|
||||
enrichment_status: none
|
||||
external_sources: []
|
||||
last_ingest: '2026-05-18T03:29:21Z'
|
||||
last_lint: '2026-05-18T03:29:21Z'
|
||||
wiki_version: 0.1.0
|
||||
---
|
||||
|
||||
# Kenneth Arnold Mount Rainier Sighting
|
||||
|
||||
## Description (EN)
|
||||
|
||||
On June 24, 1947, civilian pilot Kenneth Arnold was flying his CallAir A-2 near Mount Rainier, Washington, searching for a downed Marine transport aircraft, when he observed nine bright, crescent-shaped objects traveling in a loose chain formation at speeds he timed—against known landmarks across roughly 50 miles—at in excess of 1,200 miles per hour, far beyond any aircraft of the era. Arnold described their motion as like a saucer skipping across water; newspaper reporters rendered this as "flying saucers," inadvertently coining the term that would define the emerging field of UFO investigation. He reported the incident to an FBI office in Pendleton, Oregon, and was subsequently interviewed by Army Air Forces investigators, who were unable to identify the objects. The case is recognized as the catalytic event of the modern UFO era and established the template for both civilian and military investigation of aerial anomalies in the United States.
|
||||
|
||||
## Descrição (PT-BR)
|
||||
|
||||
Em 24 de junho de 1947, o piloto civil Kenneth Arnold sobrevoava o Monte Rainier, em Washington, à procura de uma aeronave militar desaparecida, quando avistou nove objetos brilhantes de formato crescente deslocando-se em formação encadeada. Arnold cronometrou sua passagem entre pontos de referência ao longo de aproximadamente 80 km e estimou a velocidade superior a 1.900 km/h — muito além de qualquer aeronave conhecida da época. Ele descreveu o movimento dos objetos como semelhante ao de um pires deslizando sobre a água; jornalistas reinterpretaram a expressão como "pratos voadores" (flying saucers), cunhando o termo que definiria o fenômeno nas décadas seguintes. Arnold relatou o avistamento ao escritório do FBI em Pendleton, Oregon, e foi posteriormente entrevistado por investigadores das Forças Aéreas do Exército, que não conseguiram identificar os objetos. O episódio é reconhecido como o evento catalisador da era moderna dos OVNIs e estabeleceu o precedente para investigações civis e militares de anomalias aéreas nos Estados Unidos.
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
---
|
||||
schema_version: 0.1.0
|
||||
type: entity
|
||||
entity_class: event
|
||||
event_id: EV-1947-06-24-kenneth-arnold-sighting
|
||||
canonical_name: Kenneth Arnold sighting
|
||||
aliases:
|
||||
- Kenneth Arnold Sighting
|
||||
- Kenneth Arnold sighting
|
||||
event_class: uap-encounter
|
||||
date_start: '1947-06-24'
|
||||
date_end: '1947-06-24'
|
||||
date_confidence: low
|
||||
primary_location: null
|
||||
observers: []
|
||||
uap_objects: []
|
||||
documented_in: []
|
||||
total_mentions: 1
|
||||
documents_count: 1
|
||||
narrative_summary: null
|
||||
narrative_summary_pt_br: null
|
||||
enrichment_status: none
|
||||
external_sources: []
|
||||
last_ingest: '2026-05-15T18:57:04Z'
|
||||
last_lint: '2026-05-18T03:21:56Z'
|
||||
wiki_version: 0.1.0
|
||||
summary_status: none
|
||||
summary_confidence: null
|
||||
---
|
||||
|
||||
# Kenneth Arnold sighting
|
||||
|
||||
## Description (EN)
|
||||
|
||||
_Low-signal entity — referenced **1 time(s)** across **1 document(s)**. No external enrichment performed (criteria: ≥3 mentions). Use the page references below for raw context._
|
||||
|
||||
## Descrição (PT-BR)
|
||||
|
||||
_Entidade de baixo sinal — referenciada **1 vez(es)** em **1 documento(s)**. Sem enriquecimento externo (critério: ≥3 menções). Use as referências de páginas abaixo para contexto bruto._
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
---
|
||||
schema_version: 0.1.0
|
||||
type: entity
|
||||
entity_class: event
|
||||
event_id: EV-1947-06-24-kenneth-arnold-ufo-sighting
|
||||
canonical_name: Kenneth Arnold UFO sighting
|
||||
aliases:
|
||||
- Kenneth Arnold UFO Sighting
|
||||
- Kenneth Arnold UFO sighting
|
||||
event_class: uap-encounter
|
||||
date_start: '1947-06-24'
|
||||
date_end: '1947-06-24'
|
||||
date_confidence: low
|
||||
primary_location: null
|
||||
observers: []
|
||||
uap_objects: []
|
||||
documented_in: []
|
||||
total_mentions: 1
|
||||
documents_count: 1
|
||||
narrative_summary: null
|
||||
narrative_summary_pt_br: null
|
||||
enrichment_status: none
|
||||
external_sources: []
|
||||
last_ingest: '2026-05-14T06:59:02Z'
|
||||
last_lint: '2026-05-18T03:21:56Z'
|
||||
wiki_version: 0.1.0
|
||||
summary_status: none
|
||||
summary_confidence: null
|
||||
---
|
||||
|
||||
# Kenneth Arnold UFO sighting
|
||||
|
||||
## Description (EN)
|
||||
|
||||
_Low-signal entity — referenced **1 time(s)** across **1 document(s)**. No external enrichment performed (criteria: ≥3 mentions). Use the page references below for raw context._
|
||||
|
||||
## Descrição (PT-BR)
|
||||
|
||||
_Entidade de baixo sinal — referenciada **1 vez(es)** em **1 documento(s)**. Sem enriquecimento externo (critério: ≥3 menções). Use as referências de páginas abaixo para contexto bruto._
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
schema_version: 0.1.0
|
||||
type: entity
|
||||
entity_class: event
|
||||
event_id: EV-1947-07-08-roswell-crash-announcement
|
||||
canonical_name: Roswell Crash Announcement
|
||||
aliases:
|
||||
- Roswell Crash Announcement
|
||||
event_class: uap-encounter
|
||||
date_start: '1947-07-08'
|
||||
date_end: '1947-07-08'
|
||||
date_confidence: low
|
||||
primary_location: null
|
||||
observers: []
|
||||
uap_objects: []
|
||||
documented_in: []
|
||||
total_mentions: 1
|
||||
documents_count: 1
|
||||
narrative_summary: null
|
||||
narrative_summary_pt_br: null
|
||||
enrichment_status: none
|
||||
external_sources: []
|
||||
last_ingest: '2026-05-15T18:57:02Z'
|
||||
last_lint: '2026-05-18T03:21:57Z'
|
||||
wiki_version: 0.1.0
|
||||
summary_status: none
|
||||
summary_confidence: null
|
||||
---
|
||||
|
||||
# Roswell Crash Announcement
|
||||
|
||||
## Description (EN)
|
||||
|
||||
## Descrição (PT-BR)
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
schema_version: 0.1.0
|
||||
type: entity
|
||||
entity_class: event
|
||||
event_id: EV-1947-07-08-roswell-flying-disc-recovery
|
||||
canonical_name: Roswell Flying Disc Recovery
|
||||
aliases:
|
||||
- Roswell Flying Disc Recovery
|
||||
event_class: uap-encounter
|
||||
date_start: '1947-07-08'
|
||||
date_end: '1947-07-08'
|
||||
date_confidence: low
|
||||
primary_location: null
|
||||
observers: []
|
||||
uap_objects: []
|
||||
documented_in: []
|
||||
total_mentions: 1
|
||||
documents_count: 1
|
||||
narrative_summary: null
|
||||
narrative_summary_pt_br: null
|
||||
enrichment_status: none
|
||||
external_sources: []
|
||||
last_ingest: '2026-05-15T18:57:02Z'
|
||||
last_lint: '2026-05-18T03:21:57Z'
|
||||
wiki_version: 0.1.0
|
||||
summary_status: none
|
||||
summary_confidence: null
|
||||
---
|
||||
|
||||
# Roswell Flying Disc Recovery
|
||||
|
||||
## Description (EN)
|
||||
|
||||
## Descrição (PT-BR)
|
||||
64
wiki/entities/events/EV-1947-07-08-roswell-incident.md
Normal file
64
wiki/entities/events/EV-1947-07-08-roswell-incident.md
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
---
|
||||
schema_version: 0.1.0
|
||||
type: entity
|
||||
entity_class: event
|
||||
event_id: EV-1947-07-08-roswell-incident
|
||||
canonical_name: Roswell Incident
|
||||
aliases:
|
||||
- Roswell Incident
|
||||
event_class: uap-encounter
|
||||
date_start: '1947-07-08'
|
||||
date_end: '1947-07-08'
|
||||
date_confidence: high
|
||||
primary_location: Roswell, New Mexico, USA
|
||||
observers: []
|
||||
uap_objects: []
|
||||
documented_in: []
|
||||
total_mentions: 0
|
||||
documents_count: 0
|
||||
narrative_summary: On 8 July 1947, the USAAF 509th Bombardment Group at Roswell Army
|
||||
Air Field issued an unprecedented press release announcing the recovery of a 'flying
|
||||
disc' from a ranch northwest of Roswell, New Mexico — and retracted it within hours,
|
||||
substituting a weather balloon explanation. The debris had been discovered days
|
||||
earlier by rancher William Brazel on the Foster Ranch near Corona; intelligence
|
||||
officer Major Jesse Marcel was dispatched to retrieve it and later described the
|
||||
material as unlike anything he had encountered in his career. The sequence of contradictory
|
||||
official statements, combined with Marcel's persistent testimony, made Roswell the
|
||||
most extensively documented UFO controversy of the twentieth century. A 1994 USAF
|
||||
report concluded the wreckage originated from Project Mogul — a classified program
|
||||
using high-altitude balloon trains to monitor Soviet nuclear tests — a finding that
|
||||
explained the initial secrecy but left unresolved disputes over material descriptions
|
||||
and the existence of alleged secondary recovery sites.
|
||||
narrative_summary_pt_br: Em 8 de julho de 1947, o 509º Grupo de Bombardeio da USAAF,
|
||||
sediado na Base Aérea de Roswell, emitiu um comunicado sem precedentes anunciando
|
||||
a recuperação de um 'disco voador' caído numa fazenda a noroeste de Roswell, Novo
|
||||
México — e o retratou horas depois, substituindo a versão oficial pela de um balão
|
||||
meteorológico. Os destroços haviam sido encontrados dias antes pelo fazendeiro William
|
||||
Brazel no Rancho Foster, próximo a Corona; o major Jesse Marcel, oficial de inteligência,
|
||||
foi enviado para recolhê-los e descreveu posteriormente o material como diferente
|
||||
de qualquer coisa que já havia visto em sua carreira. A sequência de declarações
|
||||
oficiais contraditórias, combinada com o testemunho persistente de Marcel, tornou
|
||||
Roswell a controvérsia OVNI mais amplamente documentada do século XX. Um relatório
|
||||
da USAF de 1994 concluiu que os destroços eram provenientes do Projeto Mogul — um
|
||||
programa classificado que utilizava cadeias de balões de grande altitude para monitorar
|
||||
testes nucleares soviéticos —, conclusão que explicou o sigilo inicial, mas deixou
|
||||
disputas residuais sobre as descrições do material e a existência de supostos locais
|
||||
secundários de recuperação.
|
||||
summary_status: curated
|
||||
summary_confidence: high
|
||||
enrichment_status: none
|
||||
external_sources: []
|
||||
last_ingest: '2026-05-18T03:32:11Z'
|
||||
last_lint: '2026-05-18T03:32:11Z'
|
||||
wiki_version: 0.1.0
|
||||
---
|
||||
|
||||
# Roswell Incident
|
||||
|
||||
## Description (EN)
|
||||
|
||||
On 8 July 1947, the USAAF 509th Bombardment Group at Roswell Army Air Field issued an unprecedented press release announcing the recovery of a 'flying disc' from a ranch northwest of Roswell, New Mexico — and retracted it within hours, substituting a weather balloon explanation. The debris had been discovered days earlier by rancher William Brazel on the Foster Ranch near Corona; intelligence officer Major Jesse Marcel was dispatched to retrieve it and later described the material as unlike anything he had encountered in his career. The sequence of contradictory official statements, combined with Marcel's persistent testimony, made Roswell the most extensively documented UFO controversy of the twentieth century. A 1994 USAF report concluded the wreckage originated from Project Mogul — a classified program using high-altitude balloon trains to monitor Soviet nuclear tests — a finding that explained the initial secrecy but left unresolved disputes over material descriptions and the existence of alleged secondary recovery sites.
|
||||
|
||||
## Descrição (PT-BR)
|
||||
|
||||
Em 8 de julho de 1947, o 509º Grupo de Bombardeio da USAAF, sediado na Base Aérea de Roswell, emitiu um comunicado sem precedentes anunciando a recuperação de um 'disco voador' caído numa fazenda a noroeste de Roswell, Novo México — e o retratou horas depois, substituindo a versão oficial pela de um balão meteorológico. Os destroços haviam sido encontrados dias antes pelo fazendeiro William Brazel no Rancho Foster, próximo a Corona; o major Jesse Marcel, oficial de inteligência, foi enviado para recolhê-los e descreveu posteriormente o material como diferente de qualquer coisa que já havia visto em sua carreira. A sequência de declarações oficiais contraditórias, combinada com o testemunho persistente de Marcel, tornou Roswell a controvérsia OVNI mais amplamente documentada do século XX. Um relatório da USAF de 1994 concluiu que os destroços eram provenientes do Projeto Mogul — um programa classificado que utilizava cadeias de balões de grande altitude para monitorar testes nucleares soviéticos —, conclusão que explicou o sigilo inicial, mas deixou disputas residuais sobre as descrições do material e a existência de supostos locais secundários de recuperação.
|
||||
61
wiki/entities/events/EV-1948-01-07-mantell-crash.md
Normal file
61
wiki/entities/events/EV-1948-01-07-mantell-crash.md
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
---
|
||||
schema_version: 0.1.0
|
||||
type: entity
|
||||
entity_class: event
|
||||
event_id: EV-1948-01-07-mantell-crash
|
||||
canonical_name: Mantell UFO Incident
|
||||
aliases:
|
||||
- Mantell UFO Incident
|
||||
event_class: uap-related-fatality
|
||||
date_start: '1948-01-07'
|
||||
date_end: '1948-01-07'
|
||||
date_confidence: high
|
||||
primary_location: Franklin, Kentucky, USA
|
||||
observers: []
|
||||
uap_objects: []
|
||||
documented_in: []
|
||||
total_mentions: 0
|
||||
documents_count: 0
|
||||
narrative_summary: On 7 January 1948, Captain Thomas F. Mantell Jr. of the Kentucky
|
||||
Air National Guard died when his P-51D Mustang disintegrated near Franklin, Kentucky,
|
||||
while in pursuit of an unidentified aerial object first reported circling above
|
||||
Godman Army Air Field. Mantell, one of four pilots vectored to investigate, climbed
|
||||
beyond 25,000 feet without supplemental oxygen, radioing that the object appeared
|
||||
'metallic' and 'of tremendous size' before all contact ceased. He lost consciousness
|
||||
from hypoxia; his aircraft broke apart and wreckage fell near Franklin at approximately
|
||||
15:18 local time. Project Sign, the Air Force's first formal UAP investigation body,
|
||||
reviewed the case—its early attribution to the planet Venus was later superseded
|
||||
by evidence pointing to a classified Navy Skyhook high-altitude research balloon.
|
||||
The incident stands as the first documented fatality of a military aviator in pursuit
|
||||
of an unidentified aerial phenomenon.
|
||||
narrative_summary_pt_br: Em 7 de janeiro de 1948, o Capitão Thomas F. Mantell Jr.,
|
||||
da Guarda Aérea Nacional de Kentucky, morreu quando seu P-51D Mustang se desintegrou
|
||||
próximo a Franklin, Kentucky, enquanto perseguia um objeto aéreo não identificado
|
||||
avistado sobre a Base do Exército de Godman. Mantell, um dos quatro pilotos enviados
|
||||
para investigar, ascendeu além de 7.600 metros sem oxigênio suplementar, transmitindo
|
||||
pelo rádio que o objeto parecia 'metálico' e 'de tamanho enorme' antes de todo contato
|
||||
ser interrompido. Ele perdeu a consciência por hipóxia; sua aeronave se fragmentou
|
||||
e os destroços caíram próximos a Franklin por volta das 15h18, horário local. O
|
||||
Project Sign, primeiro órgão formal da Força Aérea para investigação de OVNIs, analisou
|
||||
o caso — a atribuição inicial ao planeta Vênus foi posteriormente substituída por
|
||||
evidências apontando para um balão estratosférico secreto da Marinha (Skyhook).
|
||||
O incidente é registrado como a primeira morte documentada de um aviador militar
|
||||
em perseguição a um fenômeno aéreo não identificado.
|
||||
summary_status: curated
|
||||
summary_confidence: high
|
||||
enrichment_status: none
|
||||
external_sources: []
|
||||
last_ingest: '2026-05-18T03:32:47Z'
|
||||
last_lint: '2026-05-18T03:32:47Z'
|
||||
wiki_version: 0.1.0
|
||||
---
|
||||
|
||||
# Mantell UFO Incident
|
||||
|
||||
## Description (EN)
|
||||
|
||||
On 7 January 1948, Captain Thomas F. Mantell Jr. of the Kentucky Air National Guard died when his P-51D Mustang disintegrated near Franklin, Kentucky, while in pursuit of an unidentified aerial object first reported circling above Godman Army Air Field. Mantell, one of four pilots vectored to investigate, climbed beyond 25,000 feet without supplemental oxygen, radioing that the object appeared 'metallic' and 'of tremendous size' before all contact ceased. He lost consciousness from hypoxia; his aircraft broke apart and wreckage fell near Franklin at approximately 15:18 local time. Project Sign, the Air Force's first formal UAP investigation body, reviewed the case—its early attribution to the planet Venus was later superseded by evidence pointing to a classified Navy Skyhook high-altitude research balloon. The incident stands as the first documented fatality of a military aviator in pursuit of an unidentified aerial phenomenon.
|
||||
|
||||
## Descrição (PT-BR)
|
||||
|
||||
Em 7 de janeiro de 1948, o Capitão Thomas F. Mantell Jr., da Guarda Aérea Nacional de Kentucky, morreu quando seu P-51D Mustang se desintegrou próximo a Franklin, Kentucky, enquanto perseguia um objeto aéreo não identificado avistado sobre a Base do Exército de Godman. Mantell, um dos quatro pilotos enviados para investigar, ascendeu além de 7.600 metros sem oxigênio suplementar, transmitindo pelo rádio que o objeto parecia 'metálico' e 'de tamanho enorme' antes de todo contato ser interrompido. Ele perdeu a consciência por hipóxia; sua aeronave se fragmentou e os destroços caíram próximos a Franklin por volta das 15h18, horário local. O Project Sign, primeiro órgão formal da Força Aérea para investigação de OVNIs, analisou o caso — a atribuição inicial ao planeta Vênus foi posteriormente substituída por evidências apontando para um balão estratosférico secreto da Marinha (Skyhook). O incidente é registrado como a primeira morte documentada de um aviador militar em perseguição a um fenômeno aéreo não identificado.
|
||||
34
wiki/entities/events/EV-1948-01-07-mantell-ufo-incident.md
Normal file
34
wiki/entities/events/EV-1948-01-07-mantell-ufo-incident.md
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
schema_version: 0.1.0
|
||||
type: entity
|
||||
entity_class: event
|
||||
event_id: EV-1948-01-07-mantell-ufo-incident
|
||||
canonical_name: Mantell UFO Incident
|
||||
aliases:
|
||||
- Mantell UFO Incident
|
||||
event_class: uap-encounter
|
||||
date_start: '1948-01-07'
|
||||
date_end: '1948-01-07'
|
||||
date_confidence: low
|
||||
primary_location: null
|
||||
observers: []
|
||||
uap_objects: []
|
||||
documented_in: []
|
||||
total_mentions: 4
|
||||
documents_count: 4
|
||||
narrative_summary: null
|
||||
narrative_summary_pt_br: null
|
||||
enrichment_status: none
|
||||
external_sources: []
|
||||
last_ingest: '2026-05-15T18:57:02Z'
|
||||
last_lint: '2026-05-18T03:22:50Z'
|
||||
wiki_version: 0.1.0
|
||||
summary_status: none
|
||||
summary_confidence: null
|
||||
---
|
||||
|
||||
# Mantell UFO Incident
|
||||
|
||||
## Description (EN)
|
||||
|
||||
## Descrição (PT-BR)
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
---
|
||||
schema_version: 0.1.0
|
||||
type: entity
|
||||
entity_class: event
|
||||
event_id: EV-1948-07-24-chiles-whitted-encounter
|
||||
canonical_name: Chiles-Whitted UFO Encounter
|
||||
aliases:
|
||||
- Chiles-Whitted Encounter
|
||||
event_class: uap-encounter
|
||||
date_start: '1948-07-24'
|
||||
date_end: '1948-07-24'
|
||||
date_confidence: high
|
||||
primary_location: Montgomery, Alabama, USA
|
||||
observers: []
|
||||
uap_objects: []
|
||||
documented_in: []
|
||||
total_mentions: 1
|
||||
documents_count: 1
|
||||
narrative_summary: At approximately 02:45 on 24 July 1948, Eastern Airlines Captain
|
||||
Clarence S. Chiles and First Officer John B. Whitted observed an unidentified object
|
||||
pass within an estimated 700 feet of their DC-3 at 5,000 feet altitude near Montgomery,
|
||||
Alabama, the encounter lasting roughly ten seconds as the object closed head-on
|
||||
at high speed and veered off to the left. Both pilots independently described the
|
||||
craft as wingless and torpedo-shaped, approximately 100 feet long, with two rows
|
||||
of bright rectangular windows emitting an intense blue-white glow and a deep-blue
|
||||
exhaust flame trailing from its rear. A single passenger aboard corroborated the
|
||||
sighting, reporting a bright streak of light through the cabin window. Project Sign
|
||||
investigators at Wright-Patterson Air Force Base considered the Chiles-Whitted report
|
||||
among the strongest in their files, and an internal classified assessment — the
|
||||
'Estimate of the Situation' — reportedly cited it in support of an extraterrestrial
|
||||
hypothesis, only to be rejected by Chief of Staff General Hoyt Vandenberg; the document
|
||||
was subsequently ordered destroyed. The Air Force's final official explanation attributed
|
||||
the object to a large bolide meteor, a conclusion both pilots disputed publicly
|
||||
for the remainder of their careers.
|
||||
narrative_summary_pt_br: Por volta das 02h45 do dia 24 de julho de 1948, o capitão
|
||||
Clarence S. Chiles e o co-piloto John B. Whitted, da Eastern Airlines, avistaram
|
||||
um objeto não identificado que passou a aproximadamente 700 pés de seu DC-3 voando
|
||||
a 1.500 metros de altitude próximo a Montgomery, Alabama — o contato durou cerca
|
||||
de dez segundos enquanto o objeto se aproximava de frente em alta velocidade e desviou
|
||||
para a esquerda. Ambos os pilotos descreveram de forma independente uma nave sem
|
||||
asas, em formato de torpedo, com cerca de 30 metros de comprimento, duas fileiras
|
||||
de janelas retangulares que emitiam luz azul-branca intensa e uma chama de exaustão
|
||||
azul-profunda na cauda. Um único passageiro a bordo corroborou o avistamento, relatando
|
||||
ter visto um clarão de luz pela janela da cabine. Os investigadores do Projeto Sign
|
||||
na base de Wright-Patterson consideraram o caso um dos mais sólidos de seus arquivos;
|
||||
uma avaliação interna classificada — o 'Estimate of the Situation' — teria citado
|
||||
o episódio em apoio à hipótese de origem extraterrestre, mas foi rejeitada pelo
|
||||
General Hoyt Vandenberg, chefe do Estado-Maior, e o documento foi subsequentemente
|
||||
mandado destruir. A conclusão oficial da Força Aérea atribuiu o objeto a um grande
|
||||
bólido meteórico, diagnóstico que ambos os pilotos contestaram publicamente pelo
|
||||
resto de suas carreiras.
|
||||
summary_status: curated
|
||||
summary_confidence: high
|
||||
enrichment_status: none
|
||||
external_sources: []
|
||||
last_ingest: '2026-05-15T18:57:03Z'
|
||||
last_lint: '2026-05-18T03:33:26Z'
|
||||
wiki_version: 0.1.0
|
||||
---
|
||||
|
||||
# Chiles-Whitted Encounter
|
||||
|
||||
## Description (EN)
|
||||
|
||||
## Descrição (PT-BR)
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
schema_version: 0.1.0
|
||||
type: entity
|
||||
entity_class: event
|
||||
event_id: EV-1948-07-24-chiles-whitted-ufo-encounter
|
||||
canonical_name: Chiles-Whitted UFO Encounter
|
||||
aliases:
|
||||
- Chiles-Whitted UFO Encounter
|
||||
event_class: uap-encounter
|
||||
date_start: '1948-07-24'
|
||||
date_end: '1948-07-24'
|
||||
date_confidence: low
|
||||
primary_location: null
|
||||
observers: []
|
||||
uap_objects: []
|
||||
documented_in: []
|
||||
total_mentions: 1
|
||||
documents_count: 1
|
||||
narrative_summary: null
|
||||
narrative_summary_pt_br: null
|
||||
enrichment_status: none
|
||||
external_sources: []
|
||||
last_ingest: '2026-05-15T18:57:02Z'
|
||||
last_lint: '2026-05-18T03:22:51Z'
|
||||
wiki_version: 0.1.0
|
||||
summary_status: none
|
||||
summary_confidence: null
|
||||
---
|
||||
|
||||
# Chiles-Whitted UFO Encounter
|
||||
|
||||
## Description (EN)
|
||||
|
||||
## Descrição (PT-BR)
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
---
|
||||
schema_version: 0.1.0
|
||||
type: entity
|
||||
entity_class: event
|
||||
event_id: EV-1952-09-XX-operation-mainbrace-sightings
|
||||
canonical_name: Operation Mainbrace UAP Sightings
|
||||
aliases:
|
||||
- Operation Mainbrace UAP Sightings
|
||||
event_class: uap-encounter
|
||||
date_start: 1952-09
|
||||
date_end: 1952-09
|
||||
date_confidence: high
|
||||
primary_location: North Atlantic / Scandinavia
|
||||
observers: []
|
||||
uap_objects: []
|
||||
documented_in: []
|
||||
total_mentions: 0
|
||||
documents_count: 0
|
||||
narrative_summary: During NATO Exercise Mainbrace in September 1952—the alliance's
|
||||
largest naval exercise to date, spanning the North Atlantic from the Norwegian coast
|
||||
to Scotland—personnel from multiple nations reported UAP encounters on at least
|
||||
six separate occasions. Observers aboard the USS Franklin D. Roosevelt aircraft
|
||||
carrier sighted a silver sphere on 13 September; four days later, the crew of the
|
||||
Danish destroyer Willemoes tracked a triangular object estimated to be traveling
|
||||
at 900 mph. On 19 September, RAF Flight Lieutenant John Kilburn and four other aircrew
|
||||
at Topcliffe airfield watched a silver disc descend, halt, and then accelerate sharply—behavior
|
||||
irreconcilable with any known aircraft of the period. A photograph attributed to
|
||||
Navy journalist Wallace Litwin, taken from the Roosevelt, circulated in official
|
||||
channels and was later examined by the Condon Committee without a definitive identification.
|
||||
The density of credible, multi-witness, multi-nation reports during a single two-week
|
||||
exercise made Mainbrace one of the most thoroughly documented early Cold War UAP
|
||||
clusters and was cited in subsequent official inquiries by both the U.S. and British
|
||||
governments.
|
||||
narrative_summary_pt_br: Durante o Exercício Mainbrace da OTAN em setembro de 1952
|
||||
— a maior manobra naval da aliança até então, realizada no Atlântico Norte da costa
|
||||
norueguesa até a Escócia — pessoal de múltiplas nações relatou encontros com UAP
|
||||
em pelo menos seis ocasiões distintas. Observadores a bordo do porta-aviões USS
|
||||
Franklin D. Roosevelt avistaram uma esfera prateada em 13 de setembro; quatro dias
|
||||
depois, a tripulação do contratorpedeiro dinamarquês Willemoes rastreou um objeto
|
||||
triangular estimado em 900 milhas por hora. Em 19 de setembro, o tenente John Kilburn
|
||||
da RAF e outros quatro membros de tripulação na base de Topcliffe assistiram a um
|
||||
disco prateado descer, parar e então acelerar bruscamente — comportamento incompatível
|
||||
com qualquer aeronave conhecida da época. Uma fotografia atribuída ao jornalista
|
||||
naval Wallace Litwin, tirada a bordo do Roosevelt, circulou em canais oficiais e
|
||||
foi posteriormente examinada pelo Comitê Condon sem identificação conclusiva. A
|
||||
concentração de relatos críveis, com múltiplas testemunhas de múltiplas nações durante
|
||||
um único exercício de duas semanas, fez de Mainbrace um dos agrupamentos de UAP
|
||||
da Guerra Fria inicial mais documentados, sendo citado em investigações oficiais
|
||||
posteriores pelos governos americano e britânico.
|
||||
summary_status: curated
|
||||
summary_confidence: high
|
||||
enrichment_status: none
|
||||
external_sources: []
|
||||
last_ingest: '2026-05-18T03:34:10Z'
|
||||
last_lint: '2026-05-18T03:34:10Z'
|
||||
wiki_version: 0.1.0
|
||||
---
|
||||
|
||||
# Operation Mainbrace UAP Sightings
|
||||
|
||||
## Description (EN)
|
||||
|
||||
During NATO Exercise Mainbrace in September 1952—the alliance's largest naval exercise to date, spanning the North Atlantic from the Norwegian coast to Scotland—personnel from multiple nations reported UAP encounters on at least six separate occasions. Observers aboard the USS Franklin D. Roosevelt aircraft carrier sighted a silver sphere on 13 September; four days later, the crew of the Danish destroyer Willemoes tracked a triangular object estimated to be traveling at 900 mph. On 19 September, RAF Flight Lieutenant John Kilburn and four other aircrew at Topcliffe airfield watched a silver disc descend, halt, and then accelerate sharply—behavior irreconcilable with any known aircraft of the period. A photograph attributed to Navy journalist Wallace Litwin, taken from the Roosevelt, circulated in official channels and was later examined by the Condon Committee without a definitive identification. The density of credible, multi-witness, multi-nation reports during a single two-week exercise made Mainbrace one of the most thoroughly documented early Cold War UAP clusters and was cited in subsequent official inquiries by both the U.S. and British governments.
|
||||
|
||||
## Descrição (PT-BR)
|
||||
|
||||
Durante o Exercício Mainbrace da OTAN em setembro de 1952 — a maior manobra naval da aliança até então, realizada no Atlântico Norte da costa norueguesa até a Escócia — pessoal de múltiplas nações relatou encontros com UAP em pelo menos seis ocasiões distintas. Observadores a bordo do porta-aviões USS Franklin D. Roosevelt avistaram uma esfera prateada em 13 de setembro; quatro dias depois, a tripulação do contratorpedeiro dinamarquês Willemoes rastreou um objeto triangular estimado em 900 milhas por hora. Em 19 de setembro, o tenente John Kilburn da RAF e outros quatro membros de tripulação na base de Topcliffe assistiram a um disco prateado descer, parar e então acelerar bruscamente — comportamento incompatível com qualquer aeronave conhecida da época. Uma fotografia atribuída ao jornalista naval Wallace Litwin, tirada a bordo do Roosevelt, circulou em canais oficiais e foi posteriormente examinada pelo Comitê Condon sem identificação conclusiva. A concentração de relatos críveis, com múltiplas testemunhas de múltiplas nações durante um único exercício de duas semanas, fez de Mainbrace um dos agrupamentos de UAP da Guerra Fria inicial mais documentados, sendo citado em investigações oficiais posteriores pelos governos americano e britânico.
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
---
|
||||
schema_version: 0.1.0
|
||||
type: entity
|
||||
entity_class: event
|
||||
event_id: EV-1959-06-26-father-gill-papua-encounter
|
||||
canonical_name: Father Gill Papua Encounter
|
||||
aliases:
|
||||
- Father Gill Papua Encounter
|
||||
event_class: uap-encounter
|
||||
date_start: '1959-06-26'
|
||||
date_end: '1959-06-26'
|
||||
date_confidence: high
|
||||
primary_location: Boianai, Papua New Guinea
|
||||
observers: []
|
||||
uap_objects: []
|
||||
documented_in: []
|
||||
total_mentions: 0
|
||||
documents_count: 0
|
||||
narrative_summary: On the evening of 26 June 1959, Anglican missionary Reverend William
|
||||
Gill and 37 mission staff and local residents at Boianai, Papua New Guinea, observed
|
||||
a large luminous disc-shaped object descend to low altitude above the mission station
|
||||
over several hours. Human-like figures were visible on the upper surface of the
|
||||
craft and appeared to be performing tasks; Reverend Gill waved toward them and at
|
||||
least one figure waved back — an exchange subsequently repeated by other witnesses.
|
||||
The object returned the following evening of 27 June, with the waving exchange again
|
||||
occurring. All 38 witnesses signed a formal affidavit documenting both sightings,
|
||||
producing one of the most extensively attested close-encounter records of the era.
|
||||
The Royal Australian Air Force attributed the observations to misidentified stars
|
||||
and planets; astronomer and investigator J. Allen Hynek later interviewed Gill,
|
||||
found him credible, and considered the astronomical explanation inadequate given
|
||||
the witnesses' familiarity with the night sky.
|
||||
narrative_summary_pt_br: Na noite de 26 de junho de 1959, o reverendo anglicano William
|
||||
Gill e 37 membros da equipe missionária e moradores locais em Boianai, Papua Nova
|
||||
Guiné, observaram um grande objeto luminoso em forma de disco descer a baixa altitude
|
||||
sobre a estação missionária ao longo de várias horas. Figuras humanoides eram visíveis
|
||||
na superfície superior do objeto e pareciam realizar tarefas; o Reverendo Gill acenou
|
||||
em direção a elas e pelo menos uma correspondeu ao gesto — intercâmbio que se repetiu
|
||||
entre outras testemunhas. O objeto retornou na noite seguinte, 27 de junho, com
|
||||
a troca de acenos ocorrendo novamente. Todas as 38 testemunhas assinaram uma declaração
|
||||
formal documentando os dois avistamentos, produzindo um dos registros de encontro
|
||||
aproximado mais amplamente atestados da época. A Real Força Aérea Australiana atribuiu
|
||||
as observações a estrelas e planetas identificados erroneamente; o astrônomo e investigador
|
||||
J. Allen Hynek entrevistou Gill posteriormente, considerou-o uma testemunha confiável
|
||||
e avaliou a explicação astronômica como insuficiente dada a familiaridade das testemunhas
|
||||
com o céu noturno.
|
||||
summary_status: curated
|
||||
summary_confidence: high
|
||||
enrichment_status: none
|
||||
external_sources: []
|
||||
last_ingest: '2026-05-18T03:34:41Z'
|
||||
last_lint: '2026-05-18T03:34:41Z'
|
||||
wiki_version: 0.1.0
|
||||
---
|
||||
|
||||
# Father Gill Papua Encounter
|
||||
|
||||
## Description (EN)
|
||||
|
||||
On the evening of 26 June 1959, Anglican missionary Reverend William Gill and 37 mission staff and local residents at Boianai, Papua New Guinea, observed a large luminous disc-shaped object descend to low altitude above the mission station over several hours. Human-like figures were visible on the upper surface of the craft and appeared to be performing tasks; Reverend Gill waved toward them and at least one figure waved back — an exchange subsequently repeated by other witnesses. The object returned the following evening of 27 June, with the waving exchange again occurring. All 38 witnesses signed a formal affidavit documenting both sightings, producing one of the most extensively attested close-encounter records of the era. The Royal Australian Air Force attributed the observations to misidentified stars and planets; astronomer and investigator J. Allen Hynek later interviewed Gill, found him credible, and considered the astronomical explanation inadequate given the witnesses' familiarity with the night sky.
|
||||
|
||||
## Descrição (PT-BR)
|
||||
|
||||
Na noite de 26 de junho de 1959, o reverendo anglicano William Gill e 37 membros da equipe missionária e moradores locais em Boianai, Papua Nova Guiné, observaram um grande objeto luminoso em forma de disco descer a baixa altitude sobre a estação missionária ao longo de várias horas. Figuras humanoides eram visíveis na superfície superior do objeto e pareciam realizar tarefas; o Reverendo Gill acenou em direção a elas e pelo menos uma correspondeu ao gesto — intercâmbio que se repetiu entre outras testemunhas. O objeto retornou na noite seguinte, 27 de junho, com a troca de acenos ocorrendo novamente. Todas as 38 testemunhas assinaram uma declaração formal documentando os dois avistamentos, produzindo um dos registros de encontro aproximado mais amplamente atestados da época. A Real Força Aérea Australiana atribuiu as observações a estrelas e planetas identificados erroneamente; o astrônomo e investigador J. Allen Hynek entrevistou Gill posteriormente, considerou-o uma testemunha confiável e avaliou a explicação astronômica como insuficiente dada a familiaridade das testemunhas com o céu noturno.
|
||||
69
wiki/entities/events/EV-1964-04-24-lonnie-zamora-socorro.md
Normal file
69
wiki/entities/events/EV-1964-04-24-lonnie-zamora-socorro.md
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
---
|
||||
schema_version: 0.1.0
|
||||
type: entity
|
||||
entity_class: event
|
||||
event_id: EV-1964-04-24-lonnie-zamora-socorro
|
||||
canonical_name: Lonnie Zamora Socorro Landing
|
||||
aliases:
|
||||
- Lonnie Zamora Socorro Landing
|
||||
event_class: uap-encounter
|
||||
date_start: '1964-04-24'
|
||||
date_end: '1964-04-24'
|
||||
date_confidence: high
|
||||
primary_location: Socorro, New Mexico, USA
|
||||
observers: []
|
||||
uap_objects: []
|
||||
documented_in: []
|
||||
total_mentions: 0
|
||||
documents_count: 0
|
||||
narrative_summary: On 24 April 1964 at approximately 17:45 local time, Socorro Police
|
||||
Sergeant Lonnie Zamora broke off pursuit of a speeding vehicle after hearing a roar
|
||||
and observing a blue-orange flame descending southwest of the highway. Approaching
|
||||
the site in his patrol car, he observed an egg-shaped, aluminum-white object resting
|
||||
on girder-like legs in an arroyo, with two small figures in white coveralls standing
|
||||
nearby; the figures retreated into the craft as Zamora drew closer. The object rose
|
||||
with a loud roar and flame, then departed silently to the southwest at low altitude,
|
||||
leaving four symmetrical ground-impression marks, scorched soil, and burned greasewood
|
||||
brush at the landing site. Project Blue Book dispatched investigator Richard Holder
|
||||
within hours; Air Force scientific consultant J. Allen Hynek visited the site days
|
||||
later and concluded the physical trace evidence was genuine and the witness credible.
|
||||
The case was formally classified 'Unidentified' by Project Blue Book — one of the
|
||||
few to retain that designation — and remained uninvestigated as to a conventional
|
||||
explanation. Zamora's account has never been credibly impeached, and the Socorro
|
||||
landing is considered among the best-documented close-encounter cases in the historical
|
||||
record.
|
||||
narrative_summary_pt_br: Em 24 de abril de 1964, por volta das 17h45 no horário local,
|
||||
o sargento de polícia Lonnie Zamora interrompeu a perseguição a um veículo na rodovia
|
||||
de Socorro, Novo México, após ouvir um rugido e avistar uma chama azul-alaranjada
|
||||
descendo a sudoeste. Aproximando-se do local em sua viatura, ele observou um objeto
|
||||
de formato oval, de coloração alumínio, pousado sobre pernas estruturais em um arroyo,
|
||||
com duas figuras de pequena estatura vestindo macacões brancos nas proximidades;
|
||||
ao perceber a aproximação de Zamora, as figuras retornaram ao interior do objeto.
|
||||
A aeronave decolou com forte rugido e chama, partindo silenciosamente em baixa altitude
|
||||
para sudoeste, deixando quatro marcas simétricas no solo, vegetação chamuscada e
|
||||
arbustos queimados no ponto de pouso. O Projeto Blue Book enviou investigadores
|
||||
ao local em poucas horas; o consultor científico da Força Aérea J. Allen Hynek visitou
|
||||
o sítio dias depois e concluiu que as evidências físicas eram autênticas e o depoimento
|
||||
do policial confiável. O caso foi formalmente classificado como 'Não Identificado'
|
||||
pelo Projeto Blue Book — uma das poucas ocorrências a manter essa designação — sem
|
||||
que qualquer explicação convencional satisfatória fosse encontrada. O relato de
|
||||
Zamora jamais foi desacreditado de forma convincente, e o pouso de Socorro é considerado
|
||||
um dos incidentes de contato próximo mais bem documentados do registro histórico.
|
||||
summary_status: curated
|
||||
summary_confidence: high
|
||||
enrichment_status: none
|
||||
external_sources: []
|
||||
last_ingest: '2026-05-18T03:35:12Z'
|
||||
last_lint: '2026-05-18T03:35:12Z'
|
||||
wiki_version: 0.1.0
|
||||
---
|
||||
|
||||
# Lonnie Zamora Socorro Landing
|
||||
|
||||
## Description (EN)
|
||||
|
||||
On 24 April 1964 at approximately 17:45 local time, Socorro Police Sergeant Lonnie Zamora broke off pursuit of a speeding vehicle after hearing a roar and observing a blue-orange flame descending southwest of the highway. Approaching the site in his patrol car, he observed an egg-shaped, aluminum-white object resting on girder-like legs in an arroyo, with two small figures in white coveralls standing nearby; the figures retreated into the craft as Zamora drew closer. The object rose with a loud roar and flame, then departed silently to the southwest at low altitude, leaving four symmetrical ground-impression marks, scorched soil, and burned greasewood brush at the landing site. Project Blue Book dispatched investigator Richard Holder within hours; Air Force scientific consultant J. Allen Hynek visited the site days later and concluded the physical trace evidence was genuine and the witness credible. The case was formally classified 'Unidentified' by Project Blue Book — one of the few to retain that designation — and remained uninvestigated as to a conventional explanation. Zamora's account has never been credibly impeached, and the Socorro landing is considered among the best-documented close-encounter cases in the historical record.
|
||||
|
||||
## Descrição (PT-BR)
|
||||
|
||||
Em 24 de abril de 1964, por volta das 17h45 no horário local, o sargento de polícia Lonnie Zamora interrompeu a perseguição a um veículo na rodovia de Socorro, Novo México, após ouvir um rugido e avistar uma chama azul-alaranjada descendo a sudoeste. Aproximando-se do local em sua viatura, ele observou um objeto de formato oval, de coloração alumínio, pousado sobre pernas estruturais em um arroyo, com duas figuras de pequena estatura vestindo macacões brancos nas proximidades; ao perceber a aproximação de Zamora, as figuras retornaram ao interior do objeto. A aeronave decolou com forte rugido e chama, partindo silenciosamente em baixa altitude para sudoeste, deixando quatro marcas simétricas no solo, vegetação chamuscada e arbustos queimados no ponto de pouso. O Projeto Blue Book enviou investigadores ao local em poucas horas; o consultor científico da Força Aérea J. Allen Hynek visitou o sítio dias depois e concluiu que as evidências físicas eram autênticas e o depoimento do policial confiável. O caso foi formalmente classificado como 'Não Identificado' pelo Projeto Blue Book — uma das poucas ocorrências a manter essa designação — sem que qualquer explicação convencional satisfatória fosse encontrada. O relato de Zamora jamais foi desacreditado de forma convincente, e o pouso de Socorro é considerado um dos incidentes de contato próximo mais bem documentados do registro histórico.
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
---
|
||||
schema_version: 0.1.0
|
||||
type: entity
|
||||
entity_class: event
|
||||
event_id: EV-1966-04-06-westall-school-encounter
|
||||
canonical_name: Westall School Encounter
|
||||
aliases:
|
||||
- Westall School Encounter
|
||||
event_class: uap-encounter
|
||||
date_start: '1966-04-06'
|
||||
date_end: '1966-04-06'
|
||||
date_confidence: high
|
||||
primary_location: Clayton South, Victoria, Australia
|
||||
observers: []
|
||||
uap_objects: []
|
||||
documented_in: []
|
||||
total_mentions: 0
|
||||
documents_count: 0
|
||||
narrative_summary: On the morning of 6 April 1966, more than 200 students and staff
|
||||
at Westall High School in Clayton South, Victoria, Australia, witnessed an unidentified
|
||||
disc-shaped object descend into the adjacent Grange Reserve, remain briefly on the
|
||||
ground, then rise and depart at speed. The object was described as silver-grey and
|
||||
approximately the size of two family cars, and several students who ran to the landing
|
||||
site found a circular depression of flattened grass consistent with a physical contact
|
||||
event. A number of small aircraft were observed circling the area at the time, though
|
||||
their origin and purpose were never officially established. The Royal Australian
|
||||
Air Force opened an investigation but released no public findings, and multiple
|
||||
witnesses subsequently reported being instructed by school officials and unidentified
|
||||
men in plain clothes to remain silent about the incident. The combination of physical
|
||||
trace evidence, the exceptional witness count across two adjacent schools, and the
|
||||
documented suppression pressure place Westall among the most evidentially substantive
|
||||
daylight UAP encounters on record in the Southern Hemisphere.
|
||||
narrative_summary_pt_br: Na manhã de 6 de abril de 1966, mais de 200 alunos e professores
|
||||
do Westall High School, em Clayton South, Victoria, Austrália, observaram um objeto
|
||||
discóide não identificado descer para a Reserva The Grange, adjacente ao campus,
|
||||
permanecer brevemente no solo e então elevar-se e partir em alta velocidade. O objeto
|
||||
foi descrito como prateado-acinzentado, com tamanho estimado equivalente a dois
|
||||
automóveis familiares, e diversos alunos que correram ao local do pouso encontraram
|
||||
uma depressão circular de grama achatada, consistente com contato físico direto
|
||||
com o solo. Várias aeronaves leves foram vistas circulando a área durante o incidente,
|
||||
embora sua origem e propósito jamais tenham sido oficialmente esclarecidos. A Real
|
||||
Força Aérea Australiana (RAAF) abriu uma investigação, mas não divulgou quaisquer
|
||||
conclusões públicas, e múltiplas testemunhas relataram posteriormente ter sido instruídas
|
||||
por funcionários da escola e por homens não identificados em roupas civis a permanecer
|
||||
em silêncio sobre o ocorrido. A conjugação de evidência física, a quantidade excepcional
|
||||
de testemunhas em duas escolas adjacentes e a pressão documentada para supressão
|
||||
dos relatos posicionam Westall entre os encontros UAP diurnos mais substantivamente
|
||||
evidenciados já registrados no Hemisfério Sul.
|
||||
summary_status: curated
|
||||
summary_confidence: high
|
||||
enrichment_status: none
|
||||
external_sources: []
|
||||
last_ingest: '2026-05-18T03:35:50Z'
|
||||
last_lint: '2026-05-18T03:35:50Z'
|
||||
wiki_version: 0.1.0
|
||||
---
|
||||
|
||||
# Westall School Encounter
|
||||
|
||||
## Description (EN)
|
||||
|
||||
On the morning of 6 April 1966, more than 200 students and staff at Westall High School in Clayton South, Victoria, Australia, witnessed an unidentified disc-shaped object descend into the adjacent Grange Reserve, remain briefly on the ground, then rise and depart at speed. The object was described as silver-grey and approximately the size of two family cars, and several students who ran to the landing site found a circular depression of flattened grass consistent with a physical contact event. A number of small aircraft were observed circling the area at the time, though their origin and purpose were never officially established. The Royal Australian Air Force opened an investigation but released no public findings, and multiple witnesses subsequently reported being instructed by school officials and unidentified men in plain clothes to remain silent about the incident. The combination of physical trace evidence, the exceptional witness count across two adjacent schools, and the documented suppression pressure place Westall among the most evidentially substantive daylight UAP encounters on record in the Southern Hemisphere.
|
||||
|
||||
## Descrição (PT-BR)
|
||||
|
||||
Na manhã de 6 de abril de 1966, mais de 200 alunos e professores do Westall High School, em Clayton South, Victoria, Austrália, observaram um objeto discóide não identificado descer para a Reserva The Grange, adjacente ao campus, permanecer brevemente no solo e então elevar-se e partir em alta velocidade. O objeto foi descrito como prateado-acinzentado, com tamanho estimado equivalente a dois automóveis familiares, e diversos alunos que correram ao local do pouso encontraram uma depressão circular de grama achatada, consistente com contato físico direto com o solo. Várias aeronaves leves foram vistas circulando a área durante o incidente, embora sua origem e propósito jamais tenham sido oficialmente esclarecidos. A Real Força Aérea Australiana (RAAF) abriu uma investigação, mas não divulgou quaisquer conclusões públicas, e múltiplas testemunhas relataram posteriormente ter sido instruídas por funcionários da escola e por homens não identificados em roupas civis a permanecer em silêncio sobre o ocorrido. A conjugação de evidência física, a quantidade excepcional de testemunhas em duas escolas adjacentes e a pressão documentada para supressão dos relatos posicionam Westall entre os encontros UAP diurnos mais substantivamente evidenciados já registrados no Hemisfério Sul.
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
---
|
||||
schema_version: 0.1.0
|
||||
type: entity
|
||||
entity_class: event
|
||||
event_id: EV-1975-11-05-travis-walton-abduction
|
||||
canonical_name: Travis Walton Abduction
|
||||
aliases:
|
||||
- Travis Walton Abduction
|
||||
event_class: uap-abduction-claim
|
||||
date_start: '1975-11-05'
|
||||
date_end: '1975-11-05'
|
||||
date_confidence: high
|
||||
primary_location: Apache–Sitgreaves National Forest, Arizona, USA
|
||||
observers: []
|
||||
uap_objects: []
|
||||
documented_in: []
|
||||
total_mentions: 0
|
||||
documents_count: 0
|
||||
narrative_summary: On the evening of November 5, 1975, Travis Walton and a logging
|
||||
crew of six — employed by contractor Mike Rogers — were driving out of the Apache–Sitgreaves
|
||||
National Forest near Heber, Arizona, when they observed a luminous disc-shaped object
|
||||
hovering above the tree line. Walton exited the truck and approached the object;
|
||||
a beam of light struck him and hurled him backward, after which the crew fled the
|
||||
scene, leaving him behind. His disappearance was reported to the Navajo County Sheriff's
|
||||
Department that same night, and extensive searches found no trace of him for five
|
||||
days. On November 10, Walton reappeared near Heber and gave an account of being
|
||||
aboard a craft attended by small humanoid figures and a group of human-appearing
|
||||
beings. Five of the six crew members subsequently passed a polygraph examination
|
||||
administered by Cy Gilson of the Arizona Department of Public Safety; the Aerial
|
||||
Phenomena Research Organization (APRO) conducted the primary civilian investigation.
|
||||
No U.S. government agency formally investigated the incident, and the case remains
|
||||
one of the most extensively documented and disputed alleged abduction reports in
|
||||
the UFO literature.
|
||||
narrative_summary_pt_br: Na noite de 5 de novembro de 1975, Travis Walton e uma equipe
|
||||
de seis lenhadores contratados por Mike Rogers deixavam a Floresta Nacional Apache–Sitgreaves,
|
||||
próximo a Heber, Arizona, quando avistaram um objeto discóide luminoso pairando
|
||||
sobre as copas das árvores. Walton desceu do caminhão e se aproximou do objeto;
|
||||
um feixe de luz o atingiu e o arremessou para trás, momento em que a equipe fugiu
|
||||
do local, deixando-o para trás. O desaparecimento foi notificado ao Departamento
|
||||
do Xerife do Condado Navajo ainda naquela noite, e buscas intensivas não localizaram
|
||||
nenhum rastro seu por cinco dias. Em 10 de novembro, Walton reapareceu próximo a
|
||||
Heber e relatou ter estado a bordo de uma nave na presença de figuras humanoides
|
||||
de pequeno porte e de seres com aparência humana. Cinco dos seis membros da equipe
|
||||
foram submetidos a exame de polígrafo aplicado por Cy Gilson, do Departamento de
|
||||
Segurança Pública do Arizona, e foram considerados não-deceptivos; a Aerial Phenomena
|
||||
Research Organization (APRO) conduziu a principal investigação civil do caso. Nenhuma
|
||||
agência do governo norte-americano investigou formalmente o incidente, e o caso
|
||||
permanece um dos relatos de abdução mais documentados e contestados da literatura
|
||||
ufológica.
|
||||
summary_status: curated
|
||||
summary_confidence: high
|
||||
enrichment_status: none
|
||||
external_sources: []
|
||||
last_ingest: '2026-05-18T03:36:22Z'
|
||||
last_lint: '2026-05-18T03:36:22Z'
|
||||
wiki_version: 0.1.0
|
||||
---
|
||||
|
||||
# Travis Walton Abduction
|
||||
|
||||
## Description (EN)
|
||||
|
||||
On the evening of November 5, 1975, Travis Walton and a logging crew of six — employed by contractor Mike Rogers — were driving out of the Apache–Sitgreaves National Forest near Heber, Arizona, when they observed a luminous disc-shaped object hovering above the tree line. Walton exited the truck and approached the object; a beam of light struck him and hurled him backward, after which the crew fled the scene, leaving him behind. His disappearance was reported to the Navajo County Sheriff's Department that same night, and extensive searches found no trace of him for five days. On November 10, Walton reappeared near Heber and gave an account of being aboard a craft attended by small humanoid figures and a group of human-appearing beings. Five of the six crew members subsequently passed a polygraph examination administered by Cy Gilson of the Arizona Department of Public Safety; the Aerial Phenomena Research Organization (APRO) conducted the primary civilian investigation. No U.S. government agency formally investigated the incident, and the case remains one of the most extensively documented and disputed alleged abduction reports in the UFO literature.
|
||||
|
||||
## Descrição (PT-BR)
|
||||
|
||||
Na noite de 5 de novembro de 1975, Travis Walton e uma equipe de seis lenhadores contratados por Mike Rogers deixavam a Floresta Nacional Apache–Sitgreaves, próximo a Heber, Arizona, quando avistaram um objeto discóide luminoso pairando sobre as copas das árvores. Walton desceu do caminhão e se aproximou do objeto; um feixe de luz o atingiu e o arremessou para trás, momento em que a equipe fugiu do local, deixando-o para trás. O desaparecimento foi notificado ao Departamento do Xerife do Condado Navajo ainda naquela noite, e buscas intensivas não localizaram nenhum rastro seu por cinco dias. Em 10 de novembro, Walton reapareceu próximo a Heber e relatou ter estado a bordo de uma nave na presença de figuras humanoides de pequeno porte e de seres com aparência humana. Cinco dos seis membros da equipe foram submetidos a exame de polígrafo aplicado por Cy Gilson, do Departamento de Segurança Pública do Arizona, e foram considerados não-deceptivos; a Aerial Phenomena Research Organization (APRO) conduziu a principal investigação civil do caso. Nenhuma agência do governo norte-americano investigou formalmente o incidente, e o caso permanece um dos relatos de abdução mais documentados e contestados da literatura ufológica.
|
||||
65
wiki/entities/events/EV-1977-09-XX-operacao-prato.md
Normal file
65
wiki/entities/events/EV-1977-09-XX-operacao-prato.md
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
---
|
||||
schema_version: 0.1.0
|
||||
type: entity
|
||||
entity_class: event
|
||||
event_id: EV-1977-09-XX-operacao-prato
|
||||
canonical_name: Operação Prato
|
||||
aliases:
|
||||
- Operação Prato
|
||||
event_class: uap-encounter
|
||||
date_start: 1977-09
|
||||
date_end: 1977-09
|
||||
date_confidence: high
|
||||
primary_location: Ilha de Colares, Pará, Brasil
|
||||
observers: []
|
||||
uap_objects: []
|
||||
documented_in: []
|
||||
total_mentions: 0
|
||||
documents_count: 0
|
||||
narrative_summary: In September 1977, the Brazilian Air Force (FAB) launched a covert
|
||||
field investigation — designated Operação Prato — on Ilha de Colares, in Pará state,
|
||||
following widespread reports of luminous aerial objects that were physically affecting
|
||||
the local population. Under the command of Captain Uyrangê Hollanda Lima, the team
|
||||
spent roughly three months photographing and filming unidentified objects, interviewing
|
||||
hundreds of witnesses, and documenting injuries that residents attributed to tight
|
||||
beams of light directed at them from the objects — a phenomenon locals called 'chupa-chupa.'
|
||||
The collected material reportedly comprised thousands of photographic frames and
|
||||
hundreds of pages of testimony, all classified upon the operation's conclusion in
|
||||
December 1977. In 1997, shortly before his death, Hollanda gave a detailed interview
|
||||
in which he acknowledged that the phenomena his team observed defied conventional
|
||||
explanation and had left a lasting psychological impact on the investigators themselves.
|
||||
A portion of the FAB files was made available to researchers in 2004, confirming
|
||||
the operation's scope, though significant portions remain restricted.
|
||||
narrative_summary_pt_br: Em setembro de 1977, a Força Aérea Brasileira (FAB) lançou
|
||||
uma investigação de campo sigilosa — denominada Operação Prato — na Ilha de Colares,
|
||||
no Pará, em resposta a relatos generalizados de objetos aéreos luminosos que estavam
|
||||
afetando fisicamente a população local. Sob o comando do Capitão Uyrangê Hollanda
|
||||
Lima, a equipe passou cerca de três meses fotografando e filmando objetos não identificados,
|
||||
colhendo depoimentos de centenas de testemunhas e documentando ferimentos que moradores
|
||||
atribuíam a feixes de luz dirigidos pelos objetos contra pessoas — fenômeno que
|
||||
a população local chamou de 'chupa-chupa.' O material coletado teria incluído milhares
|
||||
de fotogramas e centenas de páginas de depoimentos, todos classificados ao término
|
||||
da operação, em dezembro de 1977. Em 1997, pouco antes de sua morte, Hollanda concedeu
|
||||
uma entrevista detalhada na qual reconheceu que os fenômenos observados por sua
|
||||
equipe desafiavam qualquer explicação convencional e deixaram marcas psicológicas
|
||||
duradouras nos próprios investigadores. Uma parte dos arquivos da FAB foi disponibilizada
|
||||
a pesquisadores em 2004, confirmando a dimensão da operação, embora parcelas significativas
|
||||
permaneçam restritas.
|
||||
summary_status: curated
|
||||
summary_confidence: high
|
||||
enrichment_status: none
|
||||
external_sources: []
|
||||
last_ingest: '2026-05-18T03:36:51Z'
|
||||
last_lint: '2026-05-18T03:36:51Z'
|
||||
wiki_version: 0.1.0
|
||||
---
|
||||
|
||||
# Operação Prato
|
||||
|
||||
## Description (EN)
|
||||
|
||||
In September 1977, the Brazilian Air Force (FAB) launched a covert field investigation — designated Operação Prato — on Ilha de Colares, in Pará state, following widespread reports of luminous aerial objects that were physically affecting the local population. Under the command of Captain Uyrangê Hollanda Lima, the team spent roughly three months photographing and filming unidentified objects, interviewing hundreds of witnesses, and documenting injuries that residents attributed to tight beams of light directed at them from the objects — a phenomenon locals called 'chupa-chupa.' The collected material reportedly comprised thousands of photographic frames and hundreds of pages of testimony, all classified upon the operation's conclusion in December 1977. In 1997, shortly before his death, Hollanda gave a detailed interview in which he acknowledged that the phenomena his team observed defied conventional explanation and had left a lasting psychological impact on the investigators themselves. A portion of the FAB files was made available to researchers in 2004, confirming the operation's scope, though significant portions remain restricted.
|
||||
|
||||
## Descrição (PT-BR)
|
||||
|
||||
Em setembro de 1977, a Força Aérea Brasileira (FAB) lançou uma investigação de campo sigilosa — denominada Operação Prato — na Ilha de Colares, no Pará, em resposta a relatos generalizados de objetos aéreos luminosos que estavam afetando fisicamente a população local. Sob o comando do Capitão Uyrangê Hollanda Lima, a equipe passou cerca de três meses fotografando e filmando objetos não identificados, colhendo depoimentos de centenas de testemunhas e documentando ferimentos que moradores atribuíam a feixes de luz dirigidos pelos objetos contra pessoas — fenômeno que a população local chamou de 'chupa-chupa.' O material coletado teria incluído milhares de fotogramas e centenas de páginas de depoimentos, todos classificados ao término da operação, em dezembro de 1977. Em 1997, pouco antes de sua morte, Hollanda concedeu uma entrevista detalhada na qual reconheceu que os fenômenos observados por sua equipe desafiavam qualquer explicação convencional e deixaram marcas psicológicas duradouras nos próprios investigadores. Uma parte dos arquivos da FAB foi disponibilizada a pesquisadores em 2004, confirmando a dimensão da operação, embora parcelas significativas permaneçam restritas.
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
---
|
||||
schema_version: 0.1.0
|
||||
type: entity
|
||||
entity_class: event
|
||||
event_id: EV-1980-12-27-rendlesham-forest-incident
|
||||
canonical_name: Rendlesham Forest Incident
|
||||
aliases:
|
||||
- Rendlesham Forest Incident
|
||||
event_class: uap-encounter
|
||||
date_start: '1980-12-27'
|
||||
date_end: '1980-12-27'
|
||||
date_confidence: high
|
||||
primary_location: Rendlesham Forest, Suffolk, UK (RAF Woodbridge / RAF Bentwaters)
|
||||
observers: []
|
||||
uap_objects: []
|
||||
documented_in: []
|
||||
total_mentions: 0
|
||||
documents_count: 0
|
||||
narrative_summary: On the nights of 26–27 and 27–28 December 1980, USAF security personnel
|
||||
from RAF Woodbridge encountered an unidentified luminous object in Rendlesham Forest,
|
||||
Suffolk. Sergeant Jim Penniston and Airman First Class John Burroughs were first
|
||||
to approach the craft in the early hours of 27 December, with Penniston documenting
|
||||
geometric markings on its surface in his field notebook before the object departed.
|
||||
The following night, Deputy Base Commander Lt Col Charles Halt led a second patrol
|
||||
into the forest, capturing real-time observations on a cassette recorder while his
|
||||
team measured elevated radiation readings at three triangular ground impressions
|
||||
at the reported landing site. Halt formally notified the UK Ministry of Defence
|
||||
in a memorandum dated 13 January 1981 — later declassified under FOIA — which stands
|
||||
as one of the most consequential official UAP documents produced by any NATO military
|
||||
command. The MoD reviewed the case and concluded it posed no threat to national
|
||||
security, leaving the object's origin officially unresolved.
|
||||
narrative_summary_pt_br: Nas noites de 26–27 e 27–28 de dezembro de 1980, militares
|
||||
da USAF lotados na RAF Woodbridge relataram encontros com um objeto luminoso não
|
||||
identificado na Floresta de Rendlesham, em Suffolk, Inglaterra. O Sargento Jim Penniston
|
||||
e o Soldado de Primeira Classe John Burroughs foram os primeiros a se aproximar
|
||||
do objeto nas primeiras horas do dia 27, com Penniston registrando marcações geométricas
|
||||
na superfície da aeronave em seu caderno de campo antes de o objeto se afastar.
|
||||
Na noite seguinte, o Tenente-Coronel Charles Halt, vice-comandante da base, conduziu
|
||||
uma segunda investigação na floresta, gravando observações em tempo real em uma
|
||||
fita cassete enquanto sua equipe media níveis elevados de radiação em três marcas
|
||||
triangulares no solo do local do suposto pouso. Halt notificou formalmente o Ministério
|
||||
da Defesa do Reino Unido por meio de memorando datado de 13 de janeiro de 1981 —
|
||||
posteriormente desclassificado via FOIA —, que permanece um dos documentos oficiais
|
||||
de UAP mais significativos já produzidos por um comando militar da OTAN. O Ministério
|
||||
da Defesa analisou o caso e concluiu que não havia ameaça à segurança nacional,
|
||||
deixando a origem do objeto oficialmente sem solução.
|
||||
summary_status: curated
|
||||
summary_confidence: high
|
||||
enrichment_status: none
|
||||
external_sources: []
|
||||
last_ingest: '2026-05-18T03:37:36Z'
|
||||
last_lint: '2026-05-18T03:37:36Z'
|
||||
wiki_version: 0.1.0
|
||||
---
|
||||
|
||||
# Rendlesham Forest Incident
|
||||
|
||||
## Description (EN)
|
||||
|
||||
On the nights of 26–27 and 27–28 December 1980, USAF security personnel from RAF Woodbridge encountered an unidentified luminous object in Rendlesham Forest, Suffolk. Sergeant Jim Penniston and Airman First Class John Burroughs were first to approach the craft in the early hours of 27 December, with Penniston documenting geometric markings on its surface in his field notebook before the object departed. The following night, Deputy Base Commander Lt Col Charles Halt led a second patrol into the forest, capturing real-time observations on a cassette recorder while his team measured elevated radiation readings at three triangular ground impressions at the reported landing site. Halt formally notified the UK Ministry of Defence in a memorandum dated 13 January 1981 — later declassified under FOIA — which stands as one of the most consequential official UAP documents produced by any NATO military command. The MoD reviewed the case and concluded it posed no threat to national security, leaving the object's origin officially unresolved.
|
||||
|
||||
## Descrição (PT-BR)
|
||||
|
||||
Nas noites de 26–27 e 27–28 de dezembro de 1980, militares da USAF lotados na RAF Woodbridge relataram encontros com um objeto luminoso não identificado na Floresta de Rendlesham, em Suffolk, Inglaterra. O Sargento Jim Penniston e o Soldado de Primeira Classe John Burroughs foram os primeiros a se aproximar do objeto nas primeiras horas do dia 27, com Penniston registrando marcações geométricas na superfície da aeronave em seu caderno de campo antes de o objeto se afastar. Na noite seguinte, o Tenente-Coronel Charles Halt, vice-comandante da base, conduziu uma segunda investigação na floresta, gravando observações em tempo real em uma fita cassete enquanto sua equipe media níveis elevados de radiação em três marcas triangulares no solo do local do suposto pouso. Halt notificou formalmente o Ministério da Defesa do Reino Unido por meio de memorando datado de 13 de janeiro de 1981 — posteriormente desclassificado via FOIA —, que permanece um dos documentos oficiais de UAP mais significativos já produzidos por um comando militar da OTAN. O Ministério da Defesa analisou o caso e concluiu que não havia ameaça à segurança nacional, deixando a origem do objeto oficialmente sem solução.
|
||||
68
wiki/entities/events/EV-1980-12-29-cash-landrum-incident.md
Normal file
68
wiki/entities/events/EV-1980-12-29-cash-landrum-incident.md
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
---
|
||||
schema_version: 0.1.0
|
||||
type: entity
|
||||
entity_class: event
|
||||
event_id: EV-1980-12-29-cash-landrum-incident
|
||||
canonical_name: Cash-Landrum Incident
|
||||
aliases:
|
||||
- Cash-Landrum Incident
|
||||
event_class: uap-related-injury
|
||||
date_start: '1980-12-29'
|
||||
date_end: '1980-12-29'
|
||||
date_confidence: high
|
||||
primary_location: Dayton, Texas, USA
|
||||
observers: []
|
||||
uap_objects: []
|
||||
documented_in: []
|
||||
total_mentions: 0
|
||||
documents_count: 0
|
||||
narrative_summary: 'On the evening of December 29, 1980, Betty Cash, Vickie Landrum,
|
||||
and Landrum''s seven-year-old grandson Colby were driving along FM 1485 near Dayton,
|
||||
Texas, when a large diamond-shaped object emitting jets of flame blocked the road
|
||||
ahead, its radiated heat making the car''s metal surfaces too hot to touch. As the
|
||||
object departed, approximately 23 military CH-47 Chinook helicopters appeared to
|
||||
surround and escort it—a detail noted independently by all three witnesses. In the
|
||||
days that followed, all three developed symptoms consistent with radiation exposure:
|
||||
hair loss, nausea, vomiting, and skin lesions; Betty Cash, who had stepped out of
|
||||
the vehicle and stood closest to the object, suffered the most severe injuries and
|
||||
required hospitalization. Cash and Vickie Landrum filed a $20 million civil suit
|
||||
against the U.S. government in 1981; a federal court dismissed the case in 1986
|
||||
after every contacted agency denied ownership or knowledge of the craft. The incident
|
||||
is among the few reported UAP encounters in which acute physical injury was medically
|
||||
documented, litigated in federal court, and subjected to sustained field investigation—most
|
||||
notably by NASA engineer John Schuessler.'
|
||||
narrative_summary_pt_br: 'Na noite de 29 de dezembro de 1980, Betty Cash, Vickie Landrum
|
||||
e o neto de sete anos de Vickie, Colby, trafegavam pela rodovia FM 1485, próximo
|
||||
a Dayton, Texas, quando um grande objeto em forma de diamante, lançando jatos de
|
||||
chamas, bloqueou a estrada à sua frente, irradiando calor suficiente para tornar
|
||||
as superfícies metálicas do veículo dolorosas ao toque. Enquanto o objeto se afastava,
|
||||
aproximadamente 23 helicópteros militares CH-47 Chinook pareciam cercá-lo e escoltá-lo
|
||||
— detalhe registrado de forma independente pelas três testemunhas. Nos dias seguintes,
|
||||
os três desenvolveram sintomas compatíveis com exposição à radiação: queda de cabelo,
|
||||
náuseas, vômitos e lesões cutâneas; Betty Cash, que havia saído do veículo e permanecido
|
||||
mais próxima ao objeto, sofreu as lesões mais graves e precisou ser hospitalizada.
|
||||
Cash e Vickie Landrum ajuizaram uma ação civil de 20 milhões de dólares contra o
|
||||
governo dos Estados Unidos em 1981; o processo foi arquivado por um tribunal federal
|
||||
em 1986 após nenhuma agência contactada reconhecer a propriedade ou o conhecimento
|
||||
sobre o objeto. O caso figura entre os raros relatos de encontro com um UAP em que
|
||||
danos físicos agudos foram documentados medicamente, litigados em tribunal federal
|
||||
e submetidos a investigação de campo sistemática — conduzida, em especial, pelo
|
||||
engenheiro da NASA John Schuessler.'
|
||||
summary_status: curated
|
||||
summary_confidence: high
|
||||
enrichment_status: none
|
||||
external_sources: []
|
||||
last_ingest: '2026-05-18T03:38:10Z'
|
||||
last_lint: '2026-05-18T03:38:10Z'
|
||||
wiki_version: 0.1.0
|
||||
---
|
||||
|
||||
# Cash-Landrum Incident
|
||||
|
||||
## Description (EN)
|
||||
|
||||
On the evening of December 29, 1980, Betty Cash, Vickie Landrum, and Landrum's seven-year-old grandson Colby were driving along FM 1485 near Dayton, Texas, when a large diamond-shaped object emitting jets of flame blocked the road ahead, its radiated heat making the car's metal surfaces too hot to touch. As the object departed, approximately 23 military CH-47 Chinook helicopters appeared to surround and escort it—a detail noted independently by all three witnesses. In the days that followed, all three developed symptoms consistent with radiation exposure: hair loss, nausea, vomiting, and skin lesions; Betty Cash, who had stepped out of the vehicle and stood closest to the object, suffered the most severe injuries and required hospitalization. Cash and Vickie Landrum filed a $20 million civil suit against the U.S. government in 1981; a federal court dismissed the case in 1986 after every contacted agency denied ownership or knowledge of the craft. The incident is among the few reported UAP encounters in which acute physical injury was medically documented, litigated in federal court, and subjected to sustained field investigation—most notably by NASA engineer John Schuessler.
|
||||
|
||||
## Descrição (PT-BR)
|
||||
|
||||
Na noite de 29 de dezembro de 1980, Betty Cash, Vickie Landrum e o neto de sete anos de Vickie, Colby, trafegavam pela rodovia FM 1485, próximo a Dayton, Texas, quando um grande objeto em forma de diamante, lançando jatos de chamas, bloqueou a estrada à sua frente, irradiando calor suficiente para tornar as superfícies metálicas do veículo dolorosas ao toque. Enquanto o objeto se afastava, aproximadamente 23 helicópteros militares CH-47 Chinook pareciam cercá-lo e escoltá-lo — detalhe registrado de forma independente pelas três testemunhas. Nos dias seguintes, os três desenvolveram sintomas compatíveis com exposição à radiação: queda de cabelo, náuseas, vômitos e lesões cutâneas; Betty Cash, que havia saído do veículo e permanecido mais próxima ao objeto, sofreu as lesões mais graves e precisou ser hospitalizada. Cash e Vickie Landrum ajuizaram uma ação civil de 20 milhões de dólares contra o governo dos Estados Unidos em 1981; o processo foi arquivado por um tribunal federal em 1986 após nenhuma agência contactada reconhecer a propriedade ou o conhecimento sobre o objeto. O caso figura entre os raros relatos de encontro com um UAP em que danos físicos agudos foram documentados medicamente, litigados em tribunal federal e submetidos a investigação de campo sistemática — conduzida, em especial, pelo engenheiro da NASA John Schuessler.
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
---
|
||||
schema_version: 0.1.0
|
||||
type: entity
|
||||
entity_class: event
|
||||
event_id: EV-1986-05-19-são-paulo-night-of-the-ufos
|
||||
canonical_name: São Paulo Noite Oficial dos OVNIs
|
||||
aliases:
|
||||
- São Paulo Noite Oficial dos OVNIs
|
||||
event_class: uap-encounter
|
||||
date_start: '1986-05-19'
|
||||
date_end: '1986-05-19'
|
||||
date_confidence: high
|
||||
primary_location: Costa do Brasil / São José dos Campos, SP
|
||||
observers: []
|
||||
uap_objects: []
|
||||
documented_in: []
|
||||
total_mentions: 0
|
||||
documents_count: 0
|
||||
narrative_summary: On the night of 19 May 1986, Brazilian Air Defense radar operators
|
||||
tracked up to 21 unidentified objects over southeastern Brazil, concentrated along
|
||||
the São Paulo coast and inland toward São José dos Campos. The Brazilian Air Force
|
||||
scrambled Mirage III and F-5E interceptors whose pilots reported visual contact
|
||||
with luminous objects executing abrupt directional changes at speeds the crews estimated
|
||||
between 1,000 and 15,000 km/h — beyond any known aircraft of that era. Each time
|
||||
a fighter closed to engagement range the objects vanished from radar and reappeared
|
||||
elsewhere, a pattern reported consistently across multiple crews. Brigadeiro Octavio
|
||||
Moreira Lima, then press chief of the Ministério da Aeronáutica, convened an official
|
||||
press conference on the morning of 20 May 1986 — a deliberate and publicly unprecedented
|
||||
step — confirming the objects were real, solid, and simultaneously detected on multiple
|
||||
independent radar systems. The Brazilian government has issued no subsequent explanation;
|
||||
the event stands as the most formally acknowledged UAP interception in Brazilian
|
||||
aviation history.
|
||||
narrative_summary_pt_br: Na noite de 19 de maio de 1986, operadores do radar de defesa
|
||||
aérea brasileira rastrearam até 21 objetos não identificados sobre o sudeste do
|
||||
Brasil, concentrados ao longo do litoral paulista e no interior, na direção de São
|
||||
José dos Campos. A Força Aérea Brasileira scramblou interceptores Mirage III e F-5E
|
||||
cujos pilotos relataram contato visual com objetos luminosos executando mudanças
|
||||
abruptas de direção a velocidades estimadas entre 1.000 e 15.000 km/h — além das
|
||||
capacidades de qualquer aeronave conhecida da época. Cada vez que um caça se aproximava
|
||||
ao alcance de engajamento, os objetos desapareciam do radar e reapareciam em outro
|
||||
ponto, padrão relatado de forma consistente por diversas tripulações. O Brigadeiro
|
||||
Octavio Moreira Lima, então chefe de imprensa do Ministério da Aeronáutica, convocou
|
||||
uma coletiva oficial na manhã de 20 de maio de 1986 — um passo deliberado e publicamente
|
||||
sem precedentes — confirmando que os objetos eram reais, sólidos e detectados simultaneamente
|
||||
em múltiplos sistemas de radar independentes. O governo brasileiro não emitiu nenhuma
|
||||
explicação posterior; o incidente permanece o evento de interceptação de OVNI mais
|
||||
formalmente reconhecido na história da aviação brasileira.
|
||||
summary_status: curated
|
||||
summary_confidence: high
|
||||
enrichment_status: none
|
||||
external_sources: []
|
||||
last_ingest: '2026-05-18T03:38:56Z'
|
||||
last_lint: '2026-05-18T03:38:56Z'
|
||||
wiki_version: 0.1.0
|
||||
---
|
||||
|
||||
# São Paulo Noite Oficial dos OVNIs
|
||||
|
||||
## Description (EN)
|
||||
|
||||
On the night of 19 May 1986, Brazilian Air Defense radar operators tracked up to 21 unidentified objects over southeastern Brazil, concentrated along the São Paulo coast and inland toward São José dos Campos. The Brazilian Air Force scrambled Mirage III and F-5E interceptors whose pilots reported visual contact with luminous objects executing abrupt directional changes at speeds the crews estimated between 1,000 and 15,000 km/h — beyond any known aircraft of that era. Each time a fighter closed to engagement range the objects vanished from radar and reappeared elsewhere, a pattern reported consistently across multiple crews. Brigadeiro Octavio Moreira Lima, then press chief of the Ministério da Aeronáutica, convened an official press conference on the morning of 20 May 1986 — a deliberate and publicly unprecedented step — confirming the objects were real, solid, and simultaneously detected on multiple independent radar systems. The Brazilian government has issued no subsequent explanation; the event stands as the most formally acknowledged UAP interception in Brazilian aviation history.
|
||||
|
||||
## Descrição (PT-BR)
|
||||
|
||||
Na noite de 19 de maio de 1986, operadores do radar de defesa aérea brasileira rastrearam até 21 objetos não identificados sobre o sudeste do Brasil, concentrados ao longo do litoral paulista e no interior, na direção de São José dos Campos. A Força Aérea Brasileira scramblou interceptores Mirage III e F-5E cujos pilotos relataram contato visual com objetos luminosos executando mudanças abruptas de direção a velocidades estimadas entre 1.000 e 15.000 km/h — além das capacidades de qualquer aeronave conhecida da época. Cada vez que um caça se aproximava ao alcance de engajamento, os objetos desapareciam do radar e reapareciam em outro ponto, padrão relatado de forma consistente por diversas tripulações. O Brigadeiro Octavio Moreira Lima, então chefe de imprensa do Ministério da Aeronáutica, convocou uma coletiva oficial na manhã de 20 de maio de 1986 — um passo deliberado e publicamente sem precedentes — confirmando que os objetos eram reais, sólidos e detectados simultaneamente em múltiplos sistemas de radar independentes. O governo brasileiro não emitiu nenhuma explicação posterior; o incidente permanece o evento de interceptação de OVNI mais formalmente reconhecido na história da aviação brasileira.
|
||||
72
wiki/entities/events/EV-1989-11-XX-belgian-wave.md
Normal file
72
wiki/entities/events/EV-1989-11-XX-belgian-wave.md
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
---
|
||||
schema_version: 0.1.0
|
||||
type: entity
|
||||
entity_class: event
|
||||
event_id: EV-1989-11-XX-belgian-wave
|
||||
canonical_name: Belgian UFO Wave
|
||||
aliases:
|
||||
- Belgian UFO Wave
|
||||
event_class: uap-encounter
|
||||
date_start: 1989-11
|
||||
date_end: 1989-11
|
||||
date_confidence: high
|
||||
primary_location: Belgium (multiple sites)
|
||||
observers: []
|
||||
uap_objects: []
|
||||
documented_in: []
|
||||
total_mentions: 0
|
||||
documents_count: 0
|
||||
narrative_summary: Starting in late November 1989, Belgium experienced a sustained
|
||||
wave of anomalous aerial sightings that eventually generated over 13,500 formal
|
||||
civilian witness reports compiled by SOBEPS (Société Belge d'Étude des Phénomènes
|
||||
Spatiaux). The objects were consistently described as large, low-flying, triangular
|
||||
or delta-shaped craft carrying bright lights at each corner and a central downward
|
||||
beam, typically moving at low speed before departing rapidly. The Belgian Gendarmerie
|
||||
filed official reports on the night of 29 November 1989, when officers at multiple
|
||||
posts observed the objects at close range. The operationally most significant incident
|
||||
occurred on the night of 30–31 March 1990, when ground radar stations at Glons and
|
||||
Semmerzake tracked unidentified targets and two Belgian Air Force F-16s briefly
|
||||
achieved radar lock on objects that subsequently accelerated beyond conventional
|
||||
performance parameters and broke contact. General Wilfried De Brouwer, Chief of
|
||||
Operations of the Belgian Air Staff, stated publicly that the Air Force could not
|
||||
identify the objects and that no foreign nation had claimed responsibility for any
|
||||
overflights. The Belgian government's decision to collaborate openly with SOBEPS
|
||||
and release military radar data to civilian researchers remains one of the most
|
||||
transparent official responses to a sustained UAP phenomenon on record.
|
||||
narrative_summary_pt_br: A partir do final de novembro de 1989, a Bélgica registrou
|
||||
uma onda sustentada de avistamentos aéreos anômalos que acumulou mais de 13.500
|
||||
relatos formais de civis compilados pela SOBEPS (Société Belge d'Étude des Phénomènes
|
||||
Spatiaux). Os objetos foram consistentemente descritos como aeronaves triangulares
|
||||
ou em forma de delta, de grande porte, voando em baixa altitude com luzes brilhantes
|
||||
em cada vértice e um feixe central voltado para baixo, deslocando-se lentamente
|
||||
antes de partir em alta velocidade. A Gendarmerie belga registrou boletins oficiais
|
||||
na noite de 29 de novembro de 1989, quando agentes em múltiplos postos observaram
|
||||
os objetos a curta distância. O incidente operacionalmente mais significativo ocorreu
|
||||
na noite de 30 para 31 de março de 1990, quando estações de radar terrestres em
|
||||
Glons e Semmerzake rastrearam alvos não identificados enquanto dois F-16s da Força
|
||||
Aérea belga obtiveram, momentaneamente, bloqueio de radar em objetos que em seguida
|
||||
aceleraram além dos parâmetros de desempenho convencionais e romperam o contato.
|
||||
O General Wilfried De Brouwer, Chefe de Operações do Estado-Maior da Força Aérea
|
||||
belga, declarou publicamente que a Força Aérea não conseguiu identificar os objetos
|
||||
e que nenhuma nação estrangeira reivindicou responsabilidade pelos sobrevoos. A
|
||||
decisão do governo belga de colaborar abertamente com a SOBEPS e divulgar dados
|
||||
de radar militar a pesquisadores civis permanece uma das respostas oficiais mais
|
||||
transparentes já registradas diante de um fenômeno UAP sustentado.
|
||||
summary_status: curated
|
||||
summary_confidence: high
|
||||
enrichment_status: none
|
||||
external_sources: []
|
||||
last_ingest: '2026-05-18T03:39:33Z'
|
||||
last_lint: '2026-05-18T03:39:33Z'
|
||||
wiki_version: 0.1.0
|
||||
---
|
||||
|
||||
# Belgian UFO Wave
|
||||
|
||||
## Description (EN)
|
||||
|
||||
Starting in late November 1989, Belgium experienced a sustained wave of anomalous aerial sightings that eventually generated over 13,500 formal civilian witness reports compiled by SOBEPS (Société Belge d'Étude des Phénomènes Spatiaux). The objects were consistently described as large, low-flying, triangular or delta-shaped craft carrying bright lights at each corner and a central downward beam, typically moving at low speed before departing rapidly. The Belgian Gendarmerie filed official reports on the night of 29 November 1989, when officers at multiple posts observed the objects at close range. The operationally most significant incident occurred on the night of 30–31 March 1990, when ground radar stations at Glons and Semmerzake tracked unidentified targets and two Belgian Air Force F-16s briefly achieved radar lock on objects that subsequently accelerated beyond conventional performance parameters and broke contact. General Wilfried De Brouwer, Chief of Operations of the Belgian Air Staff, stated publicly that the Air Force could not identify the objects and that no foreign nation had claimed responsibility for any overflights. The Belgian government's decision to collaborate openly with SOBEPS and release military radar data to civilian researchers remains one of the most transparent official responses to a sustained UAP phenomenon on record.
|
||||
|
||||
## Descrição (PT-BR)
|
||||
|
||||
A partir do final de novembro de 1989, a Bélgica registrou uma onda sustentada de avistamentos aéreos anômalos que acumulou mais de 13.500 relatos formais de civis compilados pela SOBEPS (Société Belge d'Étude des Phénomènes Spatiaux). Os objetos foram consistentemente descritos como aeronaves triangulares ou em forma de delta, de grande porte, voando em baixa altitude com luzes brilhantes em cada vértice e um feixe central voltado para baixo, deslocando-se lentamente antes de partir em alta velocidade. A Gendarmerie belga registrou boletins oficiais na noite de 29 de novembro de 1989, quando agentes em múltiplos postos observaram os objetos a curta distância. O incidente operacionalmente mais significativo ocorreu na noite de 30 para 31 de março de 1990, quando estações de radar terrestres em Glons e Semmerzake rastrearam alvos não identificados enquanto dois F-16s da Força Aérea belga obtiveram, momentaneamente, bloqueio de radar em objetos que em seguida aceleraram além dos parâmetros de desempenho convencionais e romperam o contato. O General Wilfried De Brouwer, Chefe de Operações do Estado-Maior da Força Aérea belga, declarou publicamente que a Força Aérea não conseguiu identificar os objetos e que nenhuma nação estrangeira reivindicou responsabilidade pelos sobrevoos. A decisão do governo belga de colaborar abertamente com a SOBEPS e divulgar dados de radar militar a pesquisadores civis permanece uma das respostas oficiais mais transparentes já registradas diante de um fenômeno UAP sustentado.
|
||||
69
wiki/entities/events/EV-1997-03-13-phoenix-lights.md
Normal file
69
wiki/entities/events/EV-1997-03-13-phoenix-lights.md
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
---
|
||||
schema_version: 0.1.0
|
||||
type: entity
|
||||
entity_class: event
|
||||
event_id: EV-1997-03-13-phoenix-lights
|
||||
canonical_name: Phoenix Lights
|
||||
aliases:
|
||||
- Phoenix Lights
|
||||
event_class: uap-encounter
|
||||
date_start: '1997-03-13'
|
||||
date_end: '1997-03-13'
|
||||
date_confidence: high
|
||||
primary_location: Phoenix, Arizona, USA (and southern Arizona)
|
||||
observers: []
|
||||
uap_objects: []
|
||||
documented_in: []
|
||||
total_mentions: 0
|
||||
documents_count: 0
|
||||
narrative_summary: 'On the night of March 13, 1997, a silent V-shaped formation of
|
||||
lights traversed southern Arizona from roughly 20:00 to 22:00 local time, moving
|
||||
northward at low altitude over Prescott, the Phoenix metropolitan area, and Tucson,
|
||||
witnessed by an estimated two thousand or more civilians as well as law enforcement
|
||||
officers and former military personnel. A second, distinct event—a stationary cluster
|
||||
of amber lights visible south of Phoenix around 22:00—was later attributed by the
|
||||
Arizona Air National Guard to LUU-2 illumination flares dropped by A-10 aircraft
|
||||
during a training exercise at Barry Goldwater Range. The earlier moving formation
|
||||
received no analogous official explanation: witnesses consistently described an
|
||||
object estimated at nearly a mile in width, traveling in near-complete silence at
|
||||
altitudes variously reported between 100 and 3,000 feet, and occluding stars as
|
||||
it passed overhead. Governor Fife Symington publicly staged a mock ''alien'' press
|
||||
conference in the days following the incident, but in a 2007 CNN interview he acknowledged
|
||||
that he personally observed the craft and considered it ''otherworldly.'' The Phoenix
|
||||
Lights remain among the most extensively documented mass-witness UAP events in American
|
||||
history.'
|
||||
narrative_summary_pt_br: 'Na noite de 13 de março de 1997, uma formação silenciosa
|
||||
em V de luzes cruzou o sul do Arizona entre aproximadamente 20h00 e 22h00 (horário
|
||||
local), deslocando-se para o norte em baixa altitude sobre Prescott, a região metropolitana
|
||||
de Phoenix e Tucson, observada por uma estimativa de dois mil ou mais civis, além
|
||||
de agentes policiais e ex-militares. Um segundo evento distinto — um agrupamento
|
||||
estacionário de luzes âmbar visíveis ao sul de Phoenix por volta das 22h00 — foi
|
||||
posteriormente atribuído pela Guarda Aérea Nacional do Arizona a sinalizadores de
|
||||
iluminação LUU-2 lançados por aeronaves A-10 durante um exercício de treinamento
|
||||
na Barry Goldwater Range. A formação em movimento, contudo, não recebeu explicação
|
||||
oficial equivalente: as testemunhas descreveram de forma consistente um objeto estimado
|
||||
em quase uma milha de largura, deslocando-se em quase completo silêncio a altitudes
|
||||
reportadas entre 30 e 900 metros, ocultando as estrelas ao passar diretamente sobre
|
||||
elas. O governador Fife Symington protagonizou uma coletiva de imprensa jocosa nos
|
||||
dias seguintes ao incidente, simulando revelar um ''alienígena'', mas em entrevista
|
||||
à CNN em 2007 reconheceu ter presenciado pessoalmente o objeto e o descreveu como
|
||||
''de outro mundo''. As Phoenix Lights permanecem um dos eventos UAP de testemunho
|
||||
em massa mais extensivamente documentados na história americana.'
|
||||
summary_status: curated
|
||||
summary_confidence: high
|
||||
enrichment_status: none
|
||||
external_sources: []
|
||||
last_ingest: '2026-05-18T03:40:20Z'
|
||||
last_lint: '2026-05-18T03:40:20Z'
|
||||
wiki_version: 0.1.0
|
||||
---
|
||||
|
||||
# Phoenix Lights
|
||||
|
||||
## Description (EN)
|
||||
|
||||
On the night of March 13, 1997, a silent V-shaped formation of lights traversed southern Arizona from roughly 20:00 to 22:00 local time, moving northward at low altitude over Prescott, the Phoenix metropolitan area, and Tucson, witnessed by an estimated two thousand or more civilians as well as law enforcement officers and former military personnel. A second, distinct event—a stationary cluster of amber lights visible south of Phoenix around 22:00—was later attributed by the Arizona Air National Guard to LUU-2 illumination flares dropped by A-10 aircraft during a training exercise at Barry Goldwater Range. The earlier moving formation received no analogous official explanation: witnesses consistently described an object estimated at nearly a mile in width, traveling in near-complete silence at altitudes variously reported between 100 and 3,000 feet, and occluding stars as it passed overhead. Governor Fife Symington publicly staged a mock 'alien' press conference in the days following the incident, but in a 2007 CNN interview he acknowledged that he personally observed the craft and considered it 'otherworldly.' The Phoenix Lights remain among the most extensively documented mass-witness UAP events in American history.
|
||||
|
||||
## Descrição (PT-BR)
|
||||
|
||||
Na noite de 13 de março de 1997, uma formação silenciosa em V de luzes cruzou o sul do Arizona entre aproximadamente 20h00 e 22h00 (horário local), deslocando-se para o norte em baixa altitude sobre Prescott, a região metropolitana de Phoenix e Tucson, observada por uma estimativa de dois mil ou mais civis, além de agentes policiais e ex-militares. Um segundo evento distinto — um agrupamento estacionário de luzes âmbar visíveis ao sul de Phoenix por volta das 22h00 — foi posteriormente atribuído pela Guarda Aérea Nacional do Arizona a sinalizadores de iluminação LUU-2 lançados por aeronaves A-10 durante um exercício de treinamento na Barry Goldwater Range. A formação em movimento, contudo, não recebeu explicação oficial equivalente: as testemunhas descreveram de forma consistente um objeto estimado em quase uma milha de largura, deslocando-se em quase completo silêncio a altitudes reportadas entre 30 e 900 metros, ocultando as estrelas ao passar diretamente sobre elas. O governador Fife Symington protagonizou uma coletiva de imprensa jocosa nos dias seguintes ao incidente, simulando revelar um 'alienígena', mas em entrevista à CNN em 2007 reconheceu ter presenciado pessoalmente o objeto e o descreveu como 'de outro mundo'. As Phoenix Lights permanecem um dos eventos UAP de testemunho em massa mais extensivamente documentados na história americana.
|
||||
73
wiki/entities/events/EV-2004-11-14-nimitz-tic-tac.md
Normal file
73
wiki/entities/events/EV-2004-11-14-nimitz-tic-tac.md
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
---
|
||||
schema_version: 0.1.0
|
||||
type: entity
|
||||
entity_class: event
|
||||
event_id: EV-2004-11-14-nimitz-tic-tac
|
||||
canonical_name: Nimitz Tic Tac Incident
|
||||
aliases:
|
||||
- Nimitz Tic Tac Incident
|
||||
event_class: uap-encounter
|
||||
date_start: '2004-11-14'
|
||||
date_end: '2004-11-14'
|
||||
date_confidence: high
|
||||
primary_location: USS Nimitz Carrier Strike Group, off San Diego coast
|
||||
observers: []
|
||||
uap_objects: []
|
||||
documented_in: []
|
||||
total_mentions: 0
|
||||
documents_count: 0
|
||||
narrative_summary: On 14 November 2004, F/A-18F crews from the USS Nimitz Carrier
|
||||
Strike Group, operating approximately 100 nautical miles southwest of San Diego,
|
||||
intercepted an unidentified aerial object that USS Princeton radar operator Senior
|
||||
Chief Kevin Day had been tracking intermittently for several days at altitudes ranging
|
||||
from 80,000 feet down to sea level. Commander David Fravor and Lt. Commander Jim
|
||||
Slaight observed a white, oblong craft—roughly 40 feet in length, with no wings,
|
||||
no exhaust plume, and no visible propulsion—hovering over a roughly 100-meter diameter
|
||||
patch of disturbed water; when Fravor descended toward it, the object climbed briefly
|
||||
to meet him, then accelerated to the northwest at a rate that outpaced any known
|
||||
aircraft of the era, without sonic signature or thermal wake. Senior Chief Day reported
|
||||
that Princeton's radar reacquired the contact within seconds at the pre-briefed
|
||||
combat air patrol rendezvous coordinate, approximately 60 miles from the original
|
||||
intercept position. On a subsequent sortie, Lt. Commander Chad Underwood captured
|
||||
the object on ATFLIR, producing the infrared footage later designated FLIR1. The
|
||||
Department of Defense declassified the FLIR1 video in April 2020 and confirmed its
|
||||
provenance; the ODNI's June 2021 preliminary assessment classified the encounter
|
||||
as uncharacterized, with no attribution to any known domestic or foreign aerospace
|
||||
program.
|
||||
narrative_summary_pt_br: Em 14 de novembro de 2004, pilotos de F/A-18F do Grupo de
|
||||
Ataque do USS Nimitz, operando a aproximadamente 100 milhas náuticas a sudoeste
|
||||
de San Diego, interceptaram um objeto aéreo não identificado que o operador de radar
|
||||
do USS Princeton, o Chefe Sênior Kevin Day, vinha rastreando de forma intermitente
|
||||
por vários dias, a altitudes entre 24.000 metros e o nível do mar. O Comandante
|
||||
David Fravor e o Tenente-Comandante Jim Slaight observaram uma aeronave branca e
|
||||
oblonga — com cerca de 12 metros de comprimento, sem asas, sem rastro de exaustão
|
||||
e sem sistema de propulsão visível — pairando sobre uma área circular de aproximadamente
|
||||
100 metros de diâmetro em que a água apresentava agitação anômala; quando Fravor
|
||||
iniciou a descida em direção ao objeto, ele subiu brevemente em sua direção e então
|
||||
acelerou para o noroeste a uma velocidade que superava qualquer aeronave conhecida
|
||||
à época, sem rastro acústico ou térmico. O Chefe Sênior Day relatou que o radar
|
||||
do Princeton readquiriu o contato em segundos no ponto de patrulha de combate pré-coordenado,
|
||||
a aproximadamente 60 milhas do local do encontro. Numa saída posterior, o Tenente-Comandante
|
||||
Chad Underwood registrou o objeto com o sensor ATFLIR, produzindo a filmagem infravermelha
|
||||
posteriormente designada FLIR1. O Departamento de Defesa americano desclassificou
|
||||
o vídeo FLIR1 em abril de 2020 e confirmou sua procedência; a avaliação preliminar
|
||||
do ODNI de junho de 2021 classificou o encontro como não caracterizado, sem atribuição
|
||||
a qualquer programa aeroespacial doméstico ou estrangeiro conhecido.
|
||||
summary_status: curated
|
||||
summary_confidence: high
|
||||
enrichment_status: none
|
||||
external_sources: []
|
||||
last_ingest: '2026-05-18T03:41:12Z'
|
||||
last_lint: '2026-05-18T03:41:12Z'
|
||||
wiki_version: 0.1.0
|
||||
---
|
||||
|
||||
# Nimitz Tic Tac Incident
|
||||
|
||||
## Description (EN)
|
||||
|
||||
On 14 November 2004, F/A-18F crews from the USS Nimitz Carrier Strike Group, operating approximately 100 nautical miles southwest of San Diego, intercepted an unidentified aerial object that USS Princeton radar operator Senior Chief Kevin Day had been tracking intermittently for several days at altitudes ranging from 80,000 feet down to sea level. Commander David Fravor and Lt. Commander Jim Slaight observed a white, oblong craft—roughly 40 feet in length, with no wings, no exhaust plume, and no visible propulsion—hovering over a roughly 100-meter diameter patch of disturbed water; when Fravor descended toward it, the object climbed briefly to meet him, then accelerated to the northwest at a rate that outpaced any known aircraft of the era, without sonic signature or thermal wake. Senior Chief Day reported that Princeton's radar reacquired the contact within seconds at the pre-briefed combat air patrol rendezvous coordinate, approximately 60 miles from the original intercept position. On a subsequent sortie, Lt. Commander Chad Underwood captured the object on ATFLIR, producing the infrared footage later designated FLIR1. The Department of Defense declassified the FLIR1 video in April 2020 and confirmed its provenance; the ODNI's June 2021 preliminary assessment classified the encounter as uncharacterized, with no attribution to any known domestic or foreign aerospace program.
|
||||
|
||||
## Descrição (PT-BR)
|
||||
|
||||
Em 14 de novembro de 2004, pilotos de F/A-18F do Grupo de Ataque do USS Nimitz, operando a aproximadamente 100 milhas náuticas a sudoeste de San Diego, interceptaram um objeto aéreo não identificado que o operador de radar do USS Princeton, o Chefe Sênior Kevin Day, vinha rastreando de forma intermitente por vários dias, a altitudes entre 24.000 metros e o nível do mar. O Comandante David Fravor e o Tenente-Comandante Jim Slaight observaram uma aeronave branca e oblonga — com cerca de 12 metros de comprimento, sem asas, sem rastro de exaustão e sem sistema de propulsão visível — pairando sobre uma área circular de aproximadamente 100 metros de diâmetro em que a água apresentava agitação anômala; quando Fravor iniciou a descida em direção ao objeto, ele subiu brevemente em sua direção e então acelerou para o noroeste a uma velocidade que superava qualquer aeronave conhecida à época, sem rastro acústico ou térmico. O Chefe Sênior Day relatou que o radar do Princeton readquiriu o contato em segundos no ponto de patrulha de combate pré-coordenado, a aproximadamente 60 milhas do local do encontro. Numa saída posterior, o Tenente-Comandante Chad Underwood registrou o objeto com o sensor ATFLIR, produzindo a filmagem infravermelha posteriormente designada FLIR1. O Departamento de Defesa americano desclassificou o vídeo FLIR1 em abril de 2020 e confirmou sua procedência; a avaliação preliminar do ODNI de junho de 2021 classificou o encontro como não caracterizado, sem atribuição a qualquer programa aeroespacial doméstico ou estrangeiro conhecido.
|
||||
71
wiki/entities/events/EV-2017-12-16-aatip-disclosure.md
Normal file
71
wiki/entities/events/EV-2017-12-16-aatip-disclosure.md
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
---
|
||||
schema_version: 0.1.0
|
||||
type: entity
|
||||
entity_class: event
|
||||
event_id: EV-2017-12-16-aatip-disclosure
|
||||
canonical_name: AATIP Public Disclosure
|
||||
aliases:
|
||||
- AATIP Public Disclosure
|
||||
event_class: uap-disclosure-event
|
||||
date_start: '2017-12-16'
|
||||
date_end: '2017-12-16'
|
||||
date_confidence: high
|
||||
primary_location: New York / Washington, D.C., USA
|
||||
observers: []
|
||||
uap_objects: []
|
||||
documented_in: []
|
||||
total_mentions: 0
|
||||
documents_count: 0
|
||||
narrative_summary: On 16 December 2017, the New York Times published a front-page
|
||||
investigation confirming the existence of the Advanced Aerospace Threat Identification
|
||||
Program (AATIP), a Pentagon effort funded at approximately $22 million from 2007
|
||||
to 2012 through appropriations secured by Senator Harry Reid, with primary research
|
||||
contracts held by Robert Bigelow's Bigelow Aerospace Advanced Space Studies. Luis
|
||||
Elizondo, who had resigned from the Office of the Secretary of Defense in October
|
||||
2017, identified himself as the program's former director and provided three declassified
|
||||
infrared videos—subsequently designated FLIR1, GIMBAL, and GOFAST—documenting U.S.
|
||||
Navy encounters with objects exhibiting aerodynamic performance outside the parameters
|
||||
of any known aircraft. The simultaneous release of the footage through To The Stars
|
||||
Academy of Arts & Science marked the first time the U.S. government acknowledged,
|
||||
however obliquely, a modern classified program dedicated to UAP study. The Pentagon
|
||||
confirmed AATIP's existence but disputed claims about its scope and Elizondo's specific
|
||||
managerial role. The disclosure triggered Senate briefings, prompted the Navy to
|
||||
formalize UAP reporting channels, and set in motion the legislative sequence that
|
||||
produced the UAP Task Force in 2020 and the All-domain Anomaly Resolution Office
|
||||
in 2022.
|
||||
narrative_summary_pt_br: Em 16 de dezembro de 2017, o New York Times publicou uma
|
||||
investigação de primeira página confirmando a existência do Advanced Aerospace Threat
|
||||
Identification Program (AATIP), um programa do Pentágono financiado com aproximadamente
|
||||
22 milhões de dólares entre 2007 e 2012, viabilizado por verbas articuladas pelo
|
||||
senador Harry Reid, com os principais contratos de pesquisa entregues à Bigelow
|
||||
Aerospace Advanced Space Studies, empresa de Robert Bigelow. Luis Elizondo, que
|
||||
havia se demitido do Escritório do Secretário de Defesa em outubro de 2017, identificou-se
|
||||
como ex-diretor do programa e disponibilizou três vídeos infravermelhos desclassificados
|
||||
— posteriormente designados FLIR1, GIMBAL e GOFAST — que registram encontros de
|
||||
pilotos da Marinha dos EUA com objetos dotados de desempenho aerodinâmico fora dos
|
||||
parâmetros de qualquer aeronave conhecida. A divulgação simultânea das imagens pela
|
||||
To The Stars Academy of Arts & Science marcou a primeira vez em que o governo norte-americano
|
||||
reconheceu, ainda que indiretamente, a existência de um programa classificado moderno
|
||||
dedicado ao estudo de PANs. O Pentágono confirmou a existência do AATIP, mas contestou
|
||||
alegações sobre o escopo do programa e o papel gerencial específico de Elizondo.
|
||||
A revelação desencadeou sessões de briefing no Senado, levou a Marinha a formalizar
|
||||
canais de reporte de PANs e iniciou o processo legislativo que resultaria no UAP
|
||||
Task Force em 2020 e no All-domain Anomaly Resolution Office em 2022.
|
||||
summary_status: curated
|
||||
summary_confidence: high
|
||||
enrichment_status: none
|
||||
external_sources: []
|
||||
last_ingest: '2026-05-18T03:41:50Z'
|
||||
last_lint: '2026-05-18T03:41:50Z'
|
||||
wiki_version: 0.1.0
|
||||
---
|
||||
|
||||
# AATIP Public Disclosure
|
||||
|
||||
## Description (EN)
|
||||
|
||||
On 16 December 2017, the New York Times published a front-page investigation confirming the existence of the Advanced Aerospace Threat Identification Program (AATIP), a Pentagon effort funded at approximately $22 million from 2007 to 2012 through appropriations secured by Senator Harry Reid, with primary research contracts held by Robert Bigelow's Bigelow Aerospace Advanced Space Studies. Luis Elizondo, who had resigned from the Office of the Secretary of Defense in October 2017, identified himself as the program's former director and provided three declassified infrared videos—subsequently designated FLIR1, GIMBAL, and GOFAST—documenting U.S. Navy encounters with objects exhibiting aerodynamic performance outside the parameters of any known aircraft. The simultaneous release of the footage through To The Stars Academy of Arts & Science marked the first time the U.S. government acknowledged, however obliquely, a modern classified program dedicated to UAP study. The Pentagon confirmed AATIP's existence but disputed claims about its scope and Elizondo's specific managerial role. The disclosure triggered Senate briefings, prompted the Navy to formalize UAP reporting channels, and set in motion the legislative sequence that produced the UAP Task Force in 2020 and the All-domain Anomaly Resolution Office in 2022.
|
||||
|
||||
## Descrição (PT-BR)
|
||||
|
||||
Em 16 de dezembro de 2017, o New York Times publicou uma investigação de primeira página confirmando a existência do Advanced Aerospace Threat Identification Program (AATIP), um programa do Pentágono financiado com aproximadamente 22 milhões de dólares entre 2007 e 2012, viabilizado por verbas articuladas pelo senador Harry Reid, com os principais contratos de pesquisa entregues à Bigelow Aerospace Advanced Space Studies, empresa de Robert Bigelow. Luis Elizondo, que havia se demitido do Escritório do Secretário de Defesa em outubro de 2017, identificou-se como ex-diretor do programa e disponibilizou três vídeos infravermelhos desclassificados — posteriormente designados FLIR1, GIMBAL e GOFAST — que registram encontros de pilotos da Marinha dos EUA com objetos dotados de desempenho aerodinâmico fora dos parâmetros de qualquer aeronave conhecida. A divulgação simultânea das imagens pela To The Stars Academy of Arts & Science marcou a primeira vez em que o governo norte-americano reconheceu, ainda que indiretamente, a existência de um programa classificado moderno dedicado ao estudo de PANs. O Pentágono confirmou a existência do AATIP, mas contestou alegações sobre o escopo do programa e o papel gerencial específico de Elizondo. A revelação desencadeou sessões de briefing no Senado, levou a Marinha a formalizar canais de reporte de PANs e iniciou o processo legislativo que resultaria no UAP Task Force em 2020 e no All-domain Anomaly Resolution Office em 2022.
|
||||
Loading…
Reference in a new issue