# AGENT_PRESERVE — o que nunca regressar (LA StressLab)

Documento permanente para o agente Cursor. Atualizar quando uma falha de bancada virar regra.

**Versão de referência:** 1.1.75 (2026-07-29)  
**Entrada:** [AGENTS.md](../AGENTS.md) · regras em `.cursor/rules/`

---

## 1. Auto-update / OTA

### O que já quebrou
| Sintoma | Causa raiz | Fix |
|---------|------------|-----|
| Sem popup de update em ≤1.1.55 | Manifesto assinado só com HMAC v2 | Publish assina com **PREVIOUS (v1)** enquanto ponte ativa |
| Download 100% → shell some → não atualiza | `update_apply.ps1` UTF-8 sem BOM + `—` Unicode → parse PS 5.1 morre | ASCII + **BOM UTF-8**; launcher re-BOM |
| Log trava em `LAUNCH invoking powershell` (pasta com espaço no user) | `start` + aspas aninhadas em `-AppDir` quebram path | Launch via **`update_apply.bat` argv list**; `wait_pid` no pending; PS não apaga o log |
| `update_apply.log` ausente | Script nem chega a executar | Parse/BOM |
| R2 403 | Sem User-Agent | Sempre UA `LAStressLab/<ver>` + fallback |
| App **fecha inteiro** no meio do Hard Stress / stress | Popup OTA no meio do teste → download → `destroy()` + `SystemExit` | **Adiar** prompt/apply enquanto `running`; oferecer só após o fim do teste |

### Preservar (NUNCA regressar)
- `publish_update.py`: se `UPDATE_MANIFEST_HMAC_SECRETS_PREVIOUS` não vazio → assinar com `PREVIOUS[0]`
- Cliente: aceitar CURRENT + PREVIOUS (`update_check._verify_manifest_signature`)
- Não remover PREVIOUS até frota ≥ build dual (1.1.56+) estável
- `update.json` no USB (`manifest_url`, `check_on_startup`)
- Smoke apply em pasta real (USB) **e pasta com espaço no path** antes de confiar no publish
- **Nunca** `download_stage_and_apply` / `SystemExit` de OTA com teste `running`
- **Nunca** relançar OTA com `start … powershell -AppDir ""path""` aninhado — só `Popen([cmd,/c,bat,appdir,staging,pid,exe])`
- **Nunca** `Set-Content` vazio no `update_apply.log` no início do PS1
- `wait_pid` sempre em `update_pending.json`
- Unit test `tools.test_update_apply_launch` (path com espaço) deve passar
- Não alterar `update_check.py` / `update_apply.ps1` / HMAC em fix de GPU/veredito sem re-testar OTA

### Arquivos críticos
`branding.py` · `publish_update.py` · `update_check.py` · `update_apply.ps1` · `update_apply.bat` · `.cursor/rules/ota-powershell.mdc`

---

## 2. Telegram

### Dois canais
1. **DUMPS (oficina/ADM)** — `dump_delivery.json` no pendrive; vai no zip; token/chat ADM.
2. **Live INÍCIO/FIM (cliente)** — `telegram.json` em `%LOCALAPPDATA%\LAStressLab\` (+ mirror ao lado do EXE); **não** vai no zip.

### O que já quebrou
| Sintoma | Causa | Fix |
|---------|-------|-----|
| Live ok no PC do dev, morto na bancada | Pareamento só no AppData do outro PC + licença | Conectar de novo; licença ativa |
| INÍCIO/FIM/DUMP SSL fail | AV inspeção HTTPS | `ssl_util` + store Windows |
| DUMPS silenciosos | AppData stale / skip sem log | USB manda dumps.*; Eventos loga skip |
| Relatório storage sem disco | Caption só CPU/RAM/GPU | Bloco Armazenamento + operador e-mail |

### Preservar
- Zip **sem** `telegram.json`, `license.json`, `user_profile.json`
- Zip **com** `dump_delivery.json` + `update.json`
- OTA apply: preservar pairing; merge ADM dumps sem apagar results
- Demo: bloqueia live; dumps ADM podem ir
- Captions: **Operador** (e-mail licença) + **Armazenamento** (tipo HDD/SSD/NVMe)

### Arquivos críticos
`dump_delivery.py` · `ssl_util.py` · `app_config.py` · `workshop_dump.py` · `app.py` (hooks Telegram)

---

## 3. Veredito / disco / RAM / GPU

### O que já quebrou (GPU / Hard Stress)
| Sintoma | Causa raiz | Fix |
|---------|------------|-----|
| Cancelou Hard Stress antes da Varredura → popup **FALHOU** + tip “escrito≠lido / RMA” | Motivo soft (“nenhum ciclo…”) ia para `errors[]` e na reavaliação virava hard FAIL; tip de FAIL GPU era genérico | Soft incompleto/cancel ≠ FAIL; tip escrito≠lido **só** com erro real de Varredura; não semear `errors` com reasons de INCONCLUSIVO |
| “Falhas durante o teste” critical só por “nenhum ciclo” | `looks_like_soft_incomplete_gpu` não reconhecia cobertura/nenhum ciclo | Ampliar matcher soft |
| Hard Stress Aberto 10–15 min fecha no meio (donut) | Duração global padrão (ex.: 5 min) dispara `Duração concluída` → `stop_event` mata a janela | Ao ligar Hard Stress / iniciar GPU: duração ≥ Hard + reserva Varredura/Hold; não abortar enquanto fase `hard_stress` |

### Preservar
- Endurance/saúde % = desgaste, **não** hard-FAIL isolado
- FAIL disco: spare, media_errors, verify, deltas SMART, **self-test Short FALHOU neste ciclo**
- Histórico de Error Log / self-test antigo = aviso (não FAIL sozinho)
- OOM RAM → INCONCLUSIVO (não falso “escrito≠lido”)
- Capacidades diferentes (ex. 8+16) → **tarja de aviso** na aba Memória; tip par/IMC **só** com evidência de stress (erro/interrupção). Inventário sozinho **nunca** FAIL
- CPU residual GEMM/FFT (perfil Estabilidade) → FAIL; OOM no bloco CPU → INCONCLUSIVO
- Throttle CPU ≠ FAIL; tip Prime95/Linpack externo = dica (nunca embutir binário)
- Inventário SSD não polui run só-Memória
- Soft telemetry ≠ FAIL se gatekeeper PASS/INCONCLUSIVO
- Fase IMC/bandwidth (stress térmico) **não** é FAIL de DIMM sozinha
- Tip MemTest86+ boot = dica de bancada (nunca embutir ISO no zip)
- “Varredura VRAM” na UI (não “MATS” para o técnico)
- **Cancel / Hard Stress interrompido / nenhum ciclo Varredura / cobertura insuficiente → INCONCLUSIVO ou INTERROMPIDO, nunca FALHOU de VRAM**
- **Tip “escrito≠lido / suspeita VRAM / RMA” só com ciclo(s) de Varredura com erro real** (não quando `não testada`)
- Reasons de INCONCLUSIVO/INTERRUPTED **não** entram em `errors[]` como hard
- Hard Stress cancelado pelo operador = aviso tip, **não** FAIL sozinho
- Duração global ≥ Hard Stress + reserva **enxuta** (Hold~5 + Varredura~3), não +20 fixo
- Timer não corta Hard Stress / Varredura / Hold no meio
- **OTA/HMAC/BOM:** não alterar `update_check.py` / `update_apply.ps1` / assinatura PREVIOUS em fix de veredito/GPU

### Arquivos críticos (veredito GPU)
`error_match.py` · `gpu_verdict.py` · `run_verdict.py` · `result_popup.py` · `diagnostics.py` · `app.py` (`_finish_test` · duração Hard Stress)

---

## 4. Pacote comprador vs bancada

| Item | Nuvem (zip) | USB oficina após uso |
|------|-------------|----------------------|
| Licença | vazia | pode ter `license.json` da máquina |
| Telegram live | vazio | `telegram.json` se pareou |
| Perfil | vazio | `user_profile.json` |
| DUMPS ADM | preenchido | preenchido |
| Update | `update.json` | idem |

Comprador: ativa licença + Conectar Telegram. Dumps ADM já apontam pro bot da oficina.

---

## 5. Ritual de release (obrigatório)

1. Bump `APP_VERSION` em `branding.py`
2. `docs/changelogs/X.Y.Z.md`
3. Linha no topo de `docs/CHANGELOG.md`
4. Atualizar `docs/PROXIMOS_UPDATES.md`
5. Testes da release (+ regressões se tocou OTA/Telegram/veredito)
6. `build.bat` → confirmar BOM em `dist/.../update_apply.ps1`
7. `python publish_update.py --upload --notes "…"`
8. Conferir `latest.json` público

---

## 6. Arc recente (1.1.54 → 1.1.61) — resumo

| Ver | Destaque |
|-----|----------|
| 1.1.54 | Varredura VRAM; NVMe endurance≠FAIL |
| 1.1.55 | RAM OOM≠FAIL; inventário SSD não polui Memória |
| 1.1.56 | Erros de hardware (Eventos); HMAC v2 dual |
| 1.1.57 | Ponte HMAC (assinatura v1); Telegram por PC; dumps USB |
| 1.1.58 | HTTPS Windows store (AV SSL) |
| 1.1.59 | Telegram: disco + e-mail operador |
| 1.1.60 | Fix OTA apply (PS1 BOM) |
| 1.1.61 | Self-test Short + logs SMART (bancada) |
| 1.1.64 | Hard Stress cenas Pixel/Tessela |
| 1.1.65 | Cancel/Hard Stress sem Varredura ≠ FALHOU; tip RMA só com erro real |
| 1.1.66 | Hard Stress duração + OTA defer mid-teste |
| 1.1.67 | Memória: perfis Rápido/Completo/Caça; Passes; tip MemTest86+ |
| 1.1.68 | Liberar memória async; aba RAM sem trava SPD; UI limpa |
| 1.1.69 | OTA bat argv (path com espaço); RAM MemTest64-like Completo/Caça |
| 1.1.70 | CPU Térmico/Estabilidade (FFT/GEMM residual Linpack-like) |

Links estáveis:
- Manifesto: `https://pub-1aa9e6ed708a4a359982f06ac3f304e9.r2.dev/latest.json`
- Zip atual: `https://pub-1aa9e6ed708a4a359982f06ac3f304e9.r2.dev/LAStressLab-<versão>.zip`
