1# SDD — Addendum: T9 · De detección a evitación (escrow topología-consciente)
2
3**Extiende:** `DESIGN.md` + `DESIGN_lattice.md` (T5–T7) + `DESIGN_T8.md` (T8).
4**Estado:** Diseño — habilitado por el desmontaje del "resultado fuerte" de T8c.
5**Precondición:** T8 cerrado. Existe `STRONG_RESULT.md` con el testigo `[10,10]/[60,60]`.
6**Última edición:** 2026-07-01
7
8---
9
10## A. Qué corrige T9 (los dos errores de T8, nombrados)
11
12**Error 1 — detección disfrazada de evitación.** El veredicto de T8 (`corre libre ⟺ punto_fijo ∈ V`)
13funde dos estados ya divergidos y comprueba el merge. Eso es *detección reactiva de conflicto por-estado*,
14no *evitación de coordinación*. I-confluence (Bailis) cuantifica sobre **todos los estados alcanzables**:
15"retirar con saldo ≥ 0" no es I-confluente porque *existe* un [60,60] que rompe `I` → coordina,
16categórico, igual que CALM (`I-confluente ⟺ monótono`, Prop. 3 de Complete CALM). El testigo `[10,10]`
17no es una cuña más fina; responde otra pregunta, y para un invariante duro llega tarde: si hubiera sido
18[60,60], ambos retiros ya se aceptaron y repararlos es compensación = coordinación.
19
20**Error 2 — invariante global tratado como edge-local.** "Saldo total ≥ 0" acopla a **todas** las
21réplicas que tocan el recurso, compartan arista o no. Codificado por aristas pares, quitar la arista
22(partición) borra la restricción del haz — justo cuando el acoplamiento sigue intacto. De ahí el
23falso-verde: `particionado → libre` es el caso *más* peligroso reportado como seguro, porque el haz está
24**ciego** a él, no porque sea seguro.
25
26T9 arregla ambos de raíz: el invariante pasa a ser una **restricción global** (una hiperarista / celda de
27recurso incidente a todas las réplicas), y el veredicto pasa de *fundir-y-comprobar* a
28**repartir-y-chequear-local** — el esquema de **escrow**.
29
30---
31
32## B. La cura canónica: escrow / Bounded Counter
33
34Prior art, no lo inventamos: método de escrow (O'Neil 1986) materializado como **Bounded Counter CRDT**
35(Balegas, Serra, Duarte, Ferreira, Shapiro, Rodrigues, Preguiça — SRDS 2015).
36
37Idea, para invariante `valor ≥ K`:
38
391. Define el *slack* global `S = valor − K` (lo que se puede gastar sin romper `I`).
402. **Reparte** `S` en presupuestos por réplica `b_i` con la condición dura `Σ b_i ≤ S`.
413. Mientras a `r_i` le quede presupuesto, **retira localmente sin coordinar con nadie** — y esto es
42 correcto **incluso particionada**, porque `b_i` es suyo.
434. **La única coordinación** aparece cuando `r_i` agota `b_i` y necesita **rebalancear**: pedir
44 presupuesto a un vecino que aún tenga. Local, entre dos réplicas, no global.
45
46**El certificado (esto es lo que el detector de T8 no podía dar).** Sea `spent_i ≤ b_i` en cada réplica.
47Entonces `valor = inicial − Σ spent_i ≥ inicial − Σ b_i ≥ inicial − S = K`. Se cumple **para toda
48ejecución, todo orden, toda partición** — no se descubre tarde, se garantiza por construcción. Eso es
49evitación de coordinación real, cuantificada sobre todos los estados alcanzables dentro de los
50presupuestos.
51
52**Honestidad (para no repetir el sobre-reclamo de T8).** Escrow **tampoco** vence a CALM. *Transforma*
53la operación: de "retirar sin límite" (no I-confluente → coordina) a "retirar dentro de presupuesto"
54(I-confluente / monótona → libre), al costo de un rebalanceo ocasional. No rompes el teorema; re-ingenieras
55la operación para que caiga del lado bueno. Dilo así en `STRONG_RESULT.md`.
56
57---
58
59## C. Dónde el haz por fin se gana el sueldo (y no es derivativo)
60
61Bounded Counter usa esquemas simples para repartir y rebalancear (preguntar a un peer). El candidato a
62**contribución real** de T9 es hacer el reparto y el rebalanceo como un **flujo sobre el grafo de
63comunicación**, con localización principiada:
64
65- **Presupuesto** = una 0-cocadena `b` sobre los vértices, con `Σ b_i ≤ S`.
66- **Rebalanceo** = un flujo (1-cocadena) que mueve presupuesto por las aristas, **conservando el total**
67 (`b_i -= δ`, `b_j += δ` ⟹ `Σ b` invariante ⟹ el certificado se preserva).
68- **El Laplaciano rutea.** La "presión" para rebalancear es el gradiente de presupuesto restante a través
69 de las aristas; un flujo Laplaciano lo suaviza proactivamente para que las réplicas rara vez lleguen a
70 cero. Cuando una se seca, el Laplaciano **localiza** el flujo mínimo que la repone desde vecinos con
71 excedente — el rebalanceo mínimo, edge-local, no un consenso global.
72
73Cierre de arco honesto: el Laplaciano lineal que en el MVP era un **detector con falso-positivo** (H¹=β₁)
74reaparece aquí como un **ruteador de presupuesto legítimo**. Misma máquina, ahora en el problema correcto:
75no detectar conflictos post-hoc, sino repartir derechos proactivamente. Es una aplicación genuina del
76Laplaciano de haces (Hansen–Ghrist) a consenso/flujo. Para un presupuesto escalar basta el Laplaciano de
77grafo ordinario; el Laplaciano de haces solo hace falta si los presupuestos son vectoriales (multi-recurso,
78§H reach).
79
80---
81
82## D. Modelo de tipos (bocetos)
83
84```rust
85/// Restricción global: NO edge-local. Incide sobre todas las réplicas que tocan el recurso.
86pub struct GlobalInvariant {
87 pub floor: i64, // K en valor ≥ K
88 pub initial: i64, // valor inicial del recurso
89}
90impl GlobalInvariant {
91 pub fn slack(&self) -> i64 { self.initial - self.floor } // S
92}
93
94/// Presupuesto repartido: la 0-cocadena b sobre las réplicas. Invariante: sum(b) ≤ slack.
95pub struct Budget { pub per_replica: Vec<i64> }
96impl Budget {
97 pub fn is_valid_split(&self, inv: &GlobalInvariant) -> bool {
98 self.per_replica.iter().sum::<i64>() <= inv.slack()
99 && self.per_replica.iter().all(|&b| b >= 0)
100 }
101 /// Gasto local: permitido sin coordinar sii hay presupuesto.
102 pub fn try_spend(&mut self, r: usize, amount: i64) -> SpendResult { /* ... */ }
103}
104
105pub enum SpendResult {
106 Local, // dentro de presupuesto: cero coordinación
107 NeedsRebalance { deficit: i64 } // presupuesto agotado: coordinación edge-local
108}
109
110/// Ruteo de presupuesto sobre el grafo (Laplaciano). Conserva el total.
111pub struct BudgetRouter<'a> { nerve: &'a Nerve, budget: &'a mut Budget }
112impl<'a> BudgetRouter<'a> {
113 /// Flujo mínimo que repone a `r` desde vecinos con excedente. Devuelve las aristas usadas.
114 pub fn rebalance_to(&mut self, r: usize, deficit: i64) -> Vec<(usize, usize, i64)> { /* ... */ }
115 /// Difusión proactiva: suaviza el presupuesto para minimizar futuros déficits.
116 pub fn diffuse(&mut self) { /* paso de Laplaciano, conserva sum(b) */ }
117}
118```
119
120---
121
122## E. Cambios de módulos
123
124```
125Nuevo src/invariant.rs — GlobalInvariant (restricción global, no edge-local)
126Nuevo src/escrow.rs — Budget, try_spend, el certificado sum(b) ≤ slack
127Nuevo src/router.rs — BudgetRouter: rebalanceo y difusión por Laplaciano (reusa el grafo del nervio)
128Editar src/verdict.rs — Verdict::via_escrow: Local | NeedsRebalance{edges}, NUNCA "libre global" ciego
129Editar STRONG_RESULT.md — corregir el reclamo de T8 (§G)
130Nuevo COMPARISON.md — detector (T8) vs escrow (T9) sobre los mismos escenarios, con el punto ciego
131```
132
133El detector de T8 **no se borra**: queda como capa de reconciliación/alarma honesta (para conflictos que
134el escrow no cubre, p.ej. invariantes sin estructura de recurso divisible). Pero deja de ser el consejero
135de coordinación.
136
137---
138
139## F. El certificado y la muerte del falso-verde
140
141Test central de T9, que **invierte** el punto ciego de T8:
142
143- **Certificado exhaustivo.** Para cualquier reparto válido (`Σ b_i ≤ S`) y **cualquier** combinación de
144 gastos con `spent_i ≤ b_i` — incluidas todas las réplicas gastando su máximo a la vez, incluidas
145 particiones arbitrarias — el invariante global `valor ≥ K` se preserva. `proptest` sobre repartos y
146 gastos aleatorios; la aserción es el certificado de §B, no un merge concreto.
147- **El falso-verde particionado desaparece.** El caso 60/60 de T8: con presupuestos `[50,50]` (reparto
148 válido de `S=100`), una réplica particionada que intenta retirar 60 recibe `NeedsRebalance{deficit:10}`
149 — correctamente **bloqueada/pendiente de coordinación**, no "libre". Contrasta explícitamente en
150 `COMPARISON.md` contra el detector de T8, que decía "particionado → libre".
151- **Rebalanceo conserva y localiza.** Tras cualquier `rebalance_to`, `Σ b` es invariante (certificado
152 intacto) y las aristas devueltas son edge-locales al nodo seco. `proptest`: nunca un rebalanceo toca una
153 réplica no adyacente al déficit.
154
155---
156
157## G. Corrección obligatoria de `STRONG_RESULT.md`
158
159T9 no está completo hasta reescribir el reclamo de T8 con lo que el testigo realmente probó:
160
161- **Quitar:** "versión fuerte probada / más fino que CALM / I-confluence sobre el grafo real."
162- **Poner:** "detección de conflicto por-estado con localización, topología-consciente. NO es una
163 separación respecto a I-confluence ni a CALM — ambos coinciden (Prop. 3). El caso particionado-60/60
164 queda documentado como **punto ciego**: el detector reporta 'libre' porque, sin arista, no ve el
165 acoplamiento del invariante global. Esto motivó T9."
166- **Añadir puntero** a `COMPARISON.md` y al certificado de escrow como la evitación real.
167
168Un error registrado con honestidad vale más que un resultado fuerte reclamado de más. Ese es el criterio.
169
170---
171
172## H. Riesgos y horizonte
173
174- **R-T9a (reparto inicial).** `Σ b_i ≤ S` deja libertad en *cómo* repartir. Un reparto malo (todo a una
175 réplica) causa rebalanceos constantes = mucha coordinación. La calidad del reparto es un problema de
176 optimización sobre el grafo — donde el Laplaciano/difusión aporta. No es corrección (el certificado
177 aguanta cualquier reparto válido), es *rendimiento*. Sepáralos en los tests.
178- **R-T9b (recurso no divisible).** Escrow sirve para invariantes sobre un *recurso conservado divisible*
179 (saldo, stock, cupo). Invariantes no divisibles (unicidad de ID, integridad referencial) NO se resuelven
180 con presupuestos numéricos — ahí el detector de T8 sigue siendo lo mejor que tienes, o se necesitan
181 reservas de otro tipo. Documenta la frontera; no vendas escrow como universal.
182- **R-T9c (honestidad de novedad).** Bounded Counter/escrow es prior art (Balegas 2015). Tu candidato a
183 novedad es el **ruteo/localización del presupuesto por el grafo real** y, como reach, **múltiples
184 invariantes acoplados** — que el propio paper de Balegas señala como no resuelto ("constraints over
185 multiple counters... only way is to store them in a single object"). Ese multi-invariante vía haces sí
186 sería nuevo. Reclámalo solo si lo construyes.
187- **Q-T9 (M4).** Con escrow, el adaptador a Tawasuyu/Hammer cablea un mecanismo libre de coordinación de
188 verdad: presupuestos por nodo soberano, gasto local sin sync, rebalanceo edge-local al secarse. Eso es
189 lo que justifica el aparato sobre una malla de gossip. El detector de T8, no.
190
191---
192
193## I. Tickets
194
195**T9a — Invariante global + certificado de escrow.**
196"Crea `src/invariant.rs` (`GlobalInvariant`, restricción global no edge-local) y `src/escrow.rs`
197(`Budget`, `try_spend`, `is_valid_split`). Implementa el veredicto `Verdict::via_escrow`:
198`Local` si hay presupuesto, `NeedsRebalance{deficit}` si no. Test del certificado (§F): `proptest` que,
199para todo reparto válido y toda combinación de gastos dentro de presupuesto —incluidas particiones y todos
200gastando el máximo—, verifica `valor ≥ K`. Reproduce el caso 60/60 de T8 con presupuestos [50,50] y
201verifica que la réplica particionada recibe `NeedsRebalance`, no `Local`."
202
203**T9b — Ruteo de presupuesto por Laplaciano.**
204"Crea `src/router.rs` (`BudgetRouter`) reusando el grafo del nervio. `rebalance_to(r, deficit)` calcula el
205flujo mínimo que repone a `r` desde vecinos con excedente y devuelve las aristas usadas; `diffuse` hace un
206paso de Laplaciano que suaviza el presupuesto conservando `Σ b`. Tests: (1) todo rebalanceo conserva
207`Σ b` (certificado intacto); (2) las aristas del rebalanceo son adyacentes al nodo seco (nunca toca una
208réplica no vecina); (3) tras `diffuse`, la varianza del presupuesto no aumenta. Para presupuesto escalar
209usa el Laplaciano de grafo ordinario; deja un `TODO` marcado para el Laplaciano de haces si se pasa a
210multi-recurso."
211
212**T9c — Comparación honesta y corrección del registro.**
213"Crea `COMPARISON.md`: corre el detector de T8 y el certificador de escrow de T9 sobre los mismos
214escenarios (conectado, particionado, 10/10, 60/60, repartos varios) y tabula sus veredictos, resaltando el
215falso-verde particionado del detector frente al bloqueo correcto del escrow. Reescribe `STRONG_RESULT.md`
216según §G (quitar el reclamo fuerte, documentar el punto ciego, apuntar a la evitación real). No declares
217T9 cerrado hasta que ambos archivos reflejen lo que los tests realmente prueban."
218
219---
220
221## J. Referencias añadidas
222
223- V. Balegas, D. Serra, S. Duarte, C. Ferreira, M. Shapiro, R. Rodrigues, N. Preguiça — *Extending
224 Eventually Consistent Cloud Databases for Enforcing Numeric Invariants*, IEEE SRDS 2015. (Bounded Counter
225 CRDT: la evitación por presupuestos.)
226- P. E. O'Neil — *The Escrow Transactional Method*, ACM TODS 11(4), 1986. (Origen del escrow.)
227- (Contexto) Milano & Myers — *Predictive Treaties* (predicados particionables enforced localmente, con
228 dependencia del tiempo): generalización del reparto local; útil si el reach multi-invariante avanza.
229- (De addenda previos) Bailis et al. 2014 (I-confluence); Ghrist–Riess 2022 (Tarski); Hansen–Ghrist 2019
230 (Laplaciano de haces, base del ruteo); Shapiro et al. 2011 (CRDTs); Hellerstein–Alvaro (CALM).
T9.md
SDD — Addendum: T9 · De detección a evitación (escrow topología-consciente)
Extiende: DESIGN.md + DESIGN_lattice.md (T5–T7) + DESIGN_T8.md (T8).
Estado: Diseño — habilitado por el desmontaje del "resultado fuerte" de T8c.
Precondición: T8 cerrado. Existe STRONG_RESULT.md con el testigo [10,10]/[60,60].
Última edición: 2026-07-01
A. Qué corrige T9 (los dos errores de T8, nombrados)
Error 1 — detección disfrazada de evitación. El veredicto de T8 (corre libre ⟺ punto_fijo ∈ V)
funde dos estados ya divergidos y comprueba el merge. Eso es detección reactiva de conflicto por-estado,
no evitación de coordinación. I-confluence (Bailis) cuantifica sobre todos los estados alcanzables:
"retirar con saldo ≥ 0" no es I-confluente porque existe un [60,60] que rompe I → coordina,
categórico, igual que CALM (I-confluente ⟺ monótono, Prop. 3 de Complete CALM). El testigo [10,10]
no es una cuña más fina; responde otra pregunta, y para un invariante duro llega tarde: si hubiera sido
[60,60], ambos retiros ya se aceptaron y repararlos es compensación = coordinación.
Error 2 — invariante global tratado como edge-local. "Saldo total ≥ 0" acopla a todas las
réplicas que tocan el recurso, compartan arista o no. Codificado por aristas pares, quitar la arista
(partición) borra la restricción del haz — justo cuando el acoplamiento sigue intacto. De ahí el
falso-verde: particionado → libre es el caso más peligroso reportado como seguro, porque el haz está
ciego a él, no porque sea seguro.
T9 arregla ambos de raíz: el invariante pasa a ser una restricción global (una hiperarista / celda de
recurso incidente a todas las réplicas), y el veredicto pasa de fundir-y-comprobar a
repartir-y-chequear-local — el esquema de escrow.
B. La cura canónica: escrow / Bounded Counter
Prior art, no lo inventamos: método de escrow (O'Neil 1986) materializado como Bounded Counter CRDT
(Balegas, Serra, Duarte, Ferreira, Shapiro, Rodrigues, Preguiça — SRDS 2015).
Idea, para invariante valor ≥ K:
- Define el slack global
S = valor − K (lo que se puede gastar sin romper I).
- Reparte
S en presupuestos por réplica b_i con la condición dura Σ b_i ≤ S.
- Mientras a
r_i le quede presupuesto, retira localmente sin coordinar con nadie — y esto es
correcto incluso particionada, porque b_i es suyo.
- La única coordinación aparece cuando
r_i agota b_i y necesita rebalancear: pedir
presupuesto a un vecino que aún tenga. Local, entre dos réplicas, no global.
El certificado (esto es lo que el detector de T8 no podía dar). Sea spent_i ≤ b_i en cada réplica.
Entonces valor = inicial − Σ spent_i ≥ inicial − Σ b_i ≥ inicial − S = K. Se cumple para toda
ejecución, todo orden, toda partición — no se descubre tarde, se garantiza por construcción. Eso es
evitación de coordinación real, cuantificada sobre todos los estados alcanzables dentro de los
presupuestos.
Honestidad (para no repetir el sobre-reclamo de T8). Escrow tampoco vence a CALM. Transforma
la operación: de "retirar sin límite" (no I-confluente → coordina) a "retirar dentro de presupuesto"
(I-confluente / monótona → libre), al costo de un rebalanceo ocasional. No rompes el teorema; re-ingenieras
la operación para que caiga del lado bueno. Dilo así en STRONG_RESULT.md.
C. Dónde el haz por fin se gana el sueldo (y no es derivativo)
Bounded Counter usa esquemas simples para repartir y rebalancear (preguntar a un peer). El candidato a
contribución real de T9 es hacer el reparto y el rebalanceo como un flujo sobre el grafo de
comunicación, con localización principiada:
- Presupuesto = una 0-cocadena
b sobre los vértices, con Σ b_i ≤ S.
- Rebalanceo = un flujo (1-cocadena) que mueve presupuesto por las aristas, conservando el total
(
b_i -= δ, b_j += δ ⟹ Σ b invariante ⟹ el certificado se preserva).
- El Laplaciano rutea. La "presión" para rebalancear es el gradiente de presupuesto restante a través
de las aristas; un flujo Laplaciano lo suaviza proactivamente para que las réplicas rara vez lleguen a
cero. Cuando una se seca, el Laplaciano localiza el flujo mínimo que la repone desde vecinos con
excedente — el rebalanceo mínimo, edge-local, no un consenso global.
Cierre de arco honesto: el Laplaciano lineal que en el MVP era un detector con falso-positivo (H¹=β₁)
reaparece aquí como un ruteador de presupuesto legítimo. Misma máquina, ahora en el problema correcto:
no detectar conflictos post-hoc, sino repartir derechos proactivamente. Es una aplicación genuina del
Laplaciano de haces (Hansen–Ghrist) a consenso/flujo. Para un presupuesto escalar basta el Laplaciano de
grafo ordinario; el Laplaciano de haces solo hace falta si los presupuestos son vectoriales (multi-recurso,
§H reach).
D. Modelo de tipos (bocetos)
/// Restricción global: NO edge-local. Incide sobre todas las réplicas que tocan el recurso.
pub struct GlobalInvariant {
pub floor: i64, // K en valor ≥ K
pub initial: i64, // valor inicial del recurso
}
impl GlobalInvariant {
pub fn slack(&self) -> i64 { self.initial - self.floor } // S
}
/// Presupuesto repartido: la 0-cocadena b sobre las réplicas. Invariante: sum(b) ≤ slack.
pub struct Budget { pub per_replica: Vec<i64> }
impl Budget {
pub fn is_valid_split(&self, inv: &GlobalInvariant) -> bool {
self.per_replica.iter().sum::<i64>() <= inv.slack()
&& self.per_replica.iter().all(|&b| b >= 0)
}
/// Gasto local: permitido sin coordinar sii hay presupuesto.
pub fn try_spend(&mut self, r: usize, amount: i64) -> SpendResult { /* ... */ }
}
pub enum SpendResult {
Local, // dentro de presupuesto: cero coordinación
NeedsRebalance { deficit: i64 } // presupuesto agotado: coordinación edge-local
}
/// Ruteo de presupuesto sobre el grafo (Laplaciano). Conserva el total.
pub struct BudgetRouter<'a> { nerve: &'a Nerve, budget: &'a mut Budget }
impl<'a> BudgetRouter<'a> {
/// Flujo mínimo que repone a `r` desde vecinos con excedente. Devuelve las aristas usadas.
pub fn rebalance_to(&mut self, r: usize, deficit: i64) -> Vec<(usize, usize, i64)> { /* ... */ }
/// Difusión proactiva: suaviza el presupuesto para minimizar futuros déficits.
pub fn diffuse(&mut self) { /* paso de Laplaciano, conserva sum(b) */ }
}
E. Cambios de módulos
Nuevo src/invariant.rs — GlobalInvariant (restricción global, no edge-local)
Nuevo src/escrow.rs — Budget, try_spend, el certificado sum(b) ≤ slack
Nuevo src/router.rs — BudgetRouter: rebalanceo y difusión por Laplaciano (reusa el grafo del nervio)
Editar src/verdict.rs — Verdict::via_escrow: Local | NeedsRebalance{edges}, NUNCA "libre global" ciego
Editar STRONG_RESULT.md — corregir el reclamo de T8 (§G)
Nuevo COMPARISON.md — detector (T8) vs escrow (T9) sobre los mismos escenarios, con el punto ciego
El detector de T8 no se borra: queda como capa de reconciliación/alarma honesta (para conflictos que
el escrow no cubre, p.ej. invariantes sin estructura de recurso divisible). Pero deja de ser el consejero
de coordinación.
F. El certificado y la muerte del falso-verde
Test central de T9, que invierte el punto ciego de T8:
- Certificado exhaustivo. Para cualquier reparto válido (
Σ b_i ≤ S) y cualquier combinación de
gastos con spent_i ≤ b_i — incluidas todas las réplicas gastando su máximo a la vez, incluidas
particiones arbitrarias — el invariante global valor ≥ K se preserva. proptest sobre repartos y
gastos aleatorios; la aserción es el certificado de §B, no un merge concreto.
- El falso-verde particionado desaparece. El caso 60/60 de T8: con presupuestos
[50,50] (reparto
válido de S=100), una réplica particionada que intenta retirar 60 recibe NeedsRebalance{deficit:10}
— correctamente bloqueada/pendiente de coordinación, no "libre". Contrasta explícitamente en
COMPARISON.md contra el detector de T8, que decía "particionado → libre".
- Rebalanceo conserva y localiza. Tras cualquier
rebalance_to, Σ b es invariante (certificado
intacto) y las aristas devueltas son edge-locales al nodo seco. proptest: nunca un rebalanceo toca una
réplica no adyacente al déficit.
G. Corrección obligatoria de STRONG_RESULT.md
T9 no está completo hasta reescribir el reclamo de T8 con lo que el testigo realmente probó:
- Quitar: "versión fuerte probada / más fino que CALM / I-confluence sobre el grafo real."
- Poner: "detección de conflicto por-estado con localización, topología-consciente. NO es una
separación respecto a I-confluence ni a CALM — ambos coinciden (Prop. 3). El caso particionado-60/60
queda documentado como punto ciego: el detector reporta 'libre' porque, sin arista, no ve el
acoplamiento del invariante global. Esto motivó T9."
- Añadir puntero a
COMPARISON.md y al certificado de escrow como la evitación real.
Un error registrado con honestidad vale más que un resultado fuerte reclamado de más. Ese es el criterio.
H. Riesgos y horizonte
- R-T9a (reparto inicial).
Σ b_i ≤ S deja libertad en cómo repartir. Un reparto malo (todo a una
réplica) causa rebalanceos constantes = mucha coordinación. La calidad del reparto es un problema de
optimización sobre el grafo — donde el Laplaciano/difusión aporta. No es corrección (el certificado
aguanta cualquier reparto válido), es rendimiento. Sepáralos en los tests.
- R-T9b (recurso no divisible). Escrow sirve para invariantes sobre un recurso conservado divisible
(saldo, stock, cupo). Invariantes no divisibles (unicidad de ID, integridad referencial) NO se resuelven
con presupuestos numéricos — ahí el detector de T8 sigue siendo lo mejor que tienes, o se necesitan
reservas de otro tipo. Documenta la frontera; no vendas escrow como universal.
- R-T9c (honestidad de novedad). Bounded Counter/escrow es prior art (Balegas 2015). Tu candidato a
novedad es el ruteo/localización del presupuesto por el grafo real y, como reach, múltiples
invariantes acoplados — que el propio paper de Balegas señala como no resuelto ("constraints over
multiple counters... only way is to store them in a single object"). Ese multi-invariante vía haces sí
sería nuevo. Reclámalo solo si lo construyes.
- Q-T9 (M4). Con escrow, el adaptador a Tawasuyu/Hammer cablea un mecanismo libre de coordinación de
verdad: presupuestos por nodo soberano, gasto local sin sync, rebalanceo edge-local al secarse. Eso es
lo que justifica el aparato sobre una malla de gossip. El detector de T8, no.
I. Tickets
T9a — Invariante global + certificado de escrow.
"Crea src/invariant.rs (GlobalInvariant, restricción global no edge-local) y src/escrow.rs
(Budget, try_spend, is_valid_split). Implementa el veredicto Verdict::via_escrow:
Local si hay presupuesto, NeedsRebalance{deficit} si no. Test del certificado (§F): proptest que,
para todo reparto válido y toda combinación de gastos dentro de presupuesto —incluidas particiones y todos
gastando el máximo—, verifica valor ≥ K. Reproduce el caso 60/60 de T8 con presupuestos [50,50] y
verifica que la réplica particionada recibe NeedsRebalance, no Local."
T9b — Ruteo de presupuesto por Laplaciano.
"Crea src/router.rs (BudgetRouter) reusando el grafo del nervio. rebalance_to(r, deficit) calcula el
flujo mínimo que repone a r desde vecinos con excedente y devuelve las aristas usadas; diffuse hace un
paso de Laplaciano que suaviza el presupuesto conservando Σ b. Tests: (1) todo rebalanceo conserva
Σ b (certificado intacto); (2) las aristas del rebalanceo son adyacentes al nodo seco (nunca toca una
réplica no vecina); (3) tras diffuse, la varianza del presupuesto no aumenta. Para presupuesto escalar
usa el Laplaciano de grafo ordinario; deja un TODO marcado para el Laplaciano de haces si se pasa a
multi-recurso."
T9c — Comparación honesta y corrección del registro.
"Crea COMPARISON.md: corre el detector de T8 y el certificador de escrow de T9 sobre los mismos
escenarios (conectado, particionado, 10/10, 60/60, repartos varios) y tabula sus veredictos, resaltando el
falso-verde particionado del detector frente al bloqueo correcto del escrow. Reescribe STRONG_RESULT.md
según §G (quitar el reclamo fuerte, documentar el punto ciego, apuntar a la evitación real). No declares
T9 cerrado hasta que ambos archivos reflejen lo que los tests realmente prueban."
J. Referencias añadidas
- V. Balegas, D. Serra, S. Duarte, C. Ferreira, M. Shapiro, R. Rodrigues, N. Preguiça — Extending
Eventually Consistent Cloud Databases for Enforcing Numeric Invariants, IEEE SRDS 2015. (Bounded Counter
CRDT: la evitación por presupuestos.)
- P. E. O'Neil — The Escrow Transactional Method, ACM TODS 11(4), 1986. (Origen del escrow.)
- (Contexto) Milano & Myers — Predictive Treaties (predicados particionables enforced localmente, con
dependencia del tiempo): generalización del reparto local; útil si el reach multi-invariante avanza.
- (De addenda previos) Bailis et al. 2014 (I-confluence); Ghrist–Riess 2022 (Tarski); Hansen–Ghrist 2019
(Laplaciano de haces, base del ruteo); Shapiro et al. 2011 (CRDTs); Hellerstein–Alvaro (CALM).