# Supervisão, recuperação e replay

Supervisione falhas cooperativas por app e reproduza RPC, relógio e aleatoriedade com diários limitados.

Estas APIs pertencem ao host e funcionam com o singleton ou uma instância privada `new WuCore()`. Importe suas funções explicitamente. O core libera supervisores e sessões ao executar `destroy()`.

<span class="section-anchor" id="section-1"></span>

## superviseApp

`superviseApp(core, appName, options?): WuHealthHandle`

A app deve estar registrada e só pode ter um supervisor. Relate falhas com `handle.report(error)` pelo host ou `context.wu.reportError(error)` pela app. Ao atingir o limite, o supervisor revoga o scope, desmonta a app e mostra um fallback; tenta recuperá-la no mesmo contêiner e restaurar suas props.

```js
import { superviseApp } from 'wu-framework/health';
// runtime already registered checkout; #checkout-slot exists.
const health = superviseApp(runtime, 'checkout', {
  threshold: 3, windowMs: 30000, retries: 2, delay: 1000, maxDelay: 30000,
  onState: status => console.log(status.state),
  fallback: element => { element.textContent = 'Checkout unavailable'; },
});
await runtime.mount('checkout', '#checkout-slot');
// Teardown when the owner of this supervisor exits:
health.stop();
```

Os valores do exemplo são os padrões. `threshold` e `retries` são inteiros; `windowMs` mede a janela de falhas. A recuperação usa atraso exponencial limitado por `maxDelay` e consome no máximo `retries` tentativas durante a vida do supervisor. `onState(status)` observa transições; `fallback(element, status)` personaliza a mensagem.

`status()` retorna `{ appName, state, attempts, failures, error? }`. Os estados são `healthy`, `quarantined`, `recovering`, `exhausted` e `stopped`. `isBlocked()` é verdadeiro em quarentena ou esgotamento: montagem manual rejeita com `WU_APP_QUARANTINED`. `settled()` aguarda a tarefa atual, não todos os atrasos de novas tentativas. `stop()` cancela a recuperação e remove o fallback; não remonta a app.

A supervisão exige erros reportados e código cooperativo. Não interrompe um loop infinito nem detecta automaticamente todas as exceções do navegador. Um segundo supervisor falha com `WU_HEALTH_ALREADY_SUPERVISED`; uma app desconhecida, com `WU_APP_NOT_REGISTERED`.

<span class="section-anchor" id="section-2"></span>

## createReplaySession

`createReplaySession(core, { apps, mode?, journal?, maxEntries?, maxBytes? }): WuReplaySession`

Registra chamadas scoped `request`, `now` e `random` para apps registradas. Use `context.wu.now()` e `context.wu.random()` no código reproduzível. O modo padrão é `record`, com 5000 entradas e 2 MiB; sessões não podem se sobrepor para a mesma app.

```js
import { createReplaySession } from 'wu-framework/replay';
import 'wu-framework/timeline';
// checkout performs its scoped operations during its awaited lifecycle.
const recording = createReplaySession(runtime, { apps: ['checkout'] });
await runtime.mount('checkout', '#checkout-slot');
const journal = await recording.export({ timeline: true });
recording.stop();
await runtime.unmount('checkout', { force: true });

const playback = createReplaySession(runtime, { apps: ['checkout'], mode: 'replay', journal });
try {
  await runtime.mount('checkout', '#checkout-slot');
  playback.assertComplete();
} finally {
  playback.stop();
}
```

Durante replay, app, operação e entrada devem coincidir, incluindo canal, dados e versão RPC. Retorna o resultado ou erro gravado sem chamar o provider real. Uma diferença lança `WU_REPLAY_MISMATCH`; dados não JSON produzem `WU_REPLAY_VALUE`, e exceder limites produz `WU_REPLAY_LIMIT`. As entradas são reproduzidas na ordem de invocação, não de conclusão.

`status()` informa `{ mode, entries, cursor, pending, stopped, invalid }`. `export()` retorna uma promessa do formato `wu-replay/1` e rejeita enquanto houver RPC pendentes ou o diário for inválido. `export({ timeline: true })` inclui o timeline do mesmo core; com a entrada core, importe timeline explicitamente. `assertComplete()` exige ausência de chamadas pendentes e entradas de replay não consumidas. `stop()` libera interceptores e permite chamar providers reais novamente.

O diário não captura `fetch`, `Date.now`, `Math.random`, timers, DOM nem efeitos externos diretos. Também não restaura automaticamente o estado inicial nem executa o timeline importado. Prepare as mesmas entradas e estado antes de reproduzir. Diários podem conter dados da aplicação: controle armazenamento e acesso. Replay não é rollback nem auditoria durável.

<span id="evidencia-y-lifecycle"></span>

<span class="section-anchor" id="section-3"></span>

## Evidências e lifecycle

Implementação: `superviseApp` em `src/core/wu-health.js`, `createReplaySession` em `src/core/wu-replay.js` e facade em `src/core/wu-app-scope.js`. Testes: `tests/unit/app-health-supervision.test.js`, `tests/unit/replay-journal.test.js`, `tests/unit/replay-session.test.js` e `tests/unit/subsystem-lifecycle.test.js`.