# Compare a shadow version

Run a hidden twin, compare its WU outputs and understand the comparison boundaries.

This page describes WU Framework `4.0.0`. Read the [Framework 4.0 and CLI 1.2 migration guide](/docs/guides/releases/) before upgrading an existing project.

The primary app stays visible and mounted. `wu.shadow` mounts a hidden, inert twin, replicates inputs crossing WU and records its facade outputs. Use it to compare a rewrite or update before replacing the visible app.

```js
import { wu } from 'wu-framework/core';
import 'wu-framework/shadow';

// cart must already be registered and mounted.
await wu.shadow.start('cart', { url: '/apps/cart-v2/', rpcTimeout: 5000 });
try {
  await wu.update('cart', { currency: 'EUR' });
  const report = wu.shadow.report('cart');
  console.log(report.equal, report.dom, report.outputs);
} finally {
  await wu.shadow.stop('cart');
}
```

<span id="entradas-y-salidas"></span>

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

## Inputs and outputs

The twin receives props, events and store reads. Its writes use a private overlay. Its RPC or contract calls receive recorded primary responses for the same channel and arguments. Twin `emit`, `handle` and `provide` operations are recorded without publishing their effects through WU.

Comparison covers facade operations; it does not intercept every browser side effect. A direct `fetch` or external write performed by twin code remains that app responsibility. It also does not replay user clicks or prove visual layout equivalence.

<span id="leer-el-informe"></span>

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

## Read the report

`report().dom` compares normalized trees without internal attributes; `outputs` compares recorded operations and arguments. `equal` means those comparisons found no differences. `unmatchedCalls` identifies calls without a match; after `rpcTimeout` they reject with `WU_SHADOW_NO_TWIN_CALL`. This is not automatic deployment approval.

`start` returns the first report; `report` returns the current report or `null`; `stop` releases the twin, hooks and waits and returns the final report. `sessions` lists sessions. The primary must be mounted; a second shadow fails with `WU_SHADOW_EXISTS`. The twin keeps primary grants and registers as `<app>~shadow` unless you configure `name`.

<span id="interceptar-operaciones-del-host"></span>

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

## Intercept operations from the host

```js
const release = wu.intercept('cart', (kind, args, next) => {
  if (kind === 'store.set') console.log('cart writes', args[0]);
  return next(...args);
});
// Remove the middleware when the host no longer needs it.
release();
```

`intercept` returns a cleanup function. `next(...args)` continues the operation; returning another result replaces it. Multiple interceptors run in registration order. Preserve the synchronous or asynchronous contract expected by the app.

The shadow report includes `props.failures` when a props update does not reach the twin. While those failures exist, `equal` is `false`; do not interpret a partial comparison as deployment equivalence.