1# COMPARISON — detector (T8) vs escrow (T9)
2
3Los dos mecanismos sobre los **mismos escenarios**, misma cuenta (saldo inicial
4100, invariante `saldo ≥ 0`, slack `S = 100`). Los veredictos de esta tabla son
5exactamente los que verifica `tests/comparison.rs` — ninguno está escrito a mano.
6
7- **Detector (T8):** funde los estados de las réplicas y comprueba el merge
8 (`AccountConfig::haz_coordination`). Detección **reactiva por-estado**.
9- **Escrow (T9):** reparto de presupuestos `[50,50]` (válido: `Σ ≤ 100`); cada
10 réplica gasta local si le cabe (`try_spend`). **Evitación por construcción**.
11
12| # | Escenario | Grafo | Retiros | Detector (T8) | Escrow `[50,50]` (T9) |
13|---|---|---|---|---|---|
14| 1 | conectado, caben | `0—1` | `10,10` | corre libre | **Local** (ambos ≤ 50) |
15| 2 | conectado, sobregiro | `0—1` | `60,60` | coordina | **rebalanceo** (60 > 50) |
16| 3 | **particionado, sobregiro** | `∅` | `60,60` | **corre libre** ⚠️ | **rebalanceo** (60 > 50) |
17| 4 | particionado, caben | `∅` | `10,10` | corre libre | Local |
18
19## La fila 3 es todo el punto de T9
20
21**El detector dice "corre libre" al caso más peligroso.** Con las dos réplicas
22particionadas retirando 60 cada una de una cuenta de 100, el saldo global va a
23`100 − 120 = −20`: viola el invariante. Pero el detector, al codificar una
24restricción **global** por aristas **pares**, se queda sin ninguna arista al
25particionar y no ve el acoplamiento. **Falso-verde: el caso inseguro reportado
26como seguro** — el peor error posible en un consejero de coordinación.
27
28**El escrow lo bloquea correctamente.** El presupuesto de cada réplica es 50; un
29retiro de 60 devuelve `NeedsRebalance{deficit:10}`, **con o sin partición**,
30porque el presupuesto `b_i` es local y su certificado (`Σ b_i ≤ S`) no depende de
31que haya arista. La partición no lo ciega: lo respeta.
32
33## Rendimiento ≠ corrección (R-T9a)
34
35| Reparto | Retiro | Escrow | Nota |
36|---|---|---|---|
37| `[100, 0]` | réplica 1 retira 10 | rebalanceo | reparto malo: la réplica seca coordina de más |
38| `[50, 50]` | réplica 1 retira 10 | Local | reparto equilibrado: cero coordinación |
39
40Ambos repartos son **correctos** (el certificado `valor ≥ K` aguanta los dos); el
41segundo es solo más **eficiente**. La calidad del reparto es un problema de
42optimización sobre el grafo — el trabajo del `BudgetRouter` (`diffuse`) — no de
43corrección. Por eso los tests los separan.
44
45## Lectura
46
47El detector no se borra (sirve como alarma de reconciliación honesta para
48invariantes sin estructura de recurso divisible, R-T9b: unicidad de ID,
49integridad referencial). Pero **deja de ser el consejero de coordinación**: para
50un invariante numérico sobre un recurso conservado, el que evita coordinación de
51verdad —y sin puntos ciegos— es el **escrow** (`escrow.rs`), ruteado por el grafo
52(`router.rs`). Ver `STRONG_RESULT.md` para la corrección del reclamo de T8.
COMPARISON.md
COMPARISON — detector (T8) vs escrow (T9)
Los dos mecanismos sobre los mismos escenarios, misma cuenta (saldo inicial
100, invariante saldo ≥ 0, slack S = 100). Los veredictos de esta tabla son
exactamente los que verifica tests/comparison.rs — ninguno está escrito a mano.
- Detector (T8): funde los estados de las réplicas y comprueba el merge
(
AccountConfig::haz_coordination). Detección reactiva por-estado.
- Escrow (T9): reparto de presupuestos
[50,50] (válido: Σ ≤ 100); cada
réplica gasta local si le cabe (try_spend). Evitación por construcción.
| # | Escenario | Grafo | Retiros | Detector (T8) | Escrow [50,50] (T9) |
| 1 | conectado, caben | 0—1 | 10,10 | corre libre | Local (ambos ≤ 50) |
| 2 | conectado, sobregiro | 0—1 | 60,60 | coordina | rebalanceo (60 > 50) |
| 3 | particionado, sobregiro | ∅ | 60,60 | corre libre ⚠️ | rebalanceo (60 > 50) |
| 4 | particionado, caben | ∅ | 10,10 | corre libre | Local |
La fila 3 es todo el punto de T9
El detector dice "corre libre" al caso más peligroso. Con las dos réplicas
particionadas retirando 60 cada una de una cuenta de 100, el saldo global va a
100 − 120 = −20: viola el invariante. Pero el detector, al codificar una
restricción global por aristas pares, se queda sin ninguna arista al
particionar y no ve el acoplamiento. Falso-verde: el caso inseguro reportado
como seguro — el peor error posible en un consejero de coordinación.
El escrow lo bloquea correctamente. El presupuesto de cada réplica es 50; un
retiro de 60 devuelve NeedsRebalance{deficit:10}, con o sin partición,
porque el presupuesto b_i es local y su certificado (Σ b_i ≤ S) no depende de
que haya arista. La partición no lo ciega: lo respeta.
Rendimiento ≠ corrección (R-T9a)
| Reparto | Retiro | Escrow | Nota |
[100, 0] | réplica 1 retira 10 | rebalanceo | reparto malo: la réplica seca coordina de más |
[50, 50] | réplica 1 retira 10 | Local | reparto equilibrado: cero coordinación |
Ambos repartos son correctos (el certificado valor ≥ K aguanta los dos); el
segundo es solo más eficiente. La calidad del reparto es un problema de
optimización sobre el grafo — el trabajo del BudgetRouter (diffuse) — no de
corrección. Por eso los tests los separan.
Lectura
El detector no se borra (sirve como alarma de reconciliación honesta para
invariantes sin estructura de recurso divisible, R-T9b: unicidad de ID,
integridad referencial). Pero deja de ser el consejero de coordinación: para
un invariante numérico sobre un recurso conservado, el que evita coordinación de
verdad —y sin puntos ciegos— es el escrow (escrow.rs), ruteado por el grafo
(router.rs). Ver STRONG_RESULT.md para la corrección del reclamo de T8.