# IronDome — Referência Técnica Completa

**Versão**: 1.14.0 · **Atualizado**: 2026-04-24
**Público**: agente Claude que vai modificar o código.
**Complementa**: `HANDOFF.md` (arquitetura high-level). Esse arquivo é "baixo
nível" — cada botão, campo, função e a lógica da IA em detalhes.

---

## ÍNDICE

1. [Referência da UI — cada elemento explicado](#1-referência-da-ui)
2. [Referência das funções backend](#2-referência-das-funções-backend)
3. [IA DEEP DIVE — como a IA aprende e decide](#3-ia-deep-dive)

---

# 1. Referência da UI

Arquivo: `static/index.html` (~3500 linhas, vanilla JS/HTML/CSS).

## 1.1 Topbar (sempre visível)

| Elemento | ID/Classe | Função |
|---|---|---|
| Brand `IronDome v1.14.0` | `.brand` + `#brandVersion` | Versão preenchida via `/api/version` no load. |
| Clock pill | `#clockPill`, `#clockDate`, `#clockTime` | Data + hora local atualizado a cada 1s via `tick()`. |
| Bot toggle | `#botToggle` + `#botLabel` | Liga/desliga o bot inteiro (`config.enabled`). Clique → `toggleBot()` → POST `/api/autoclick/config`. Verde=ON, vermelho=OFF. |
| P&L pill | `#pnlPill` + `#pnlValue` | Mostra `stats.pnl_today`. Verde se ≥0, vermelho se <0. Classe `.dry` aparece em DRY-RUN (fundo cinza + sufixo "· SIM"). |
| WS status | `#wsStatus` | Estado do snippet: "snippet ativo · N msg" quando recebendo. |
| Session timer | `#sessionStatus` | `ws_msgs_received - session_start` formatado como `sessão 12m 34s`. |

**Função**: `updateAutoStatusPill(cfg)` controla o pill de status (`#autoStatusPill`)
que aparece só quando bot está operando de verdade. Somem em parada técnica.

## 1.2 Aba PAINEL 10x (antiga PAINEL)

Dashboard de observação pro target 10x.

| Elemento | Função |
|---|---|
| Termômetro vertical | Mostra quão "quente" está o ciclo (intervalos desde último 10x). Subida verde = mais próximo do pico. |
| Card "Último crash" | `crashes[-1].value`. |
| Card "Rodadas desde 10x" | `stats.intervalo_atual`. |
| Card "Média teórica" | Valor esperado (≈ 10 rodadas). |
| Card "Max gap observado" | `stats.max_intervalo` — maior drought histórico. |
| Sparkline | Últimos 60 crashes como barras verticais. |
| Tabela histórico | Últimas 20 com timestamp/valor. |

## 1.3 Aba ANÁLISE PRO

Dashboard estatístico profissional (compute em `compute_pro_stats` no server).

| Métrica | Significado |
|---|---|
| EV por aposta (R$) | Valor esperado por R$1 apostado em target 10x. Negativo = perde no longo prazo. |
| RTP da estratégia | Return To Player da sua estratégia (não do jogo). 100% = break-even. |
| Kelly fraction | Fração ótima do bankroll pra apostar. Negativo = não apostar. |
| Max Drawdown | Maior queda desde o pico acumulado. |
| Max losing streak | Pior série de perdas seguidas. |
| P&L cumulativo | Se você apostasse 1 unidade em cada rodada sacando em 10x, seu saldo seria isso. |

## 1.4 Aba AUTO-CLICK

Aba principal de operação. Dois cards lado a lado (Painel 1 rosa, Painel 2 azul).

### 1.4.1 Header do painel
- `.panel-head` contém título "PAINEL 1" / "PAINEL 2" + `.wl-counter`.
- `.wl-counter` tem 3 badges:
  - **V** (verde): `panels_stats[panel].wins` — hits totais.
  - **D** (vermelho): `panels_stats[panel].losses` — misses totais.
  - **IA** (roxo): formato `x/y` = `ia_wins/ia_approves` — hits onde IA aprovou.

### 1.4.2 Campos principais (sempre visíveis)

| Campo | Classe | Função |
|---|---|---|
| Painel N ATIVO | `.p-enabled` | Liga/desliga esse painel individual. |
| Aposta (R$) | `.p-betAmount` | Valor base da aposta. Será multiplicado por intelligence.bet_multiplier e confidence tier. |
| Target (x) | `.p-cashoutTarget` | Multiplicador alvo pro cashout (ex: 2.0 = saca em 2x). |

### 1.4.3 Status box (AGUARDAR/ENTRADA/COOLDOWN)

`.op-status-box` é o indicador visual grande (min-height 96px):

- **ENTRADA** (verde): IA aprovou e sem cooldown. Bot vai apostar próxima rodada.
- **AGUARDAR** (vermelho): IA rejeitou ou painel desativado.
- **AGUARDANDO N RODADAS** (amarelo): em cooldown. Sublinha mostra tipo:
  - "cooldown após perda"
  - "cooldown após ganho"
  - "cooldown" (genérico, entre apostas)

Segunda linha (`.op-bet`): mostra **aposta efetiva** calculada (base × tier):
- Badge **BASE** (cinza) — sem confidence sizing ou prob baixa.
- Badge **MÉDIA ×N** (amarelo) — prob ≥ threshold médio.
- Badge **ALTA ×N** (verde) — prob ≥ threshold alto.

### 1.4.4 Modo de operação (dropdown)

`.p-operationMode` — select com 3 opções:

| Opção | Margem sobre teórico | Critério target 2x |
|---|---|---|
| Conservador | 15% | ~57% |
| **Equilibrado** (default) | 10% | ~55% |
| Agressivo | 5% | ~52% |

**Ação ao mudar**: `onModeChange(panel)` → readAutoCfgFromForm() → saveAutoCfg() → POST `/api/autoclick/config` → pollIaGate() re-renderiza status.

Não precisa clicar SALVAR — save imediato.

### 1.4.5 Seção Avançado (collapsible, default fechada)

Botão `.advanced-toggle` expande/recolhe. Contém:

#### Checkbox "Usar IA" (`.p-useIA`)
Quando ligado, consulta `/api/intelligence/gate` antes de cada aposta. Se rejeitar, bot não aposta.

#### IA Status Box (`#iaStatusP1`/`#iaStatusP2`)
Aparece só com `useIA=true`. Atualizado por `pollIaGate(panel)` a cada 3s. Mostra:
- Decisão ("✓ LIBERA" / "✗ AGUARDA") + target.
- Probabilidade estimada vs teórica.
- Reasoning completo (linhas do pattern match, smoothing, causal, blacklist, threshold final).

#### Aposta dinâmica por confiança (`.p-confidenceSizing`)
Se ligado, aposta escala com confiança da IA:

| Campo | Classe | Default |
|---|---|---|
| Prob. alta ≥ (%) | `.p-confHighThreshold` | 85 |
| Multiplica base por | `.p-confHighMultiplier` | 5 |
| Prob. média ≥ (%) | `.p-confMedThreshold` | 70 |
| Multiplica base por | `.p-confMedMultiplier` | 2 |

Preview ao vivo mostra aposta em cada faixa (ex: "≥85% → R$5.00 · ≥70% → R$2.00 · abaixo → R$1.00").

**Lógica no daemon**: após gate aprovar com prob `p`, escolhe tier:
- `p >= high_thr` → amount *= high_mul, tier='ALTA'
- `p >= med_thr` → amount *= med_mul, tier='MÉDIA'
- senão → base, tier='BASE'

#### Disparar aposta quando (`.p-entryTrigger`)

| Opção | Lógica |
|---|---|
| Sinal BET (apenas) | Só aposta se `stats.signals[-1].type == 'BET'`. |
| Sinal BET ou ATENÇÃO | Idem + ATENCAO. |
| Gap ≥ N rodadas sem 10x | Aposta se `stats.intervalo_atual >= gapThreshold`. |
| Sempre (toda rodada) | Aposta em toda rodada (sujeito a cooldowns + IA). |

Se trigger=gap_threshold, aparece campo `.p-gapThreshold` (default 18).

#### Cooldowns

| Campo | Classe | Default | Efeito |
|---|---|---|---|
| Cooldown (rodadas) | `.p-cooldown` | 0 | Aguarda N rodadas após cada aposta. |
| Cooldown após perda | `.p-cooldownAfterLoss` | 0 | Extra após miss. |
| Cooldown após ganho | `.p-cooldownAfterWin` | 2 | Extra após hit (força reavaliação). |

Fórmula no daemon (v1.4.1+): `loss_wait_until = api_total + caf + 1`. O `+1` compensa que api_total já incrementou quando miss é registrado.

#### Horário (`.p-startTime` / `.p-endTime`)

Dois inputs `type=time`. Janela em HH:MM. Se os dois vazios, opera 24h.
Suporta janela que cruza meia-noite (ex: 22:00–06:00).

Status embaixo atualizado por `updateScheduleStatus(panel)`: "Dentro do horário" verde ou "Fora do horário — painel em espera" amarelo.

### 1.4.6 Limites compartilhados (abaixo dos painéis)

| Campo | ID | Default | Função |
|---|---|---|---|
| Stop-loss do dia (R$) | `#cfgStopLoss` | 50 | Se `pnl_today <= -X`, dispara parada técnica. |
| Stop-win do dia (R$) | `#cfgStopWin` | 100 | Se `pnl_today >= X`, dispara parada técnica. |
| Máximo de apostas por sessão | `#cfgMaxBets` | 1000 | Se `bets_today >= X`, dispara parada técnica. |
| Parar após N perdas consecutivas | `#cfgMaxLossStreak` | 10 | Se `loss_streak >= X`, parada técnica com "Tivemos N perdas consecutivas". |
| Duração da parada técnica (min) | `#cfgTechPauseMinutes` | 30 | Minutos de pausa. |

### 1.4.7 Anti-detecção (global)

| Campo | Default | Função |
|---|---|---|
| Jitter mín (ms) | 400 | Delay aleatório antes do click. |
| Jitter máx (ms) | 1200 | Limite superior. |

Implementação: `jitter()` em daemon retorna `random.randint(mn, mx)/1000.0`.

### 1.4.8 Outros controles

- **DRY-RUN** (`#cfgDryRun`): simula apostas sem clicar de verdade. P&L pill fica cinza + sufixo "· SIM". Shadow learning ativa.
- **Efeito GREEN** (`#cfgGreenFx`): overlay verde + som de moedas ao ganhar.
- **BOT ATIVO** (`#cfgEnabled`): master toggle, equivalente ao botão BOT no topbar.
- **SALVAR** (botão): `saveAutoCfg()` → POST `/api/autoclick/config`. Badge "alterações não salvas" pulsa amarelo quando há dirty.
- **Reset** (botão): POST `/api/autoclick/reset` → zera `stats`, `panels_stats`, `shadow_stats`, `paused_until`.

## 1.5 Aba TELEGRAM

Campos pra configurar notificações:

| Campo | ID | Função |
|---|---|---|
| Bot Token | `#tgBotToken` | Token do bot criado via @BotFather. |
| Chat ID | `#tgChatId` | ID do chat (individual ou grupo). |
| Habilitado | `#tgEnabled` | Liga/desliga notificações. |
| Notificar ganho | `#tgNotifyHit` | Envia mensagem ao hit. |
| Notificar perda | `#tgNotifyMiss` | Idem ao miss. |
| Notificar aposta | `#tgNotifyBet` | Ao colocar aposta (default off, verboso). |
| Notificar parada | `#tgNotifyStop` | Quando dispara parada técnica. |

**Testar** (botão): POST `/api/telegram/test` — síncrono, mostra resultado na hora.

Config é **persistida em `data/telegram.json`** (desde v1.5.3) — sobrevive restart.

## 1.6 Aba SOBRE

- Metadados: versão, release date, totais DB (crashes + bets).
- Update banner (`#updateBanner`): aparece só se `/api/updates/check` indicar versão nova.
- Botão "Atualizar agora" (`installUpdate()`) → POST `/api/updates/install` → restart em 4s.
- Changelog completo (lido de `/CHANGELOG.md`).

## 1.7 Overlays / modais

### License Overlay (`#licenseOverlay`)
Splash fullscreen quando `!is_licensed()`. Bloqueia UI inteira.
- Input `#licKeyInput` aceita key.
- Botão Ativar (`activateLicense()`) → POST `/api/license/activate`.
- Mensagem de erro em `#licError`.

### Tech Pause Banner (`#techPauseBanner`)
Banner âmbar fixo no topo quando parada técnica ativa.
- Título "PARADA TÉCNICA".
- Subtítulo dinâmico via `formatPauseDetail(reason)`:
  - Se reason match /Tivemos N perdas consecutivas/: mostra mensagem regulatória.
- Countdown `#tpCountdown` em mm:ss, atualizado a cada 1s.
- Contador de simulação `#tpShadow`: "Simulação: N/M = X% acerto (mínimo 50% pra retomar)".
- Botão "Retomar agora" → `cancelTechPause()` → POST `/api/autoclick/pause {minutes:0}`.

### Green overlay (`#greenOverlay`)
Aparece em hit. Animação CSS + som de moedas (Web Audio API inline em `playMoneyFallSound()`).

---

# 2. Referência das funções backend

## 2.1 `server.py`

### Globais/estado (modulo-level)

```python
state = {
    'crashes': deque(maxlen=2000),
    'crashes_total': int,
    'signals': [], 'session_start': time.time(),
    'ws_connected': bool, 'ws_msgs_received': int, 'last_ws_msg_at': float,
}
autoclick = {
    'config': dict,  # panels + limites
    'stats': {bets_today, pnl_today, loss_streak, last_reset},
    'panels_stats': {0: {wins,losses,ia_approves,ia_wins,ia_losses}, 1: same},
    'events': deque(maxlen=50),  # log events pra UI
    'paused_until': float, 'pause_reason': str,
    'shadow_stats': {0: {bets,wins,losses}, 1: same},
    'panel_cooldowns': {0: {remaining, kind}, 1: same},
    'ia_last_approval': {0: bool, 1: bool},  # rastreia se última aposta foi IA-aprovada
}
intelligence = {'config': {...}, 'state': {...}}
telegram = {'config': {...}}
```

### Funções principais

**`register_crash(value)`** — chamado via `/api/events` pelo snippet.
- Debounce 5s (evita duplicata).
- `state['crashes'].append({'value':value, 'at':now})`.
- `state['crashes_total'] += 1`.
- Call `db.insert_crash`.
- Fecha signals pendentes (compara actual vs target).
- Emit novo signal via `emit_signal()`.
- Auto-trigger inteligência (reavaliação a cada N rodadas se habilitado).

**`emit_signal(crashes)`** — heurística pro target 10x.
- Conta intervalo atual desde último 10x.
- Decide BET / ATENÇÃO / SKIP / OBSERVE.
- Retorna dict ou None.

**`_compute_scenario(crashes, stats)`** — análise de cenário pra pro tab.
- Score de -100 a +100 baseado em: low tail, autocorrelation, etc.
- Classifica: FAVORAVEL, NEUTRO, CAUTELOSO, DESFAVORAVEL.
- Retorna multiplier de aposta, cooldown extra, should_pause.

**`evaluate_target_probability(target, panel, margin_override)`** — CORAÇÃO DA IA. Ver seção 3.

**`get_snapshot()`** — monta resposta de `/api/state`.
- Todas as stats + crashes + cycles.

**`_guard_licensed()`** — middleware. Retorna False + envia 403 se não-licenciado.

**`_send_telegram(text)`** — envia via fila async (retry 3x).

### Handlers HTTP (classe `Handler`)

Roteamento em `do_GET` e `do_POST`. Lista já em HANDOFF.md seção 5.1.

## 2.2 `autoclick_daemon.py`

### Main loop structure
```python
while True:
    try:
        # 1. reconnect se CDP morreu
        # 2. re-inject HELPER_JS a cada 30s
        # 3. fetch config, state, intelligence, stats
        # 4. check license enabled + parada técnica
        # 5. check_stop_conditions (se pnl/streak bateu → trigger_tech_pause)
        # 6. para cada panel em (0, 1):
        #    - calcula cooldown e reporta pro server
        #    - lê phase via CDP
        #    - resolve pending_hits (espera crash da rodada)
        #    - shadow evaluation (durante pausa)
        #    - BETTING PHASE: decide entrada
        #    - FLIGHT PHASE: consume armed result ou fallback cashout
        #    - MISS detection (só apostas reais)
        # 7. sleep 50ms se algum flight ativo, senão 150ms
    except Exception:
        ...
```

### Funções-chave

**`HELPER_JS`** — string JS injetada no iframe do Spribe. Define `window.__aviator`:
- `detectState(panel)` → `{phase, btnText, cashoutValue}`.
- `detectAll()` → `{panel0:..., panel1:...}`.
- `setBetAmount(panel, value)` → ajusta input via DOM.
- `clickBet(panel)` → clica botão de aposta.
- `clickCashout(panel)` → clica cashout.
- **`armCashout(panel, target, betAmount)`** — loop 20ms dentro do iframe que clica cashout assim que `btnValue / betAmount >= target`. Muito mais rápido que CDP roundtrip.
- `checkArmedResult(panel)` → retorna `{clicked,mult,val}` uma vez, depois limpa.
- `cancelArmed(panel)` → cancela timer.

**`should_enter(panel_cfg, api_state)`** — checa trigger (bet_only, gap_threshold, always).

**`in_schedule_window(panel_cfg)`** — verifica se hora atual está na janela HH:MM configurada. Suporta cruzar meia-noite.

**`ia_gate(target, panel, margin)`** — HTTP GET pro server local com query string. Retorna `(approve, probability, pattern_key)`.

**`margin_for_panel(p_cfg)`** — traduz `operationMode` em fração: conservador=0.15, equilibrado=0.10, agressivo=0.05.

**`_register_miss(reason_tag)`** — closure dentro do loop. Posta evento miss, limpa `active_bets[p_idx]`, seta `loss_wait_until`.

**`trigger_tech_pause(minutes, reason, extend)`** — POST `/api/autoclick/pause`.

**`disable_bot_master()`** — legacy, substituída por `trigger_tech_pause` em v1.6.0.

## 2.3 `db.py`

SQLite helpers. Thread-safe via `_lock`.

**`init_db()`** — cria tabelas + índices + migra colunas.

**`insert_crash(value, ts)`** — grava 1 crash.

**`load_recent_crashes(limit)`** — pega últimos N, ordem cronológica.

**`count_crashes()`** — total.

**`insert_bet(panel, target, amount, ia_approved, pattern, probability, ts)`** — registra aposta aberta (sem resolução).

**`resolve_last_open_bet(panel, hit, delta_pnl, ts)`** — atualiza última aposta aberta desse painel.

**`get_panels_stats()`** — agrega wins/losses/ia_* por painel de todas as apostas resolvidas.

**`pattern_bet_history(pattern, target, tol=0.25, limit=30)`** — count + hits + pnl das apostas recentes com esse padrão + target similar.

**`prob_band_hit_rate(panel, target, probability, tol=0.05, ...)`** — LEGACY (não usada após v1.14.0, mas código permanece).

**`recent_ia_performance(panel, limit=75)`** — LEGACY idem.

**`recent_pattern_bets(panel, pattern, target, n=3, tol=0.25)`** — últimas N apostas desse padrão, pra BLACKLIST check.

**`totals()`** — `{crashes:X, bets:Y}`.

## 2.4 `license.py`

**`activate(key)`** — POST pro Vercel, grava cache em `data/license.json`.

**`status()`** — retorna estado atual:
- `{licensed: True, ...}` se tem token válido.
- `{licensed: False, reason}` senão.
- Dispara refresh async a cada 6h.

**`is_licensed()` → bool** — wrapper de status.

**`deactivate()`** — apaga `data/license.json`.

**`_refresh_async()`** — thread daemon que renova token sem bloquear.

## 2.5 `updater.py`

**`check_update()`** — fetch `https://irondome-site.vercel.app/version.json`.

**`install_update()`**:
1. Cria snapshot pre-update em `_snapshots/`.
2. Download ZIP.
3. Extrai em `/tmp`.
4. Copia files sobre o install, **preservando** `data/`, `_snapshots/`, `cert.pem`, `key.pem`.
5. Return `{ok, snapshot}`.

**`schedule_restart(delay_s)`** — thread que espera delay, spawn `restart.sh`, `os._exit(0)`.

---

# 3. IA DEEP DIVE

A seção crítica. Vou explicar passo-a-passo **como a IA decide apostar e
como ela aprende** ao longo do tempo.

## 3.1 Visão geral

A "IA" do IronDome **não é machine learning clássico** (sem rede neural,
sem gradient descent). É um **modelo estatístico explícito** que:

1. **Observa** os últimos 1000 crashes.
2. **Quantiza** em buckets (B/M/A/V).
3. **Procura padrões** na cauda atual vs histórico.
4. **Calcula probabilidade** de a próxima rodada atingir o target.
5. **Aplica aprendizado causal** baseado em apostas reais passadas.
6. **Bloqueia padrões** que estão perdendo consecutivamente.
7. **Decide** aprovar ou rejeitar comparando com threshold.

A "aprendizagem" não está nos pesos de um modelo — está na **tabela `bets`
do SQLite**, que acumula pattern + probability + hit real. A cada aposta
resolvida, essa tabela cresce e afeta decisões futuras.

## 3.2 Input

Função assinatura: `evaluate_target_probability(target, panel, margin_override)`.

| Parâmetro | Tipo | Significado |
|---|---|---|
| `target` | float | Multiplicador alvo (ex: 2.0, 5.0, 10.0). |
| `panel` | int \| None | Painel (0 ou 1). None = avaliação neutra. |
| `margin_override` | float \| None | Margem escolhida pelo user (0.05 / 0.10 / 0.15). |

Data adicional lida de `state['crashes']` (in-memory deque) e `db.bets` (SQLite).

## 3.3 Pipeline da decisão (passo a passo)

### Passo 1 — Baseline observado
```python
last = crashes[-1000:]
hits_tot = sum(1 for v in last if v >= target)
p_base = hits_tot / 1000   # hit rate observado nas últimas 1000 rodadas
```

Para target 2.0: se crashes[-1000] tem 535 valores ≥2.0, `p_base = 0.535`.

O **teórico** (baseline de jogo provably fair) é `theo = 0.99 / target`.
Target 2.0 → theo = 49.5%. Se p_base > theo significa viés recente pra cima.

### Passo 2 — Quantização em buckets
```python
def _bucket(v):
    if v < 1.3: return 'B'    # BAIXO
    if v < 2.0: return 'M'    # MÉDIO
    if v < 5.0: return 'A'    # ALTO
    return 'V'                 # TOPO (≥5x)
```

Cada crash vira 1 letra. `crashes[-1000:]` vira uma string de 1000 chars.

### Passo 3 — Pattern matching
`_find_best_pattern(vals, target, min_support=3)`:

```python
for k in (6, 5, 4, 3, 2):
    pattern = tuple(seq[-k:])  # cauda atual de k buckets
    occurrences = []
    for i in range(len(seq) - k):
        if tuple(seq[i:i+k]) == pattern:
            occurrences.append(vals[i+k])  # crash seguinte
    support = len(occurrences)
    if support < min_support: continue
    hits = sum(1 for v in occurrences if v >= target)
    rate = hits / support
    score = rate * (1 - 1/(support + 1))
    # score balanceia hit rate vs confiança na amostra
```

Escolhe o k com maior score. Ex: cauda `MBAAMV` procura em todo histórico
quantas vezes apareceu `MBAAMV` e o que crashou logo depois.

**Por que score = rate × (1 − 1/(support+1))?** Pra penalizar amostras pequenas:
- support=3: fator = 0.75
- support=10: fator = 0.91
- support=50: fator = 0.98

Padrão com 10/10 hits (rate=100%, support=10) score=0.91.
Padrão com 50/75 hits (rate=67%, support=75) score=0.66.
O primeiro ganha — mesmo com support menor, a hit rate alta é mais relevante.

### Passo 4 — Suavização Bayesiana
```python
K_PRIOR = 5
smoothed = (hits + K_PRIOR × theo) / (support + K_PRIOR)
```

Essa é uma **regra de Laplace/aditivo** com prior centrado no teórico.

Exemplo concreto — padrão `AAB` em target 2.0 (theo=0.495):
- Raw: 8/10 = 80%.
- Smoothed: `(8 + 5×0.495) / (10 + 5)` = `10.475 / 15` = **69.8%**.

Ou raro — padrão `VMB` com 4/5 hits:
- Raw: 80%.
- Smoothed: `(4 + 2.475) / 10` = **64.8%**.

**Para que serve**: se só tem 5 observações, 4/5 = 80% pode ser sorte.
O prior puxa pra o teórico; mais amostra → puxa menos. Aos 30+ amostras,
smoothed ≈ raw.

`p_adj = smoothed`.

### Passo 5 — Aprendizado causal por padrão
Query em `bets` pelas apostas resolvidas recentemente com esse pattern + target:

```python
pattern_history = db.pattern_bet_history(pattern_key, target, tol=0.25, limit=30)
# retorna {count, hits, pnl}
```

Se count ≥ 3:
```python
real_smoothed = (hits + K_PRIOR × theo) / (count + K_PRIOR)
pattern_bet_corr = (real_smoothed - p_adj) * min(1.0, count / 6)
p_adj += pattern_bet_corr
```

**Interpretação**: o hit rate **em apostas REAIS** (com dinheiro) é mais
confiável que o hit rate nas rodadas observadas. Se o padrão `AAB` aparece
no histórico com 70% de acerto, mas em apostas reais acertou só 50%, a IA
aprende que tem viés ali.

O peso cresce com `min(1.0, count/6)`: 3 apostas = 50% peso, 6+ = 100%.

Exemplo:
- p_adj = 70% (smoothed do pattern histórico).
- Apostas reais com esse pattern+target: 6 apostas, 3 hits = 50%.
- real_smoothed = (3 + 5×0.495) / (6 + 5) = 5.475 / 11 = 49.8%.
- pattern_bet_corr = (49.8% - 70%) × 1.0 = **-20.2pp**.
- p_adj = 70% - 20.2% = **49.8%**.

### Passo 6 — Pattern blacklist (v1.14.0 NOVA)
Query as últimas 3 apostas resolvidas desse pattern+target pro painel:

```python
last3 = db.recent_pattern_bets(panel, pattern_key, target, n=3)
if len(last3) == 3 and all(r['hit'] == 0 for r in last3):
    oldest_ts = min(r['ts'] for r in last3)
    if time.time() - oldest_ts < 7200:  # 2 horas
        pattern_blacklisted = True
        # força decision='reject' independente da prob
```

**Lógica**: se o padrão perdeu 3× consecutivas nas últimas 2h, ele entra em
timeout. Não aposta nesse padrão específico. OUTROS padrões continuam normais.

É granularidade máxima — isola o problema sem punir tudo.

### Passo 7 — Critério final
```python
MARGIN_BASE = float(margin_override) if margin_override is not None else 0.10
ABSOLUTE_FLOOR = 0.25
approve_threshold = max(theo * (1 + MARGIN_BASE), ABSOLUTE_FLOOR)

if pattern_blacklisted:
    decision = 'reject'
else:
    decision = 'approve' if p_adj >= approve_threshold else 'reject'
```

Exemplos:
- Target 2x, modo equilibrado: threshold = max(0.495 × 1.10, 0.25) = **0.545** (54.5%).
- Target 5x, modo agressivo: threshold = max(0.198 × 1.05, 0.25) = **0.25** (piso ativa!).
- Target 10x, modo conservador: threshold = max(0.099 × 1.15, 0.25) = **0.25**.

**Piso absoluto 25%** impede apostas em targets muito altos com probabilidade
absurdamente baixa. Ex: target 50x, teórico 2%. Sem piso, critério = 2.1% — aprovaria
qualquer pattern match >=2.1%. Com piso 25%, exige probabilidade 25% real.

## 3.4 Output da função

```python
return {
    'target': target,
    'probability': round(p_adj, 4),
    'probability_raw': round(p_base, 4),
    'pattern': {k, pattern, support, hits, rate},
    'pattern_bet_history': {count, hits, pnl},
    'tail': 'BMVBAM',  # string da cauda atual
    'theoretical': round(theo, 4),
    'approve_threshold': round(approve_threshold, 4),
    'blacklisted': bool,
    'blacklist_info': {pattern, losses_in_row, until_ts} | None,
    'decision': 'approve' | 'reject',
    'reasoning': [lines],
    'sample_size': n,
    'ia_correction': 0.0,  # legacy, sempre 0 em 1.14.0
    'pattern_bet_corr': round(pattern_bet_corr, 4),
}
```

O campo `reasoning` é array de strings pra UI mostrar — cada camada que afetou
a decisão adiciona uma linha.

## 3.5 Como a IA APRENDE ao longo do tempo

A aprendizagem é **incremental, baseada no DB**, sem re-treinar modelo.

### Ciclo de feedback (simplificado)

```
┌────────────────┐
│ aposta colocada │
│ (pattern, prob, │
│  target salvos) │
└────────┬────────┘
         ↓
┌────────────────┐
│ rodada resolve │
│ (hit ou miss)  │
└────────┬────────┘
         ↓
┌──────────────────────┐
│ bets table atualizada│
│ (hit=1 ou 0)         │
└────────┬─────────────┘
         ↓
┌──────────────────────────────┐
│ próxima avaliação do mesmo   │
│ pattern+target consulta esse │
│ histórico e ajusta p_adj     │
└──────────────────────────────┘
```

### 3 mecanismos de aprendizado ativos em v1.14.0

**1. Pattern matching refinado** (observacional)
- Cada crash novo entra em `state['crashes']`.
- Próximo `_find_best_pattern` encontra uma cauda ligeiramente diferente (o último bucket mudou) ou suporte ligeiramente maior.
- O algoritmo é "memoryless" no sentido de que sempre olha 1000 últimas rodadas, mas a base evolui.

**2. Pattern bet history** (causal)
- Cada aposta real é gravada em `bets` com o pattern usado.
- `pattern_bet_history(pattern, target)` query futura agrega hits/misses.
- Correção `pattern_bet_corr` escala com count até 6.
- Se um pattern historicamente favorável começa a falhar em apostas reais,
  `real_smoothed` cai → `p_adj` cai → IA rejeita mais.

**3. Pattern blacklist temporário** (emergencial)
- Se 3 losses consecutivas naquele pattern+target nas últimas 2h → block.
- Blackjack matemático local: "algo mudou nesse pattern agora, melhor evitar".
- Self-healing: se 2h passam e não há novas apostas no pattern (porque bot
  não aposta mais nele), sai do timeout naturalmente. Se user ignora e aposta
  manualmente e ganha, a próxima query não terá 3 perdas → sai do timeout.

### O que foi REMOVIDO em v1.14.0 (importante pro handoff)

O sistema anterior tinha 3 camadas adicionais que causavam feedback loop negativo:

**CALIBRAÇÃO por faixa de prob** (REMOVIDA):
- Media "IA é overconfident nessa faixa de %?"
- Problema: amostras pequenas por faixa, ruído alto.

**APRENDIZADO GLOBAL por painel** (REMOVIDA):
- Compara `ia_wins/resolved` com teórico pra TODOS os padrões.
- Problema: punia padrões bons junto com padrões ruins.

**AUTO-AJUSTE** (REMOVIDA):
- Escalava margem/piso baseado em `pnl_per_unit` das últimas 75 apostas.
- Problema: FEEDBACK LOOP NEGATIVO. Perde → aperta → para de apostar → pnl
  estagnado negativo → nunca relaxa → bot nunca volta.

Esses 3 foram substituídos pelo **pattern blacklist** que é targeted (só mata
o padrão específico) em vez de global (que travava tudo).

## 3.6 Como você (agent futuro) pode evoluir a IA

### Ideias implementáveis

**Beta-Binomial distribution** (melhora quantitativa):
- Em vez de suavização com K_PRIOR=5 fixo, usar posterior Beta(α+h, β+n-h)
  com α=β adaptativos.

**Hierarchical prior by target**:
- Padrões em target 2x e 10x têm dinâmicas diferentes. Priors separados.

**Feature expansion**:
- Adicionar: tempo entre 10x, volatilidade recente (std dev), autocorr.
- Combinar via regressão logística treinada com sklearn offline, embarcar pesos.

**Thompson sampling**:
- Cada (pattern, target) vira um braço com posterior Beta.
- Amostra da posterior pra decidir.
- Auto-explora braços novos, explora menos braços ruins.

**Bankroll-aware sizing**:
- Kelly fraction em cima da prob estimada.
- Stake dinâmico proporcional ao edge.

### Ideias ruins (evitar)

- **Cascata de correções**: foi o que removemos. Não recriar.
- **Aprendizado global**: muito grosseiro. Foca em padrões ou prob bands.
- **Thresholds hardcoded por target**: melhor generalizar via modo user.
- **Memory sem janela rolante**: se observa "forever", drifts antigos poluem.

## 3.7 Exemplo concreto de evaluate_target_probability

Cenário:
- Target: 2.0
- Panel: 0
- Modo: equilibrado (margin=0.10)
- Cauda atual: `AAMBVA`
- Histórico: 1000 crashes com baseline 53.5%
- Pattern match (k=3): `MBVA` apareceu 12×, crash seguinte ≥2x em 7 → rate 58.3%
- Apostas reais passadas com `MBVA`+target 2x: count=0 (ainda sem histórico)
- Blacklist: não
- 3 últimas com `MBVA`: não há

Pipeline:
```
1. p_base = 0.535
2. best = {k:3, pattern:'MBVA', support:12, hits:7, rate:0.583, score:0.538}
3. smoothed = (7 + 5×0.495) / (12+5) = 9.475/17 = 0.557 (55.7%)
4. p_adj = 0.557
5. pattern_history.count = 0 → sem correção causal
6. blacklist = False
7. threshold = max(0.495 × 1.10, 0.25) = 0.545
8. p_adj (0.557) >= threshold (0.545) → APROVA
```

Reasoning:
```
Janela: últimas 1000 rodadas — baseline observado 53.5% (teórico 49.5%)
Cauda atual (6): ALTO → ALTO → MÉDIO → BAIXO → TOPO → ALTO  [AAMBVA]
PADRÃO casado (k=3): MBVA apareceu 12× → hit rate bruto 58.3% · suavizado 55.7%
Probabilidade final: 55.7%. ✓ APROVA (critério: ≥ 54.5% = teórico 49.5% + 10% margem)
```

## 3.8 Performance e limites

- `evaluate_target_probability` roda em ~5-15ms (janela 1000, 5 tamanhos de pattern).
- Chamada a cada gate check (3s no UI poll, 50-150ms no daemon durante flight).
- Overhead total: ~1-3% CPU em idle, 5-10% durante rodadas ativas.
- Memory: deque 2000 crashes ≈ 80KB, DB cresce ~1MB/semana em operação ativa.

## 3.9 Como testar a IA sem apostar

```bash
# Avaliar um target sem conectar no cassino — só precisa ter crashes no DB
curl -sk "https://localhost:8766/api/intelligence/gate?target=2&panel=0&margin=0.10" \
  | python3 -m json.tool

# Listar top padrões ativos
curl -sk https://localhost:8766/api/analytics/ia | python3 -m json.tool
```

Resposta mostra o reasoning completo. Útil pra entender o que a IA tá vendo.

---

**Fim da referência técnica.** Combine com `HANDOFF.md` + `CHANGELOG.md` + o
próprio código. Qualquer função não coberta aqui, o source tem docstring.
