# Fermenty — Léxico

> Catálogo de términos del dominio: qué significa cada palabra, qué **no** significa,
> y dónde vive en el esquema. Sirve para hablar de las mismas cosas y como base de la
> documentación.
>
> Destino sugerido: `docs/Lexico.md` · Documento vivo · v3 · 2026-07-25

**Cómo leerlo.** Cada término lleva tres partes: la definición, un contraste (*«no es…»*) porque casi todos los errores del proyecto han venido de confundir dos conceptos parecidos, y su sitio en la base de datos. Los marcados 🔜 aún no existen en código — a fecha de v3 solo quedan tres: **Bucle 2**, **Calibración** y **Favoritos**. Todo lo demás (motor, ficha, ingredientes, trueques, corte, cierre del lazo, bucle 1) ya está construido.

---

## 1. La cadena

El esqueleto del sistema, de lo general a lo concreto:

```
Clasificación → Guía → Receta → [Motor] → Tanda → Lote
  (navegar)    (el fermento) (variación)  (calcular) (lo que pasó) (lo trazable)
```

Las tres primeras son **definición**: mutables, se corrigen en un sitio y valen para todos. Las dos últimas son **hecho**: congeladas, pertenecen a una persona y a un momento.

### Clasificación
El estante donde se navega. Bebidas fermentadas, Vegetales fermentados, Bebidas alcohólicas, Vinagres. Existe para el usuario y para Google.

*No es* una taxonomía microbiológica. Antes lo era (láctica, acética, fúngica) y se cambió: nadie busca «fermentación acética», busca «cómo hacer vinagre».

📁 `fermentation_categories`

### Guía
**El fermento.** Kombucha, chucrut, kimchi. Contiene el esqueleto de proceso completo, su identidad pública (nombre, slug, imagen, dificultad) y —cuando exista— su ficha.

Una por fermento. Inmutable en el sentido de que su esqueleto no cambia según quién la use.

*No es* una versión de nada. Hubo versionado (`version`, `is_current`) y murió: cada tanda congela su copia, así que el historial ya está preservado donde importa.

📁 `fermentation_guides`

### Receta
Una variación sobre una guía: **qué ingredientes concretos entran y qué pasos opcionales se encienden**. «Kombucha de cereza y lavanda».

*No es* dueña del proceso. No tiene pasos propios, ni títulos, ni descripciones, ni tiempos. Si una variación necesitara un paso de proceso que la guía no tiene, sería otro fermento.

*No es* tampoco una lista de cantidades fijas — o dejará de serlo cuando el motor las calcule.

📁 `recipes`

### Tanda
Una fermentación real de una persona concreta que empezó un día concreto. Lleva copia congelada de todo y registra lo que de verdad pasó.

*No es* una receta en marcha. La receta es una intención; la tanda es un hecho, con sus desviaciones, olvidos y decisiones sobre la marcha.

**Cinco estados, sin `failed`:** `borrador` (formulada, sin arrancar — aún no tiene fecha ni código de lote) → `activa` → `pausada` → `completada` **o** `descartada`. «Fermentó y salió mal de sabor» *no* es un estado: es una **completada con mala valoración** (y esa señal de paladar sí calibra, §Calibración). Solo el accidente físico —moho, cultivo muerto— es `descartada`, y esa **no calibra**. El borrador no caduca; que esté desfasado respecto a su guía es un aviso de la UI, no un estado.

📁 `batches`

### Lote
El resultado trazable de una tanda: su código identificable. En el esquema **es la misma fila que la tanda**, no una tabla aparte.

📁 `batches` (mismo registro)

---

## 2. El proceso

### Paso
Una etapa del proceso del fermento: hervir el agua, poner el arrancador, filtrar, segunda fermentación.

La guía contiene **todos los pasos posibles** del fermento, no la secuencia de una tanda concreta. Es un supersconjunto del que cada receta enciende lo que aplica.

📁 `guide_steps` (definición) · `batch_steps` (copia congelada)

### Paso obligatorio / opcional
**Obligatorio** = va siempre, en todas las recetas de ese fermento. Hervir, poner arrancador.
**Opcional** = la receta decide si lo enciende. Filtrar, segunda fermentación.

Un obligatorio **no se puede apagar**: si una receta pudiera omitirlo, sería otro fermento — o el «obligatorio» no significaba nada.

📁 `guide_steps.is_optional` · el encendido, en `recipe_steps`

### Interruptor
El mecanismo por el que una receta enciende un paso opcional. **Presencia de la fila = encendido; ausencia = apagado.** Sin booleano, porque así el estado «obligatorio apagado» es irrepresentable por construcción.

📁 `recipe_steps` (una fila = un opcional encendido)

### Acción
Una instrucción concreta dentro de un paso: «calienta el agua a 90°», «remueve hasta disolver». Varias por paso.

📁 `step_actions` (definición) · `batch_steps.actions` (congelada)

### Consejo *(hint)*
Prosa editorial del **por qué se hace así**. Vive en dos ejes independientes
(`Consejos_Admin_Pantalla.md` §1): el **nivel** dice hasta dónde es cierto —general,
categoría, fermento, paso; cada consejo vive en el más general en el que sigue
siéndolo— y la **ventana** dice por dónde sale: `is_daily` es el diario de portada,
la FK a paso es el proactivo dentro de la tanda, y el síntoma es el solucionador 🔜.
El nivel no es la ventana, y la prueba es el moho: «qué hacer si aparece moho» es un
síntoma y es general a la vez.

*No es* un aviso del motor. Un consejo es conocimiento editorial que no depende de tu
tanda; un aviso del motor está atado a una **condición** de tu formulación y solo
aparece cuando se cumple. Desde 2026-08 la frontera es además de plan: el aviso va
gratis con el motor; el consejo sale gratis solo por el diario y por la seguridad del
solucionador (`Planes.md` §2).

📁 `hints`

### Desviación
Lo que ocurrió distinto de lo previsto: un paso saltado a propósito, uno olvidado, una cantidad cambiada sobre la marcha, un cambio de sitio.

Pertenece **siempre a la tanda**, nunca a la receta. La receta se escribió semanas antes y no sabía qué iba a pasar.

📁 `batch_steps.status` (incl. `skipped`) · `batch_events`

---

## 3. Los ingredientes

### Ingrediente
Una entrada del catálogo maestro, organizada en árbol de **profundidad libre**:

```
Té
├── Té negro → Assam, Darjeeling
└── Té verde → Sencha, Chun mee, Jazmín
```

**Un nivel nuevo existe cuando la distinción cambia algo**: el cálculo del motor, una barandilla, o lo que la persona compra. Si no cambia ninguna de las tres, es una nota de sabor y no un ingrediente aparte. Sin este criterio el árbol degenera.

**Cada capa apunta al nivel que le corresponde.** La guía de kombucha apunta a «Té» a secas porque cualquiera vale; la receta baja a «Té negro»; la tanda puede bajar hasta «Sencha», que es lo que había en la despensa. La guía define el hueco, la receta elige quién lo ocupa.

El perfil se resuelve por **herencia con sobreescritura** (§6): si un nodo no lo tiene, se sube.

El zumo de cereza es **su propio ingrediente**, hijo de Cereza, con su propio azúcar. El motor no razona sobre el acto de exprimir; lee el nodo que le ponen.

📁 `ingredients`

### Papel
**Qué función cumple un ingrediente en un fermento concreto.** Es una propiedad del *vínculo* guía↔ingrediente, no del ingrediente: el mismo ingrediente puede cumplir papeles distintos en fermentos distintos.

| Papel | Qué es | Ejemplos |
|---|---|---|
| **sustrato** | Lo que los microbios se comen | Azúcar, mosto, zumo |
| **medio** | El líquido o matriz donde ocurre | Agua, té |
| **arrancador** | El cultivo que arranca | SCOBY, líquido de arranque, ginger bug |
| **saborizante** | Lo que aporta sabor y aroma | Fruta, especias, lavanda |
| **regulador** | Lo que decide **quién** come y en qué condiciones | Sal, conservantes, antibacterianos |

La **sal es regulador, no sustrato**: nadie se la come, decide quién prospera. Si fuera sustrato, el motor interpretaría que subirla acelera la fermentación — y es al revés.

Todos los papeles tienen efectos secundarios (el té es medio y además sabe). El papel nombra la **función principal**.

📁 `guide_ingredients.papel`

### Propiedad *(del ingrediente)*
Lo que un ingrediente hace **lo pongas por lo que lo pongas**. Contiene azúcar fermentable, contiene lactobacterias, inhibe.

> **El papel dice por qué lo pusiste. La propiedad dice qué hace de todos modos.**

La canela entra como `saborizante` —la quieres por el sabor— pero **inhibe** porque es canela, y lo hace en cualquier fermento donde aparezca. Inhibir por tanto es propiedad, no papel.

Un papel es uno solo (es una columna); las propiedades son varias a la vez (van en JSON). Un conservante añadido a propósito es papel `regulador` **y** propiedad `inhibe`: dos preguntas distintas con la misma respuesta.

📁 `ingredients.profile` (json)

### Preparación
Cómo se prepara un ingrediente para este uso: «exprimir y colar», «triturar con piel». **Texto libre**, la lee una persona.

*No es* un dato del motor. Si la preparación cambia el comportamiento, lo que cambia es el ingrediente (zumo ≠ fruta entera), y eso se resuelve con una variante.

📁 `recipe_ingredients.notes`

---

## 4. El motor

### Motor
El cálculo que convierte *lo que quieres* en *lo que tienes que hacer*. Recibe ficha + receta + perfil objetivo + contexto, y devuelve cantidades por ingrediente y paso, ventanas de tiempo y avisos.

*No es* una tabla. Es un servicio; por eso no aparece en el diagrama entidad-relación.

📁 `App\Mentor\Motor` (ensambla la salida pública del modelo)

### Ficha
Los coeficientes y barandillas de un fermento: lo que el motor necesita para calcular. Vive colgando de la guía y **nunca viaja al cliente**.

Se congela entera en la tanda al formular, en vez de versionarse — es un JSON de pocos KB y así se evita gestionar versiones de algo que cambiará muchas veces.

📁 `fermentation_guides.ficha` (json) · se congela en `batches.formulacion.ficha`

### Perfil objetivo
Lo que el usuario **quiere conseguir**: suave, equilibrada, fuerte. Es la entrada del motor y lo que sustituye a elegir una receta con cantidades fijas.

Técnicamente son cuatro ejes —intensidad, equilibrio, gas y complejidad— no un valor en una escala. «Fuerte» es una diagonal, no «más que equilibrada».

📁 `recipes.perfil_defecto` (el punto probado de la receta) · `batches.formulacion` (json)

### Complejidad *(eje del perfil)*
Uno de los cuatro ejes. En el modelo determina el **arrancador**: más complejidad → menos arranque → fermentación más lenta y con más fondo.

*No es* «complejidad de sabor», aunque el nombre lo sugiera. Sale del arrancador, y el arranque se pone por **vigor y prisa**, no por buscar complejidad. Por eso, al invertir una receta probada (§Inversor), este eje **reproduce la fórmula pero no describe el sabor**: Summer Sencha, «fácil de beber», sale con complejidad *alta*; Forest Lab, «complejo», *baja* — invertido. En el plano del Nivel 2 hay que leerlo como «lento ↔ rápido», no «simple ↔ complejo». Es la razón por la que el backtest cargó la prueba solo sobre el eje de acidez (equilibrio), que sí describe lo que se bebe.

### Inversor
De una receta **probada** a su perfil: recibe lo que el obrador sabe —azúcar, arranque, temperatura, días— e invierte el modelo para deducir el `perfil_defecto`. Es lo que hace una receta creable sin elegir ejes a ciegas.

*No es* validación cuando cierra el round-trip: invertir con las mismas fórmulas con las que se calcula vuelve al inicio por álgebra. Lo que prueba es que los ejes derivados caigan donde el obrador dijo, algo que el modelo no vio. Recorta los ejes fuera de rango **diciéndolo** (arranque al 25% → «lo trato como 22%»), nunca se niega: una receta que existe se tiene que poder guardar.

📁 `App\Mentor\Inversor`

### Contexto
Las condiciones reales de esta fermentación: temperatura del sitio, vigor del cultivo, calibración acumulada del usuario. Es la otra entrada del motor.

📁 `batches.formulacion.entrada` (json) — temperatura y confianza congeladas al formular. La calibración acumulada aún no (§Calibración 🔜).

### Ventana
Un rango de días en que algo debe ocurrir: «corta entre el día 6 y el 8».

Siempre un rango, nunca un punto — es humildad calibrada hecha dato. Aparece en tres sitios con dueños distintos y no se pisan:

- **Horquilla orientativa** → la guía (`min_day`/`max_day`)
- **Predicción** → el motor
- **Realidad** → la tanda

**El tiempo nunca vive en la receta.**

### Barandilla
Un límite que el motor no cruza aunque el usuario lo pida: presión máxima en botella, acidez mínima segura, umbrales de alcohol. Vive en la ficha.

*No es* un aviso. La barandilla impide; el aviso informa.

📁 `fermentation_guides.ficha` (los umbrales: `presionMax`, `pHSeguridad`, `abv`…)

### El azúcar de la 2F tiene ventana, no es un mando de volumen
Subir el gas **no** es «echar más azúcar». El azúcar de la segunda fermentación tiene una ventana: metido a tiempo fermenta y hace burbuja; metido tarde solo endulza —se queda sin degradar y acabas con una kombucha dulce y sin más gas, lo contrario de lo que buscabas—. Más gas se consigue dando **más tiempo o más temperatura** a lo que ya hay, no añadiendo más.

Es el error que un principiante hace por intuición —«poco gas, pues más azúcar»— y donde el mentor gana su sitio. Por eso el aviso del cambio de saborizante (`gas_menos_por_cambio`) reconoce que saldrá con menos gas **sin** ofrecer añadir azúcar como arreglo: sería un consejo malo.

*No es* una barandilla de la ficha (no impide un valor): es **conceptual** — gobierna lo que el mentor puede aconsejar.

### Trueque
Una decisión que el motor tomó y explica: «he bajado el azúcar porque la cereza también aporta cuerpo». Es donde el mentor enseña oficio.

Va atado a una **condición** de la formulación, no a un paso. Lo COMPUTA el motor —no es una fila de `notification_rules` (ése es el sistema de recordatorios: pasos, medidas, vencimientos, cata)—: el catálogo evalúa la salida y devuelve `codigo + params`, y la voz lo redacta.

📁 `App\Mentor\CatalogoAvisos` (evalúa) · `App\Mentor\Voz` + `lang/es/motor.php · trueques` (redacta)

---

## 5. El aprendizaje

### Bucle 1 — dentro de la tanda
Catas, respondes cómo está, la predicción se ajusta y la ventana se mueve. Inmediato, en horas o días. La cata manda sobre el cálculo; el pH/temp lo corrigen. Mueve por **criterio, no por coeficiente estimado** (la iteración converge).

📁 `App\Mentor\BucleUno` · la ventana vive en `batches.forecast` (json) · el ritmo y las respuestas de cata los declara el modelo (`catasDisponibles`, `ritmoCata`)

### Bucle 2 — entre tandas
Cierras una tanda, valoras el resultado, y las predicciones futuras se estrechan para ti. Lento, a lo largo de meses. 🔜

### Calibración
Lo que el sistema aprende de una persona: **su sitio** (a qué velocidad fermenta su cocina) y **su lengua** (qué llama «en su punto»). Se acumula por usuario × guía.

*No es* un perfil de preferencias declarado. Se deriva de lo que pasó, no de lo que el usuario dijo que le gusta.

**Solo calibra la tanda `completada`.** Una `descartada` (accidente físico) es ausencia de señal, no señal negativa: se excluye del aprendizaje. La completada con mala valoración sí entra —dice «tu lengua rechaza esto aquí», que es exactamente lo que el bucle 2 necesita. En el esquema esto es `Batch::scopeCalibratable()` = `where('status','completed')`.

*No es* tampoco un requisito para que el motor funcione: se resuelve por **herencia con sobreescritura** (§6), así que sin calibración propia se usan los coeficientes generales. No hay arranque en frío.

🔜

### Backtest
Contrastar el modelo contra fermentaciones ya conocidas antes de escribir código. **Es una prueba de humo, no una calibración**: puede tumbar el modelo (si predice cuatro días donde nunca bajó de seis), pero no puede afinar coeficientes.

---

## 6. Conceptos del esquema

### Definición vs. hecho
La distinción que gobierna todo el modelo de datos.

- **Definición** — mutable, se corrige en un sitio, vale para todos: guía, pasos, acciones, receta, catálogo de ingredientes.
- **Hecho** — congelado al arrancar, pertenece a una persona y a un momento: la tanda entera y todo lo que cuelga de ella.

Nunca se corrige un hecho editando una definición.

### Congelar / snapshot
Copiar en la tanda todo lo que la define en el momento de arrancar: pasos, ingredientes, cantidades, ventanas previstas, versión del motor y ficha. Después, editar la guía no cambia esa tanda.

Un snapshot **autónomo** guarda sus propios valores y no necesita leer el catálogo vivo para saber qué llevaba. Esto es innegociable: en su día aparecieron 40 filas que sí dependían de la guía y costó una migración arreglarlo.

📁 `batch_steps`, `batch_ingredients`

### Puntero de origen
Una FK que dice *de dónde salió esto* pero de la que nada depende. Siempre nullable y `ON DELETE SET NULL`. Tiene valor diagnóstico, no funcional.

📁 `batch_ingredients.guide_ingredient_id`, `batch_steps.guide_step_id`

### Herencia con sobreescritura
**Un valor no definido se busca subiendo por su cadena hasta encontrar el primero que exista.** Es el mismo mecanismo en cuatro sitios del sistema, y por eso merece un solo nombre:

| Cadena | Hijo | Padre |
|---|---|---|
| **Ingredientes** | Sencha (sin perfil) | Té verde → Té |
| **Definición** | Tanda | Receta → Guía |
| **Predicción** | Calibración del usuario | Coeficientes generales de la ficha |
| **Consejos** 🔜 | Remedio del fermento para un síntoma | Categoría → General |

La regla es idéntica en los cuatro: **sube hasta encontrar un valor.**

Lo que resuelve, caso por caso: rellenar el perfil de «Té» una vez y que sirva para cuarenta tés; que la receta no tenga que repetir el proceso de la guía; que un remedio escrito una vez —«si hay moho, descarta»— valga para todos los fermentos sin repetirlo en cada uno; y —el más importante— que **el motor funcione el primer día, con cero historial**. No hay arranque en frío: si no hay calibración propia se usa la general. Cerrar una tanda no activa nada nuevo, simplemente pone un valor más específico donde antes se heredaba.

*No es* copiar. Congelar es copiar (la tanda se lleva su snapshot y deja de mirar arriba); heredar es mirar arriba en el momento de resolver. Un ingrediente sin perfil no tiene una copia del de su padre: no tiene ninguno, y se resuelve al leerlo.

**Contrapartida:** la herencia hace difícil saber *por qué* un valor es el que es. La defensa es que el sistema pueda decir de dónde vino cada número — y eso, que suena a detalle de depuración, es exactamente el estrechamiento visible del bucle 2: «antes te habría dicho 6-9 porque es lo general; contigo digo 6-7 porque es tu cocina». La información que hace falta para depurar es la misma que hace que el mentor suene a mentor.

### Migración inmutable
**Una migración ya ejecutada no se edita nunca.** Cambiar su archivo no revierte el dato: deja archivo y base desincronizados, y en silencio. Lo que se corrige, se corrige con otra migración.

Aprendido por poco: una edición de la ventana 2F de kombucha solo cuadró porque una reimportación posterior tapó la divergencia.

### Un solo dueño
El principio del proyecto. Todos los bugs encontrados han salido de que dos sitios reclamaban ser dueños de lo mismo: pasos duplicados, cantidades en tres tablas, el tiempo configurado en cuatro sitios, dos tablas midiendo el sabor.

Antes de añadir algo: comprobar que nada más reclama ser dueño de eso.

### Lista de defunción
El registro de lo que debe desaparecer, con el paso en que muere. Su razón de ser: **lo que se reemplaza se borra en el mismo commit.** Nada de «lo dejo por si acaso».

### Deuda con vencimiento
Una decisión de aplazar algo, **con el paso en que se resuelve escrito**. La diferencia entre una decisión y sedimento es exactamente ésa: que tenga fecha.

📁 `docs/Paso*_Deuda.md`

### Columnas vs. JSON
> Columnas donde el motor no llega. JSON donde sí.

Los coeficientes del motor cambiarán muchas veces; en columnas, cada cambio de opinión sería una migración y acabaría dejando columnas fósiles. Ingredientes, papeles, pasos, fechas, estados y trazabilidad van en columnas porque no dependen de si el motor acierta.

---

## 7. La persona

### Nivel
Cuánto quiere ver el usuario. **Es una lente, no una casta**: cualquiera puede cambiar de nivel cuando quiera.

- **Nivel 1** — elige con una frase («suave», «equilibrada»)
- **Nivel 2** — quiere ver el plano y afinar
- **Nivel 3** — productor: restricciones de casa, lotes, trazabilidad

📁 `users.fermenter_profile`

### Favorito vs. *like*
**Distintos, decidido explícitamente.** Favorito es un marcador privado («guardo ésta para mí»); *like* es una señal pública y contable («a 40 personas les gusta»). Cuando existan serán dos tablas con dos propósitos.

🔜

---

## 8. Vocabulario ↔ esquema

El código está en inglés y hablamos en español. Equivalencias:

| Hablamos de | En el esquema |
|---|---|
| Clasificación | `fermentation_categories` |
| Guía · fermento | `fermentation_guides` |
| Paso de guía | `guide_steps` |
| Acción | `step_actions` |
| Consejo | `hints` (cuatro niveles + idioma + síntoma) |
| Receta | `recipes` |
| Interruptor de opcional | `recipe_steps` |
| Ingrediente | `ingredients` |
| Papel | `guide_ingredients.papel` |
| Tanda · lote | `batches` |
| Paso de tanda | `batch_steps` |
| Evento, check-in | `batch_events`, `daily_logs` (medidas y catas) |
| Predicción de la tanda | `batches.forecast` (json) |
| Voz del mentor | `insights` (cierre) · motor en vivo (avisos/trueques/bucle 1) |
| Recordatorio condicionado | `notification_rules` (pasos, medidas, vencimientos, cata) |

**Nombres propios entre idiomas** — «**Afinador**» no se traduce (decisión 2026-07-28,
`Zymolab_Enlace_Fermenty.md` §8): en otros idiomas se glosa la primera vez por página
(*our Afinador — a fermentation tuner*) y después se usa a secas. Los **slugs de URL sí
se traducen** (`/en/tools/kombucha-tuner`): el slug posiciona, no nombra. El término
alemán queda por decidir antes de encender el DE — hasta entonces el copy DE mantiene
«Rechner» en títulos y el nombre propio en la prosa.

**Términos retirados** — si aparecen en código o documentos viejos, están obsoletos:

| Ya no se usa | Por qué |
|---|---|
| Tipo de fermento (`FermentType`) | La guía es el fermento (paso 3) |
| Versión de guía, guía vigente | El historial vive en el snapshot (paso 2) |
| Fase primaria/secundaria como capa | Es el paso, o el corte |
| Duración por defecto | El tiempo es predicción, no configuración |
| `guide_step_hints` | Absorbida por `hints` (2026-08): niveles + idioma + síntoma |

---

*Documento vivo. Cuando un término cambie de significado, se corrige aquí primero.*
