# Protocolo de crecimiento Cómo decide el enjambre qué hacer con un nodo que se acaba de unir, sin reiniciar nada y sin apagar ningún pipeline. ## 1. El problema Un clúster doméstico no es estático: un portátil se enciende, un mini-PC se añade, un móvil deja de estar disponible. Reiniciar el enjambre cada vez que cambia el hardware convierte el sistema en un juguete. El protocolo de crecimiento responde a una sola pregunta: **dado un nodo nuevo y el plan actual, ¿cuál es el mejor uso para ese hardware?** ## 2. Puntuación de extensión (FSENNL) El marco de referencia es **FSENNL** (*Federated Self-Expanding Neural Network Learning*): decide *cuándo* y *dónde* expandir combinando la información de Fisher de los parámetros con el perfil del dispositivo. El plano de control solo puede observar la parte del dispositivo, así que implementa esa mitad: ```text score = w_vram · vram_gb + w_ram · ram_gb + w_npu · npu_tops - w_lat · latency_ms ``` | Peso | Valor por defecto | Motivo | | --- | --- | --- | | `w_vram` | 1.0 | Es el recurso escaso: los expertos y el contexto compiten por ella. | | `w_ram` | 0.25 | Abundante en equipos domésticos; sirve para expertos, no para latencia. | | `w_npu` | 1.5 | Un nodo con NPU ejecuta un fragmento completo sin competir por el bus de la GPU. | | `w_lat` | 0.05 | 20 ms de latencia cuestan una capa de capacidad: la red es el límite físico. | La puntuación **se recalcula en el plano de control**, no se acepta la que anuncia el nodo. Un nodo puede estar desactualizado o medir mal su propia latencia, y el crecimiento no debe depender de una cifra que no se puede auditar. El campo `extension_score` del contrato Protobuf es informativo. ### Umbral de extensión `ExtensionWeights::extend_threshold` (6.0 por defecto) separa a los nodos que pueden sostener un tramo nuevo de los que solo pueden replicar. Un nodo por debajo del umbral no es inútil: sumando réplicas mejora el throughput y la tolerancia a fallos. ## 3. La decisión `growth_decision(new_node, current_plan)` es una cascada explícita, en orden de prioridad: ```mermaid flowchart TD A[Nodo nuevo] --> B{¿Faltan pipelines
para lo pedido?} B -->|Sí| R1[Replicate
copia completa] B -->|No| C{¿Réplicas medias
< objetivo 2.0?} C -->|Sí| R2[Replicate
reduce riesgo de failover] C -->|No| D{¿Telomerasa + modelo local
y score ≥ umbral?} D -->|Sí| E[AddExpert
aporta conocimiento propio] D -->|No| F{¿score ≥ umbral?} F -->|Sí| G[ExtendDepth
capas nuevas al final] F -->|No| R3[Replicate
nodo débil, solo redundancia] ``` ### Por qué este orden 1. **Faltan pipelines** — es la ganancia más directa: una copia entera más del modelo. Nada de lo demás compite con eso. 2. **Faltan réplicas** — sin dos copias por capa, un failover no tiene a quién recurrir. La redundancia es requisito del Pilar A, no un lujo. 3. **Aporta inteligencia** — si el nodo ya trae una IA local con telomerasa y además tiene capacidad, su valor no es más cómputo del mismo modelo, sino **conocimiento distinto**. Ese es el caso de *Composition of Experts*. 4. **Puede alargar** — con capacidad suficiente, aporta capas nuevas al final del pipeline (*Growing Transformers*: composición modular sobre un sustrato congelado). 5. **Nodo débil** — replicar es lo único honesto que se puede ofrecer. ## 4. Las tres acciones ### `Replicate` ```json { "action": "replicate", "pipeline_id": "ncm-base#0", "layers": [{ "first": 0, "last": 7 }, { "first": 8, "last": 11 }], "reason": "réplicas medias 1.00 < objetivo 2.00; laptop reduce el riesgo de failover" } ``` El orquestador re-ejecuta `plan_swarm` con el nodo añadido y `target_pipelines + 1`. El plan nuevo se valida antes de desplegarse; si no encaja, no se toca nada. ### `ExtendDepth` ```json { "action": "extend_depth", "pipeline_id": "ncm-base#0", "new_layers": 8, "reason": "puntuación 20.90 >= 6.00; beast extiende el modelo 8 capas" } ``` Las capas nuevas son proporcionales a la VRAM declarada (`layers_per_vram_gb`, una capa por GB por defecto) y se acotan a **la mitad del modelo**: crecer más de golpe convertiría la expansión en una migración y el sustrato dejaría de estar congelado. ### `AddExpert` ```json { "action": "add_expert", "expert_id": "ncm-vision@seer", "model_id": "ncm-vision", "reason": "seer tiene IA local con telomerasa y puntuación 49.85 >= 6.00; entra como experto" } ``` El experto se planifica con `plan_swarm_named` sobre su propio modelo y luego se fusiona con `merge_expert`, que mantiene los pipelines base vivos y añade los del experto. El enrutador distingue unos de otros por el campo `model_id` de la petición. ## 5. Aplicación atómica `apply_growth(plan, nodes, new_node, action)` es una función **pura**: recalcula el plan completo y lo valida. No muta el plan anterior ni habla con la red. ```rust let action = growth_decision(&newcomer, &plan)?; let grown = apply_growth(&plan, &nodes, &newcomer, &action)?; grown.validate()?; // invariantes del enjambre cluster.deploy(&grown).await?; // aquí sí se toca la red ``` Si `validate()` falla, el orquestador descarta el plan nuevo y sigue con el viejo. Esa es la propiedad que hace que el crecimiento no pueda degradar el servicio: **el plan nuevo existe completo antes de que nadie dependa de él**. ## 6. Invariantes que se verifican | Invariante | Qué rompe si se viola | | --- | --- | | Cada pipeline cubre `0..model_layers` de forma contigua | Un salto se quedaría sin capas que ejecutar. | | Cada capa tiene al menos un nodo | Petición imposible de servir. | | `deployments` de un nodo = unión de sus etapas | Un nodo cargaría fragmentos que nadie le pide, o le faltarían. | | El reparto suma exactamente `model_layers` | Capas huérfanas o duplicadas dentro de una copia. | | El plan es determinista para la misma entrada | Dos orquestadores calcularían planes distintos. | ## 7. Límites honestos - **El crecimiento es de hardware, no de conocimiento.** Añadir un nodo añade copias o profundidad; la inteligencia nueva solo llega por la telomerasa. - **`ExtendDepth` no reentrena el sustrato.** Las capas nuevas se inicializan y hay que ajustarlas; el planificador decide *dónde* crecer, no *cómo* entrenar. - **La decisión es codiciosa.** No planifica a cinco nodos vista: reacciona a cada llegada. Con hardware que llega en ráfaga conviene llamar a `plan_swarm` una vez con todos.