# Actualização DEV — harness autónomo no CT

Data: 2026-08-25  
Estado: activado e validado

## Objectivo

Executar os 30 scans expostos inteiramente a partir do CT DEV, mesmo com o Mac desligado. Os
valores de referência continuam a ser lidos de PROD apenas por `SELECT` e seguem em memória pelo
pipe exportador -> benchmark; não são gravados em ficheiro nem aparecem no relatório.

O holdout não é uma opção do runner autónomo. Continua bloqueado no código e só pode ser aberto no
gate final pelo fluxo explícito já existente.

## Ficheiros

- `/Volumes/www/develop/scripts/pataniscai_benchmark_export.php`: aceita credenciais externas,
  valida permissões do ficheiro e recusa arrancar se a conta tiver qualquer grant fora de `USAGE`
  mais `SELECT` nas duas tabelas do corpus.
- `/Volumes/www/develop/scripts/pataniscai_benchmark_ct.sh`: runner DEV, pipe efémero, lock de
  concorrência e modo background.

## Alteração PROD executada com autorização

Foi criado `pataniscai_benchmark@192.168.4.2` com password aleatória e apenas estes grants:

```sql
GRANT SELECT (id, mai, nif, id_processo, duration_ms, submetido_em, sucesso, nif_divergente)
ON segunor.ocr_corpus_scans TO 'pataniscai_benchmark'@'192.168.4.2';
GRANT SELECT (id_scan, campo, valor_final, valor_ocr)
ON segunor.ocr_corpus_campos TO 'pataniscai_benchmark'@'192.168.4.2';
```

Não conceder `SELECT` sobre a BD inteira nem `GRANT OPTION`. O próprio exportador consulta
`SHOW GRANTS` e aborta se o âmbito divergir. A rota do CT para PROD usa origem
`192.168.69.20`; confirmar com a primeira ligação e remover imediatamente a conta se não casar.

O kernel do CT escolhe `192.168.69.20`, mas o MariaDB registou a origem efectiva como
`192.168.4.2` devido ao NAT VPN. A primeira tentativa limitada ao IP anterior foi recusada e o
rollback removeu tanto a conta como o ficheiro DEV; a segunda criação usou o endpoint observado.
Não foi aberta qualquer sub-rede.

## Configuração DEV instalada

Foi criado `/etc/sabichao/pataniscai_benchmark.ini` com `root:www-data` e modo `0640`:

```ini
host=10.0.30.253
port=3306
user=pataniscai_benchmark
password="PASSWORD_ALEATORIA"
database=segunor
```

O exportador exige este caminho exacto, proprietário `root` e ausência de escrita pelo grupo ou
qualquer acesso por outros utilizadores.

## Execução após activação

Foreground:

```bash
/var/www/develop/scripts/pataniscai_benchmark_ct.sh development
```

Background, independente da sessão SSH:

```bash
/var/www/develop/scripts/pataniscai_benchmark_ct.sh --background
```

O modo background imprime PID e caminho de um log `0700` sob `/var/tmp/pataniscai-benchmark`.
Esse log contém apenas IDs de scan, métricas e diagnósticos redigidos. Um `flock` impede duas
medições simultâneas.

## Verificações já executadas

- PHP 7.3: lint limpo no exportador, benchmark e cliente;
- exportador: 5/5 guardas sintéticas (incluindo escrita, âmbito alargado e `GRANT OPTION`);
- benchmark: selftest sanitizado `SELFTEST: OK`;
- cliente pataniscAI: 46/46 fixtures;
- `bash -n` limpo;
- runner recusa holdout com exit 2 e configuração ausente com exit 3;
- nenhum utilizador MySQL anterior aceitava a origem DEV;
- ligação real: 1 scan de referência e 22 campos recebidos pelo pipe, sem imprimir valores;
- processo background iniciado como `www-data` (PID inicial 765936), independente da sessão SSH;
- o primeiro scan concluiu depois de a sessão de arranque ter terminado;
- protecções reais: holdout recusado com exit 2; segunda execução simultânea recusada com exit 5.

## Gate de activação

Reprodução concluída nos 30 scans expostos, sem fallback nem falhas: Tesseract 14 erros/58 vazios,
Paddle 5 erros/96 vazios; média Paddle 32944 ms e p95 49584 ms (Tesseract 40090/83156 ms). Reproduz
a medição aceite de 5/96 e 32,5/49 s dentro da variação normal de inferência. Harness concluído.

## Rollback

1. Remover `/etc/sabichao/pataniscai_benchmark.ini` no CT DEV.
2. Executar `DROP USER 'pataniscai_benchmark'@'192.168.4.2'` em PROD.
3. O runner volta a falhar antes de qualquer leitura. O benchmark pelo pipe do Mac permanece
   disponível como caminho anterior.
