# Supervision, recovery and replay

Supervise cooperative per-app failures and replay RPC, clock and randomness with bounded journals.

These APIs belong to the host and work with the singleton or a private `new WuCore()` instance. Import their functions explicitly. The core releases supervisors and sessions when `destroy()` runs.

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

## superviseApp

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

The app must be registered and can have only one supervisor. Report failures through `handle.report(error)` from the host or `context.wu.reportError(error)` from the app. At the threshold, the supervisor revokes the scope, unmounts the app and shows a fallback; it attempts to recover in the same container and restore its 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();
```

The example values are the defaults. `threshold` and `retries` are integers; `windowMs` measures the failure window. Recovery uses exponential delay capped by `maxDelay` and consumes at most `retries` attempts over the supervisor’s lifetime. `onState(status)` observes transitions; `fallback(element, status)` customizes the message.

`status()` returns `{ appName, state, attempts, failures, error? }`. States are `healthy`, `quarantined`, `recovering`, `exhausted` and `stopped`. `isBlocked()` is true in quarantine or exhaustion: manual mounting rejects with `WU_APP_QUARANTINED`. `settled()` awaits the current task, not all future retry delays. `stop()` cancels recovery and removes the fallback; it does not remount the app.

Supervision requires reported errors and cooperative code. It does not interrupt an infinite loop or automatically detect every browser exception. A second supervisor fails with `WU_HEALTH_ALREADY_SUPERVISED`; an unknown app fails with `WU_APP_NOT_REGISTERED`.

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

## createReplaySession

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

Records scoped `request`, `now` and `random` calls for registered apps. Use `context.wu.now()` and `context.wu.random()` in replayable code. The default mode is `record`, with 5000 entries and 2 MiB; sessions cannot overlap for the same 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();
}
```

During replay, app, operation and input must match, including RPC channel, data and version. It returns the recorded result or error without calling the real provider. A difference throws `WU_REPLAY_MISMATCH`; non-JSON data produces `WU_REPLAY_VALUE`, and exceeding limits produces `WU_REPLAY_LIMIT`. Entries replay in invocation order, not completion order.

`status()` reports `{ mode, entries, cursor, pending, stopped, invalid }`. `export()` returns a promise for the `wu-replay/1` format and rejects while RPC calls are pending or the journal is invalid. `export({ timeline: true })` includes the same core’s timeline; with the core entrypoint, import timeline explicitly. `assertComplete()` requires no pending calls or unconsumed replay entries. `stop()` releases interceptors and allows calls to real providers again.

The journal does not capture direct `fetch`, `Date.now`, `Math.random`, timers, DOM or external effects. It also does not automatically restore initial state or execute the imported timeline. Prepare the same inputs and state before replaying. Journals may contain application data: control storage and access. Replay is not rollback or durable auditing.

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

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

## Evidence and lifecycle

Implementation: `superviseApp` in `src/core/wu-health.js`, `createReplaySession` in `src/core/wu-replay.js` and facade in `src/core/wu-app-scope.js`. Tests: `tests/unit/app-health-supervision.test.js`, `tests/unit/replay-journal.test.js`, `tests/unit/replay-session.test.js` and `tests/unit/subsystem-lifecycle.test.js`.