feat: fase 2+3 — expansion masiva multi-perfil + bug fixes + hardening

Fase 2 (expansion):
- Francesca: +6 etapas matematicas (5-10), +7 etapas lectura (3-9), +2 ortografia
- Sebastian: +8 etapas matematicas (4-11), +6 lectura, +3 historia, +3 geografia, +1 banderas
- Nuevas paginas: ortografia, lectura, geografia, matematicas
- ~900 ejercicios nuevos, paridad ~500 por perfil

Fase 3 (bug fixes + hardening):
- Fix: SessionLog auto-create en grade route
- Fix: engine cache (no borrar antes de revisar)
- Fix: FSRS cards actualizadas en flujo curriculum
- Fix: responseMs real (no hardcodeado 3000)
- Fix: options dentro de content en sebastian etapa-1
- Fix: trazo-letra consonantes crash
- Security: execFileSync anti-command-injection en TTS
- DB: 18 cascade deletes + indices + unique constraints
- APIs debug: reset, state, seed
- Docker: espeak-ng + prisma + user/group
- E2E: 23 tests nuevos (curriculum-flow.spec.ts)
- Code review: tailwind colors, middleware dev mode, eslint config

Verificado: tsc exit 0, build exit 0, 61 E2E tests pass
This commit is contained in:
renato97
2026-07-25 22:07:28 -03:00
parent 25614704dc
commit a3360ec6e4
67 changed files with 5294 additions and 213 deletions
+108
View File
@@ -0,0 +1,108 @@
import type { Stage } from "../types"
const lecturas = [
{
text: "La atmósfera terrestre está compuesta principalmente por nitrógeno (78%) y oxígeno (21%). El oxígeno es el gas que respiramos. La atmósfera nos protege de los rayos dañinos del sol y mantiene la temperatura del planeta estable.",
question: "¿Cuál es el gas más abundante en la atmósfera?",
options: ["Nitrógeno", "Oxígeno", "Dióxido de carbono"],
correcta: "Nitrógeno",
emoji: "🌍",
},
{
text: "El ciclo del agua describe cómo el agua se mueve por el planeta. El sol calienta el agua de los océanos y se evapora. Forma nubes. Luego llueve y el agua vuelve a la tierra. Corre por los ríos hasta el mar, y el ciclo comienza de nuevo.",
question: "¿Qué pasa después de que el agua se evapora?",
options: ["Forma nubes", "Vuelve al mar", "Se congela"],
correcta: "Forma nubes",
emoji: "☁️",
},
{
text: "Los imanes tienen dos polos: norte y sur. Los polos opuestos se atraen y los polos iguales se repelen. La Tierra tiene un campo magnético que funciona como un imán gigante. Por eso la brújula siempre apunta al norte.",
question: "¿Por qué la brújula apunta al norte?",
options: ["Por el campo magnético de la Tierra", "Porque el norte es más frío", "Porque el sol sale por el norte"],
correcta: "Por el campo magnético de la Tierra",
emoji: "🧲",
},
{
text: "El corazón humano late aproximadamente 100.000 veces al día. Bombea sangre a todo el cuerpo a través de las arterias. La sangre lleva oxígeno y nutrientes a las células. El ejercicio fortalece el corazón y lo mantiene saludable.",
question: "¿Cuántas veces late el corazón por día?",
options: ["100.000 veces", "10.000 veces", "1.000.000 veces"],
correcta: "100.000 veces",
emoji: "❤️",
},
{
text: "Los fósiles son restos de seres vivos que vivieron hace millones de años. Pueden ser huesos, dientes o huellas. Los paleontólogos los estudian para entender cómo era la vida en el pasado. El fósil más antiguo tiene 3.500 millones de años.",
question: "¿Quiénes estudian los fósiles?",
options: ["Los paleontólogos", "Los astrónomos", "Los geólogos"],
correcta: "Los paleontólogos",
emoji: "🦴",
},
{
text: "Los rayos son descargas eléctricas muy potentes que ocurren durante las tormentas. Un rayo puede alcanzar 30.000°C, más caliente que la superficie del sol. El trueno es el sonido que produce el aire al calentarse repentinamente. Primero vemos el rayo, luego escuchamos el trueno.",
question: "¿Qué temperatura puede alcanzar un rayo?",
options: ["30.000°C", "5.000°C", "100.000°C"],
correcta: "30.000°C",
emoji: "⚡",
},
{
text: "Charles Darwin fue un científico inglés que estudió la evolución de las especies. Viajó a las Islas Galápagos y observó que los pinzones tenían picos diferentes según su alimentación. Propuso que las especies cambian con el tiempo mediante la selección natural.",
question: "¿Qué observó Darwin en los pinzones?",
options: ["Tenían picos diferentes según su alimentación", "Tenían colores diferentes", "Cantaban distinto"],
correcta: "Tenían picos diferentes según su alimentación",
emoji: "🐦",
},
{
text: "La tabla periódica organiza todos los elementos químicos conocidos. Fue creada por Dimitri Mendeléyev en 1869. Los elementos están ordenados por su número atómico. Hay 118 elementos confirmados. El hidrógeno es el más ligero y el oxígeno es esencial para la vida.",
question: "¿Quién creó la tabla periódica?",
options: ["Mendeléyev", "Darwin", "Einstein"],
correcta: "Mendeléyev",
emoji: "🧪",
},
{
text: "Las montañas se forman por el movimiento de las placas tectónicas. Cuando dos placas chocan, la corteza terrestre se pliega y se eleva. La cordillera de los Andes se formó así. El Aconcagua, con 6.961 metros, es la montaña más alta de América.",
question: "¿Cómo se forman las montañas?",
options: ["Por el choque de placas tectónicas", "Por la erosión del viento", "Por los volcanes"],
correcta: "Por el choque de placas tectónicas",
emoji: "🏔️",
},
{
text: "La energía renovable proviene de fuentes naturales que no se agotan. La energía solar usa paneles para captar la luz del sol. La energía eólica usa el viento para mover molinos. La energía hidroeléctrica usa el agua. Estas energías no contaminan el ambiente.",
question: "¿Qué tienen en común las energías renovables?",
options: ["No contaminan y provienen de fuentes naturales", "Son muy caras", "Solo funcionan de día"],
correcta: "No contaminan y provienen de fuentes naturales",
emoji: "🌞",
},
]
const exercises: any[] = []
for (let i = 0; i < lecturas.length; i++) {
const l = lecturas[i]
exercises.push({
id: `seb-lect3-${i}`,
stage: 3,
skillCode: `lectura-informativa-${i}`,
type: "lectura",
prerequisites: i === 0 ? [`seb-lect2-9`] : [`seb-lect3-${i - 1}`],
content: {
instruction: "Leé el texto informativo y respondé:",
text: l.text,
audioText: l.text,
emoji: l.emoji,
options: l.options.map((o) => ({
label: o,
correct: o === l.correcta,
id: o.toLowerCase().slice(0, 15),
})),
},
functionalContext: "Comprender textos informativos y expositivos sobre ciencia, historia y naturaleza (método Decroly).",
errorType: "omision-letra",
})
}
const stage: Stage = {
id: 3,
name: "Textos informativos (Decroly)",
description: "Comprender textos expositivos sobre ciencia, historia y naturaleza con preguntas de comprensión.",
exercises,
}
export default stage