{
  "title": "Banco Canónico de Prompts para Gemini Canvas",
  "version": "2.1.0",
  "locale": "es-CO",
  "totalPossibilities": 10,
  "totalExercises": 20,
  "possibilities": [
    {
      "id": "doc-editorial",
      "number": "1",
      "name": "Redacción Editorial y Documentos Extensos",
      "badge": "Documentos",
      "icon": "📄",
      "image": "assets/images/canvas/canvas_documento.jpg",
      "imageCaption": "Reconstrucción del resultado esperado, dibujada con los datos del prompt. Tu Canvas puede variar en diseño, no en cifras ni en contenido.",
      "objective": "Estructurar propuestas técnicas, actas complejas y términos de referencia sin perder coherencia entre secciones.",
      "canvasControls": [
        "📏 Ajustar longitud",
        "📄 Exportar a Google Docs"
      ],
      "masterPrompt": "Actúa como profesional de infraestructura tecnológica que redacta términos de referencia para compras públicas.\n\nContexto: El centro de formación publicará una invitación abierta para comprar estaciones de trabajo. El comité de compras necesita un documento técnico que cualquier proveedor pueda cumplir sin favorecer una marca.\n\nDatos:\n\"\"\"\nNecesidad: Laboratorio de Inteligencia Artificial y Multimedia (ambiente 305). Lo usan 40 aprendices por jornada para entrenar modelos pequeños, practicar visión artificial y editar video. Los equipos actuales no tienen GPU dedicada.\nCantidad: 20 estaciones de trabajo.\nValor unitario de referencia (estudio de mercado, IVA incluido): COP 9.800.000.\n\nRequisitos mínimos (componente | requisito mínimo | tolerancia aceptada):\nProcesador | 14 núcleos y 20 hilos; frecuencia base de 2,5 GHz o superior | Equivalente o superior, de cualquier fabricante\nMemoria RAM | 32 GB DDR5 a 5600 MHz en doble canal | Ampliable a 64 GB\nGPU dedicada | 12 GB de VRAM GDDR6, compatible con PyTorch y TensorFlow | Equivalente o superior\nAlmacenamiento | SSD NVMe PCIe 4.0 de 1 TB; lectura de 5.000 MB/s o más | Una ranura M.2 adicional libre\nPantalla | 27 pulgadas, 2560 × 1440, panel IPS | Hasta 32 pulgadas\n\nCronograma (plazo | actividad):\nSemanas 1 y 2 | Entrega de los equipos en el ambiente 305\nSemana 3 | Instalación y configuración\nSemana 4 | Pruebas de rendimiento y recibo a satisfacción\n\nGarantía mínima: 36 meses en sitio, con atención de fallas críticas en máximo 24 horas hábiles.\n\"\"\"\n\nInstrucción: En Canvas, redacta los términos de referencia con estas cuatro secciones numeradas:\n1. Resumen ejecutivo y justificación de la necesidad formativa, en máximo 120 palabras.\n2. Especificaciones técnicas mínimas: una tabla con las columnas Componente, Requisito mínimo y Tolerancia aceptada, con las 5 filas de los datos.\n3. Presupuesto y cronograma: calcula el presupuesto total (cantidad × valor unitario) y presenta el cronograma en una tabla de 3 filas.\n4. Criterios de aceptación y garantía: una lista de verificación que el comité pueda marcar al recibir los equipos.\n\nRestricciones:\n- Copia los requisitos y valores tal como aparecen en los datos; no agregues componentes ni cambies cifras.\n- No menciones marcas ni modelos comerciales.\n- Escribe los valores en pesos colombianos con punto de miles, por ejemplo COP 1.000.000.\n\nFormato: Documento formal en español de Colombia, con títulos numerados del 1 al 4 y dos tablas.",
      "expectedResultSummary": "El documento tiene exactamente 4 secciones numeradas: resumen ejecutivo, especificaciones técnicas, presupuesto y cronograma, y aceptación y garantía. La tabla de especificaciones tiene 5 filas (Procesador, Memoria RAM, GPU dedicada, Almacenamiento y Pantalla) con los mismos valores de los datos. El presupuesto total es COP 196.000.000 (20 × COP 9.800.000). El cronograma tiene 3 filas: semanas 1 y 2, semana 3 y semana 4. La garantía dice 36 meses en sitio y atención de fallas críticas en máximo 24 horas hábiles. No aparece ninguna marca ni modelo comercial de procesadores, tarjetas gráficas o pantallas. El resumen ejecutivo tiene 120 palabras o menos.",
      "variationPrompts": [
        "Selecciona el resumen ejecutivo y haz clic en 'Ajustar longitud > Más corto' para generar una versión de 2 oraciones para la junta directiva.",
        "Cambia el tono a 'Divulgativo' para presentar las especificaciones a los aprendices en la jornada de inducción."
      ],
      "exercises": [
        {
          "id": "doc-a-informe-desercion",
          "title": "Informe de deserción con infografía automática",
          "level": "Básico",
          "duration": "25 min",
          "canvasFeature": "Documento + menú Crear → Infografía",
          "goal": "Convertir una tabla de deserción en un informe ejecutivo claro y, desde el mismo Canvas, generar una infografía visual lista para presentar al comité.",
          "scenario": "La coordinación académica debe presentar el lunes cómo bajó la deserción durante 2025 y qué acciones funcionaron. Solo tienes la tabla trimestral y la encuesta de salida.",
          "promptParts": {
            "role": "analista de calidad educativa que prepara informes para comités directivos",
            "context": "El comité académico se reúne el lunes para decidir qué acciones contra la deserción mantener en 2026. Leerá el informe en cinco minutos.",
            "data": "DATOS DE DESERCIÓN 2025 — Centro de Gestión Empresarial (datos de práctica)\n\nTrimestre,Matriculados,Desertores,Tasa\n2025-T1,420,38,9.0%\n2025-T2,395,31,7.8%\n2025-T3,402,22,5.5%\n2025-T4,388,14,3.6%\n\nCausas reportadas en la encuesta de salida (105 desertores):\n- Dificultad económica: 41\n- Cruce con horario laboral: 27\n- Problemas de conectividad: 19\n- Salud o situación familiar: 10\n- Otras causas: 8\n\nAcciones aplicadas desde el T2:\n- Apoyo de sostenimiento para 60 aprendices (desde abril).\n- Franja nocturna nueva en dos programas (desde julio).\n- Préstamo de 35 portátiles con módem (desde septiembre).",
            "instruction": "En Canvas, redacta un informe ejecutivo con:",
            "task": [
              "Un resumen de máximo cinco líneas con la conclusión principal.",
              "Una tabla con la tasa de cada trimestre y su variación en puntos frente al trimestre anterior.",
              "Las causas de deserción ordenadas de mayor a menor, con su porcentaje.",
              "Tres recomendaciones para 2026, cada una con un responsable sugerido y un indicador."
            ],
            "constraints": [
              "Usa solo estos datos; si una acción no se puede relacionar con certeza con un resultado, dilo.",
              "No inventes cifras ni nombres de personas."
            ],
            "format": "Títulos numerados, una tabla y frases cortas en español de Colombia."
          },
          "steps": [
            "Activa Canvas en la barra del chat de Gemini, pega el prompt y envíalo.",
            "Revisa el resultado con la lista «Verifica tu resultado» y marca lo que ya cumple.",
            "Selecciona la sección de recomendaciones y escribe en el cuadro de edición: «Agrega una meta numérica para 2026 a cada recomendación».",
            "Usa «Ajustar longitud → Más corto» y comprueba que la tabla se conserve.",
            "Abre el menú «Crear» del Canvas y elige «Infografía».",
            "Exporta el informe con «Compartir y exportar → Exportar a Documentos de Google»."
          ],
          "expected": [
            "El resumen menciona la caída de 9,0 % a 3,6 % (5,4 puntos).",
            "La tabla muestra las cuatro tasas y la variación entre trimestres.",
            "La dificultad económica aparece como primera causa (39 % de 105).",
            "Cada recomendación tiene responsable, fecha e indicador.",
            "La infografía generada muestra la tendencia descendente y las tres acciones.",
            "El documento quedó guardado en Google Docs."
          ],
          "stretch": "Pide en el chat: «Crea una página web a partir de este informe con un gráfico de líneas animado y tarjetas para cada acción». Compara la página con la infografía.",
          "prompt": "Actúa como analista de calidad educativa que prepara informes para comités directivos.\n\nContexto: El comité académico se reúne el lunes para decidir qué acciones contra la deserción mantener en 2026. Leerá el informe en cinco minutos.\n\nDatos:\n\"\"\"\nDATOS DE DESERCIÓN 2025 — Centro de Gestión Empresarial (datos de práctica)\n\nTrimestre,Matriculados,Desertores,Tasa\n2025-T1,420,38,9.0%\n2025-T2,395,31,7.8%\n2025-T3,402,22,5.5%\n2025-T4,388,14,3.6%\n\nCausas reportadas en la encuesta de salida (105 desertores):\n- Dificultad económica: 41\n- Cruce con horario laboral: 27\n- Problemas de conectividad: 19\n- Salud o situación familiar: 10\n- Otras causas: 8\n\nAcciones aplicadas desde el T2:\n- Apoyo de sostenimiento para 60 aprendices (desde abril).\n- Franja nocturna nueva en dos programas (desde julio).\n- Préstamo de 35 portátiles con módem (desde septiembre).\n\"\"\"\n\nInstrucción: En Canvas, redacta un informe ejecutivo con:\n1. Un resumen de máximo cinco líneas con la conclusión principal.\n2. Una tabla con la tasa de cada trimestre y su variación en puntos frente al trimestre anterior.\n3. Las causas de deserción ordenadas de mayor a menor, con su porcentaje.\n4. Tres recomendaciones para 2026, cada una con un responsable sugerido y un indicador.\n\nRestricciones:\n- Usa solo estos datos; si una acción no se puede relacionar con certeza con un resultado, dilo.\n- No inventes cifras ni nombres de personas.\n\nFormato: Títulos numerados, una tabla y frases cortas en español de Colombia."
        },
        {
          "id": "doc-b-propuesta-huerta",
          "title": "Propuesta de proyecto productivo con cuestionario de repaso",
          "level": "Intermedio",
          "duration": "30 min",
          "canvasFeature": "Exportar a Docs + menú Crear → Cuestionario",
          "goal": "Redactar una propuesta formal de proyecto productivo con presupuesto y cronograma, y generar un cuestionario para que el equipo de aprendices domine la propuesta.",
          "scenario": "Un equipo quiere financiar una huerta hidropónica con recursos del centro. La propuesta debe convencer al comité y el equipo debe sustentarla sin leer.",
          "promptParts": {
            "role": "formulador de proyectos productivos para convocatorias institucionales",
            "context": "Un equipo de aprendices presentará esta propuesta al comité de proyectos del centro para obtener financiación. El comité decide por viabilidad, costos y valor formativo.",
            "data": "PROYECTO PRODUCTIVO: HUERTA HIDROPÓNICA ESCOLAR (datos de práctica)\n\nEquipo: 12 aprendices del programa Producción Agropecuaria Ecológica y 1 instructor líder.\nEspacio: terraza de 60 m² con punto de agua y energía.\nDuración: 16 semanas.\nMeta de producción: 180 kg de lechuga y 40 kg de cilantro por ciclo de 8 semanas.\n\nPresupuesto (COP):\nEstructura y canales NFT,3.200.000\nBomba y temporizador,850.000\nSemillas y plántulas,420.000\nSolución nutritiva 12 meses,960.000\nSensores de pH y conductividad,1.150.000\nCapacitación y certificación BPA,1.420.000\nTOTAL,8.000.000\n\nCronograma:\nSemanas 1-3: diseño e instalación de la estructura.\nSemanas 4-5: siembra y calibración de sensores.\nSemanas 6-13: dos ciclos de cultivo con registro diario.\nSemanas 14-16: venta en la feria del centro y evaluación.\n\nIndicadores: kg cosechados, costo por kg, % de plántulas perdidas, ingresos por venta.",
            "instruction": "En Canvas, redacta la propuesta con:",
            "task": [
              "El problema, la justificación pedagógica y el objetivo general.",
              "Tres objetivos específicos medibles, cada uno ligado a un indicador de los datos.",
              "El presupuesto con el porcentaje de cada rubro y el cronograma por fases.",
              "El costo por kilogramo si toda la inversión se carga al primer ciclo y los kilos que hay que vender a 9.000 COP para recuperarla.",
              "Tres riesgos con su medida de mitigación."
            ],
            "constraints": [
              "Muestra los cálculos paso a paso.",
              "Montos en pesos colombianos con punto de miles."
            ],
            "format": "Documento formal con títulos numerados y dos tablas."
          },
          "steps": [
            "Activa Canvas en la barra del chat de Gemini, pega el prompt y envíalo.",
            "Revisa el resultado con la lista «Verifica tu resultado» y marca lo que ya cumple.",
            "Selecciona el párrafo de justificación y escribe: «Relaciónalo con las competencias del programa de formación».",
            "Usa «Cambiar tono → Más formal» para la versión que irá al comité.",
            "Abre «Crear → Cuestionario» para que el equipo repase la propuesta antes de sustentarla.",
            "Exporta la propuesta a Documentos de Google desde «Compartir y exportar»."
          ],
          "expected": [
            "La tabla de presupuesto suma 8.000.000 COP y la estructura NFT pesa 40 %.",
            "Los tres objetivos específicos tienen un indicador medible.",
            "El cálculo de costo por kilogramo y punto de equilibrio aparece paso a paso.",
            "El cronograma cubre las 16 semanas sin huecos.",
            "El cuestionario generado tiene preguntas sobre presupuesto, cronograma e indicadores.",
            "Existe una copia de la propuesta en Google Docs."
          ],
          "stretch": "Pide: «Agrega un escenario optimista con dos ciclos más y compáralo en una tabla con el escenario base».",
          "prompt": "Actúa como formulador de proyectos productivos para convocatorias institucionales.\n\nContexto: Un equipo de aprendices presentará esta propuesta al comité de proyectos del centro para obtener financiación. El comité decide por viabilidad, costos y valor formativo.\n\nDatos:\n\"\"\"\nPROYECTO PRODUCTIVO: HUERTA HIDROPÓNICA ESCOLAR (datos de práctica)\n\nEquipo: 12 aprendices del programa Producción Agropecuaria Ecológica y 1 instructor líder.\nEspacio: terraza de 60 m² con punto de agua y energía.\nDuración: 16 semanas.\nMeta de producción: 180 kg de lechuga y 40 kg de cilantro por ciclo de 8 semanas.\n\nPresupuesto (COP):\nEstructura y canales NFT,3.200.000\nBomba y temporizador,850.000\nSemillas y plántulas,420.000\nSolución nutritiva 12 meses,960.000\nSensores de pH y conductividad,1.150.000\nCapacitación y certificación BPA,1.420.000\nTOTAL,8.000.000\n\nCronograma:\nSemanas 1-3: diseño e instalación de la estructura.\nSemanas 4-5: siembra y calibración de sensores.\nSemanas 6-13: dos ciclos de cultivo con registro diario.\nSemanas 14-16: venta en la feria del centro y evaluación.\n\nIndicadores: kg cosechados, costo por kg, % de plántulas perdidas, ingresos por venta.\n\"\"\"\n\nInstrucción: En Canvas, redacta la propuesta con:\n1. El problema, la justificación pedagógica y el objetivo general.\n2. Tres objetivos específicos medibles, cada uno ligado a un indicador de los datos.\n3. El presupuesto con el porcentaje de cada rubro y el cronograma por fases.\n4. El costo por kilogramo si toda la inversión se carga al primer ciclo y los kilos que hay que vender a 9.000 COP para recuperarla.\n5. Tres riesgos con su medida de mitigación.\n\nRestricciones:\n- Muestra los cálculos paso a paso.\n- Montos en pesos colombianos con punto de miles.\n\nFormato: Documento formal con títulos numerados y dos tablas."
        }
      ],
      "expected": [
        {
          "criterion": "El documento tiene exactamente 4 secciones numeradas: resumen ejecutivo, especificaciones técnicas, presupuesto y cronograma, y aceptación y garantía.",
          "how": "Revisa los títulos del Canvas de arriba abajo."
        },
        {
          "criterion": "La tabla de especificaciones tiene 5 filas (Procesador, Memoria RAM, GPU dedicada, Almacenamiento y Pantalla) con los mismos valores de los datos.",
          "how": "Compara fila por fila con el bloque de datos del prompt."
        },
        {
          "criterion": "El presupuesto total es COP 196.000.000 (20 × COP 9.800.000).",
          "how": "Busca «196.000.000» con Ctrl+F en el Canvas."
        },
        {
          "criterion": "El cronograma tiene 3 filas: semanas 1 y 2, semana 3 y semana 4.",
          "how": "Cuenta las filas de la segunda tabla."
        },
        {
          "criterion": "La garantía dice 36 meses en sitio y atención de fallas críticas en máximo 24 horas hábiles.",
          "how": "Lee la sección 4."
        },
        {
          "criterion": "No aparece ninguna marca ni modelo comercial de procesadores, tarjetas gráficas o pantallas.",
          "how": "Busca nombres de fabricantes con Ctrl+F; no debe haber resultados."
        },
        {
          "criterion": "El resumen ejecutivo tiene 120 palabras o menos.",
          "how": "Si se pasa, selecciónalo y usa «Ajustar longitud» → «Más corto»."
        }
      ],
      "followUps": [],
      "imageSpec": {
        "blocks": [
          {
            "type": "heading",
            "text": "TÉRMINOS DE REFERENCIA · LABORATORIO DE IA Y MULTIMEDIA"
          },
          {
            "type": "callout",
            "title": "1. Resumen ejecutivo",
            "text": "El centro de formación requiere 20 estaciones de trabajo para el Laboratorio de Inteligencia Artificial y Multimedia, donde 40 aprendices por jornada entrenan modelos pequeños, practican visión artificial y editan video. El presupuesto de referencia es de COP 196.000.000, y la entrega, la instalación y las pruebas se completan en 4 semanas."
          },
          {
            "type": "subheading",
            "text": "2. Especificaciones técnicas mínimas"
          },
          {
            "type": "table",
            "headers": [
              "Componente",
              "Requisito mínimo",
              "Tolerancia aceptada"
            ],
            "rows": [
              [
                "Procesador",
                "14 núcleos y 20 hilos; frecuencia base de 2,5 GHz o superior",
                "Equivalente o superior, de cualquier fabricante"
              ],
              [
                "Memoria RAM",
                "32 GB DDR5 a 5600 MHz en doble canal",
                "Ampliable a 64 GB"
              ],
              [
                "GPU dedicada",
                "12 GB de VRAM GDDR6, compatible con PyTorch y TensorFlow",
                "Equivalente o superior"
              ],
              [
                "Almacenamiento",
                "SSD NVMe PCIe 4.0 de 1 TB; lectura de 5.000 MB/s o más",
                "Una ranura M.2 adicional libre"
              ],
              [
                "Pantalla",
                "27 pulgadas, 2560 × 1440, panel IPS",
                "Hasta 32 pulgadas"
              ]
            ]
          },
          {
            "type": "subheading",
            "text": "3. Presupuesto: 20 × COP 9.800.000 = COP 196.000.000"
          },
          {
            "type": "table",
            "headers": [
              "Plazo",
              "Actividad"
            ],
            "rows": [
              [
                "Semanas 1 y 2",
                "Entrega de los equipos en el ambiente 305"
              ],
              [
                "Semana 3",
                "Instalación y configuración"
              ],
              [
                "Semana 4",
                "Pruebas de rendimiento y recibo a satisfacción"
              ]
            ]
          },
          {
            "type": "subheading",
            "text": "4. Criterios de aceptación y garantía"
          },
          {
            "type": "bullets",
            "items": [
              "☐ Cada estación cumple los requisitos mínimos de la tabla.",
              "☐ Garantía de 36 meses en sitio; fallas críticas atendidas en máximo 24 horas hábiles."
            ]
          }
        ],
        "file": "canvas_documento.jpg",
        "docTitle": "Terminos_Referencia_Laboratorio_IA.docx",
        "chat": "Abrí un Canvas con los términos de referencia: cuatro secciones numeradas, la tabla de especificaciones con las 5 filas de los datos, el presupuesto calculado y el cronograma. Puedes ajustar la longitud o exportarlo a Google Docs."
      }
    },
    {
      "id": "code-prototyping",
      "number": "2",
      "name": "Desarrollo y Prototipado Web en Vivo",
      "badge": "Código Web",
      "icon": "💻",
      "image": "assets/images/canvas/canvas_codigo.jpg",
      "imageCaption": "Reconstrucción del resultado esperado, dibujada con los datos del prompt. Tu Canvas puede variar en diseño, no en cifras ni en contenido.",
      "objective": "Construir aplicaciones interactivas en HTML/CSS/JS ejecutables directamente dentro de Gemini Canvas con previsualización en vivo.",
      "canvasControls": [
        "▶ Vista previa",
        "🧮 Regla de estado"
      ],
      "masterPrompt": "Actúa como desarrollador frontend que construye prototipos accesibles para equipos docentes.\n\nContexto: La coordinación quiere probar un tablero sencillo antes de pedir un desarrollo formal. Lo usarán instructores sin conocimientos técnicos.\n\nDatos:\n\"\"\"\nFicha 2871 · Seguimiento semanal (porcentajes de 0 a 100)\nAprendiz | Asistencia | Evidencias entregadas\nAna María García | 95 | 100\nCarlos Andrés Pérez | 88 | 90\nJulián David Torres | 70 | 65\nLaura Sofía Mendoza | 92 | 80\nDiego Alejandro Ruiz | 64 | 72\nValentina Ríos Cárdenas | 86 | 95\n\nRegla de estado:\n- Requiere apoyo: asistencia o evidencias por debajo de 70.\n- Al día: asistencia y evidencias de 85 o más.\n- En progreso: cualquier otro caso.\n\"\"\"\n\nInstrucción: En Canvas, crea una sola página HTML, con CSS y JavaScript, que incluya:\n1. Dos tarjetas KPI calculadas con JavaScript a partir de los datos: asistencia promedio y evidencias promedio, con un decimal.\n2. Una tabla con los 6 aprendices y una columna Estado calculada con la regla de los datos.\n3. Un buscador por nombre que no distinga mayúsculas y un filtro por estado que funcionen al mismo tiempo.\n4. Una gráfica de barras en SVG con las evidencias de cada aprendiz.\n\nRestricciones:\n- No uses librerías externas, CDN ni npm: todo debe funcionar en la vista previa de Canvas.\n- Guarda los datos en un arreglo de JavaScript; no escribas los resultados a mano.\n- Usa el color #2D5A4C para los elementos principales y conserva un contraste AA.\n\nFormato: Un único archivo HTML con comentarios breves en español.",
      "expectedResultSummary": "La tarjeta de asistencia muestra 82,5 % y la de evidencias 83,7 % (también vale con punto decimal). La tabla tiene 6 filas y la columna Estado suma 3 Al día, 1 En progreso y 2 Requiere apoyo. Al filtrar «Requiere apoyo» quedan 2 filas: Julián David Torres y Diego Alejandro Ruiz. Al escribir «ana» en el buscador queda 1 fila (Ana María García); al borrar el texto vuelven las 6. La gráfica tiene 6 barras; la más alta es Ana María García (100) y la más baja Julián David Torres (65). El código no contiene etiquetas script con src, sentencias import ni enlaces a CDN.",
      "variationPrompts": [
        "Añade un botón de exportar tabla a formato CSV directamente en el navegador.",
        "Cambia la vista de la gráfica de barras a un gráfico circular (donut) de distribución porcentual de estados."
      ],
      "exercises": [
        {
          "id": "code-a-simulador-presupuesto",
          "title": "Simulador de presupuesto con sliders y gráfico de dona en vivo",
          "level": "Intermedio",
          "duration": "30 min",
          "canvasFeature": "Canvas de código + Vista previa ejecutable",
          "goal": "Construir una aplicación web que recalcula en tiempo real un presupuesto, con controles deslizantes, alertas de negocio y un gráfico animado.",
          "scenario": "El comité de la feria necesita jugar con la distribución del presupuesto en la reunión, ver el impacto en pesos y detectar de inmediato una distribución riesgosa.",
          "promptParts": {
            "role": "desarrollador frontend que crea herramientas de decisión para equipos no técnicos",
            "context": "El comité de la feria repartirá su presupuesto en una reunión en vivo y quiere ver al instante el efecto de cada cambio.",
            "data": "PRESUPUESTO DE LA FERIA DE PROYECTOS (datos de práctica)\n\nPresupuesto total: 12.000.000 COP\n\nCategoría,Porcentaje inicial,Mínimo permitido\nLogística y montaje,35,25\nAlimentación,25,15\nPublicidad,15,5\nPremios,15,10\nImprevistos,10,5\n\nReglas de negocio:\n1. La suma de porcentajes siempre debe ser 100.\n2. Si una categoría queda por debajo de su mínimo, se muestra una alerta roja con el nombre de la categoría.\n3. Si imprevistos baja de 8 %, se muestra una advertencia amarilla: \"Riesgo financiero alto\".\n4. Cada categoría muestra su valor en pesos (porcentaje x 12.000.000).",
            "instruction": "En Canvas, crea una aplicación web de una sola página que permita:",
            "task": [
              "Ajustar el porcentaje de cada categoría con un control deslizante y ver su valor en pesos.",
              "Mantener la suma en 100 % repartiendo la diferencia entre las demás categorías.",
              "Ver la distribución en un gráfico de dona que se actualice en vivo (Chart.js desde CDN).",
              "Recibir las alertas de las reglas de negocio.",
              "Restablecer los valores iniciales."
            ],
            "constraints": [
              "Formato de moneda colombiano.",
              "Diseño responsivo y accesible: contraste AA y controles con etiqueta."
            ],
            "format": "Un solo archivo HTML con CSS y JavaScript."
          },
          "steps": [
            "Activa Canvas en la barra del chat de Gemini, pega el prompt y envíalo.",
            "Cuando termine, abre la «Vista previa» del Canvas para usar la aplicación.",
            "Sube «Logística» a 50 % y observa cómo se redistribuyen las demás categorías.",
            "Baja «Imprevistos» a 6 % y comprueba que aparezca la advertencia.",
            "Si la vista previa muestra un error, escribe en el chat: «Corrige el error que aparece en la consola».",
            "Pide en el chat: «Agrega un botón para descargar la distribución en CSV y un interruptor de modo oscuro»."
          ],
          "expected": [
            "La app abre en la vista previa sin errores.",
            "Los valores iniciales en pesos son 4.200.000 / 3.000.000 / 1.800.000 / 1.800.000 / 1.200.000.",
            "La suma de porcentajes se mantiene en 100 al mover cualquier control.",
            "La dona cambia en vivo con animación.",
            "Las alertas roja y amarilla aparecen según las reglas.",
            "El CSV descargado contiene las cinco categorías y el modo oscuro funciona."
          ],
          "stretch": "Pide: «Agrega un segundo escenario guardado y un gráfico de barras que compare ambos escenarios lado a lado».",
          "prompt": "Actúa como desarrollador frontend que crea herramientas de decisión para equipos no técnicos.\n\nContexto: El comité de la feria repartirá su presupuesto en una reunión en vivo y quiere ver al instante el efecto de cada cambio.\n\nDatos:\n\"\"\"\nPRESUPUESTO DE LA FERIA DE PROYECTOS (datos de práctica)\n\nPresupuesto total: 12.000.000 COP\n\nCategoría,Porcentaje inicial,Mínimo permitido\nLogística y montaje,35,25\nAlimentación,25,15\nPublicidad,15,5\nPremios,15,10\nImprevistos,10,5\n\nReglas de negocio:\n1. La suma de porcentajes siempre debe ser 100.\n2. Si una categoría queda por debajo de su mínimo, se muestra una alerta roja con el nombre de la categoría.\n3. Si imprevistos baja de 8 %, se muestra una advertencia amarilla: \"Riesgo financiero alto\".\n4. Cada categoría muestra su valor en pesos (porcentaje x 12.000.000).\n\"\"\"\n\nInstrucción: En Canvas, crea una aplicación web de una sola página que permita:\n1. Ajustar el porcentaje de cada categoría con un control deslizante y ver su valor en pesos.\n2. Mantener la suma en 100 % repartiendo la diferencia entre las demás categorías.\n3. Ver la distribución en un gráfico de dona que se actualice en vivo (Chart.js desde CDN).\n4. Recibir las alertas de las reglas de negocio.\n5. Restablecer los valores iniciales.\n\nRestricciones:\n- Formato de moneda colombiano.\n- Diseño responsivo y accesible: contraste AA y controles con etiqueta.\n\nFormato: Un solo archivo HTML con CSS y JavaScript."
        },
        {
          "id": "code-b-kanban-burndown",
          "title": "Tablero Kanban arrastrable con gráfico de avance del sprint",
          "level": "Avanzado",
          "duration": "35 min",
          "canvasFeature": "Canvas de código + Vista previa + iteración por selección",
          "goal": "Crear un tablero Kanban funcional con arrastrar y soltar, contadores por columna y un gráfico burndown que compara el avance ideal y el real.",
          "scenario": "El equipo del sprint 3 quiere ver en una sola pantalla qué tareas faltan y si va atrasado frente a la línea ideal.",
          "promptParts": {
            "role": "desarrollador frontend con experiencia en herramientas ágiles",
            "context": "El equipo del sprint 3 necesita ver en una sola pantalla qué falta y si va atrasado frente al plan.",
            "data": "TAREAS DEL SPRINT 3 — APP DE CITAS DEL CENTRO (datos de práctica)\n\nid,titulo,responsable,puntos,estado\nT1,Diseñar pantalla de login,Ana,3,Hecho\nT2,Conectar API de usuarios,Luis,5,Hecho\nT3,Validar formulario de cita,Marta,3,En curso\nT4,Calendario de disponibilidad,Jorge,8,En curso\nT5,Notificación por correo,Sara,5,Por hacer\nT6,Pruebas unitarias del backend,Iván,5,Por hacer\nT7,Panel del instructor,Ana,8,Por hacer\nT8,Accesibilidad y contraste,Marta,3,Hecho\n\nSprint de 10 días con 40 puntos.\nPuntos pendientes reales por día (corte del día 10): 40,40,37,37,34,34,32,29,29,29",
            "instruction": "En Canvas, crea un tablero Kanban web que:",
            "task": [
              "Muestre las tareas en las columnas Por hacer, En curso y Hecho.",
              "Permita mover las tarjetas arrastrándolas y también con el teclado.",
              "Sume los puntos de cada columna y muestre el avance del sprint.",
              "Incluya un gráfico burndown con la línea ideal y la real (Chart.js desde CDN).",
              "Filtre las tarjetas por responsable."
            ],
            "constraints": [
              "Sin frameworks: solo HTML, CSS y JavaScript.",
              "Cada tarjeta debe poder enfocarse y tener una etiqueta para lectores de pantalla."
            ],
            "format": "Un solo archivo HTML ejecutable."
          },
          "steps": [
            "Activa Canvas en la barra del chat de Gemini, pega el prompt y envíalo.",
            "Cuando termine, abre la «Vista previa» del Canvas para usar la aplicación.",
            "Arrastra la tarea T3 a «Hecho» y observa los contadores y la barra de avance.",
            "Filtra por «Ana» y comprueba qué tarjetas quedan.",
            "En el código, selecciona la función del gráfico y escribe: «Marca los días en que la línea real supera a la ideal».",
            "Pide en el chat: «Guarda el estado del tablero en localStorage» y recarga la vista previa."
          ],
          "expected": [
            "Al inicio, Hecho suma 11 puntos (T1, T2, T8) y el progreso es 27,5 %.",
            "Las tarjetas se mueven con ratón y con teclado.",
            "El burndown muestra la línea ideal y la real; la real queda por encima desde el día 2 y cierra con 29 puntos pendientes.",
            "El filtro por responsable funciona sin recargar.",
            "Los contadores se actualizan al soltar una tarjeta.",
            "El estado persiste al recargar la vista previa."
          ],
          "stretch": "Pide: «Agrega una columna Bloqueado con límite WIP de 2 tarjetas y una alerta visual cuando se supere».",
          "prompt": "Actúa como desarrollador frontend con experiencia en herramientas ágiles.\n\nContexto: El equipo del sprint 3 necesita ver en una sola pantalla qué falta y si va atrasado frente al plan.\n\nDatos:\n\"\"\"\nTAREAS DEL SPRINT 3 — APP DE CITAS DEL CENTRO (datos de práctica)\n\nid,titulo,responsable,puntos,estado\nT1,Diseñar pantalla de login,Ana,3,Hecho\nT2,Conectar API de usuarios,Luis,5,Hecho\nT3,Validar formulario de cita,Marta,3,En curso\nT4,Calendario de disponibilidad,Jorge,8,En curso\nT5,Notificación por correo,Sara,5,Por hacer\nT6,Pruebas unitarias del backend,Iván,5,Por hacer\nT7,Panel del instructor,Ana,8,Por hacer\nT8,Accesibilidad y contraste,Marta,3,Hecho\n\nSprint de 10 días con 40 puntos.\nPuntos pendientes reales por día (corte del día 10): 40,40,37,37,34,34,32,29,29,29\n\"\"\"\n\nInstrucción: En Canvas, crea un tablero Kanban web que:\n1. Muestre las tareas en las columnas Por hacer, En curso y Hecho.\n2. Permita mover las tarjetas arrastrándolas y también con el teclado.\n3. Sume los puntos de cada columna y muestre el avance del sprint.\n4. Incluya un gráfico burndown con la línea ideal y la real (Chart.js desde CDN).\n5. Filtre las tarjetas por responsable.\n\nRestricciones:\n- Sin frameworks: solo HTML, CSS y JavaScript.\n- Cada tarjeta debe poder enfocarse y tener una etiqueta para lectores de pantalla.\n\nFormato: Un solo archivo HTML ejecutable."
        }
      ],
      "expected": [
        {
          "criterion": "La tarjeta de asistencia muestra 82,5 % y la de evidencias 83,7 % (también vale con punto decimal).",
          "how": "Abre la vista previa del Canvas."
        },
        {
          "criterion": "La tabla tiene 6 filas y la columna Estado suma 3 Al día, 1 En progreso y 2 Requiere apoyo.",
          "how": "Cuenta los estados en la vista previa."
        },
        {
          "criterion": "Al filtrar «Requiere apoyo» quedan 2 filas: Julián David Torres y Diego Alejandro Ruiz.",
          "how": "Usa el filtro por estado."
        },
        {
          "criterion": "Al escribir «ana» en el buscador queda 1 fila (Ana María García); al borrar el texto vuelven las 6.",
          "how": "Escribe en el buscador de la vista previa."
        },
        {
          "criterion": "La gráfica tiene 6 barras; la más alta es Ana María García (100) y la más baja Julián David Torres (65).",
          "how": "Observa la gráfica en la vista previa."
        },
        {
          "criterion": "El código no contiene etiquetas script con src, sentencias import ni enlaces a CDN.",
          "how": "Pasa a la vista de código y busca «src=» e «import»."
        }
      ],
      "followUps": [],
      "imageSpec": {
        "blocks": [
          {
            "type": "heading",
            "text": "Seguimiento de aprendizaje · Ficha 2871"
          },
          {
            "type": "kpis",
            "items": [
              {
                "label": "Asistencia promedio",
                "value": "82,5 %"
              },
              {
                "label": "Evidencias promedio",
                "value": "83,7 %"
              }
            ]
          },
          {
            "type": "table",
            "headers": [
              "Aprendiz",
              "Asistencia",
              "Evidencias",
              "Estado"
            ],
            "rows": [
              [
                "Ana María García",
                "95",
                "100",
                "Al día"
              ],
              [
                "Carlos Andrés Pérez",
                "88",
                "90",
                "Al día"
              ],
              [
                "Julián David Torres",
                "70",
                "65",
                "Requiere apoyo"
              ],
              [
                "Laura Sofía Mendoza",
                "92",
                "80",
                "En progreso"
              ],
              [
                "Diego Alejandro Ruiz",
                "64",
                "72",
                "Requiere apoyo"
              ],
              [
                "Valentina Ríos Cárdenas",
                "86",
                "95",
                "Al día"
              ]
            ]
          },
          {
            "type": "bars",
            "title": "Evidencias por aprendiz",
            "items": [
              {
                "label": "Ana",
                "value": 100
              },
              {
                "label": "Carlos",
                "value": 90
              },
              {
                "label": "Julián",
                "value": 65
              },
              {
                "label": "Laura",
                "value": 80
              },
              {
                "label": "Diego",
                "value": 72
              },
              {
                "label": "Valentina",
                "value": 95
              }
            ]
          }
        ],
        "file": "canvas_codigo.jpg",
        "docTitle": "Tablero_Seguimiento_Ficha_2871.html",
        "chat": "Creé la página en un Canvas de código. Los promedios, los estados y la gráfica se calculan con JavaScript a partir del arreglo de datos. Usa «Vista previa» para probar el buscador y el filtro."
      }
    },
    {
      "id": "data-matrix",
      "number": "3",
      "name": "Análisis Estructurado, Matrices y Tablas",
      "badge": "Hojas de Cálculo",
      "icon": "📊",
      "image": "assets/images/canvas/canvas_matriz.jpg",
      "imageCaption": "Reconstrucción del resultado esperado, dibujada con los datos del prompt. Tu Canvas puede variar en diseño, no en cifras ni en contenido.",
      "objective": "Evaluar propuestas de oferentes o comparar alternativas complejas con fórmulas matemáticas y exportación transparente a Google Sheets.",
      "canvasControls": [
        "🧮 Ver fórmula",
        "📊 Exportar a Google Sheets"
      ],
      "masterPrompt": "Actúa como analista de compras públicas que prepara matrices de decisión para comités evaluadores.\n\nContexto: El comité de compras debe elegir un proveedor de internet dedicado para el campus y necesita una matriz cuantitativa que cualquier integrante pueda recalcular.\n\nDatos:\n\"\"\"\nProceso: servicio de internet dedicado para el campus durante 36 meses.\nEscala de calificación: de 1 (no cumple) a 10 (cumple de forma sobresaliente).\n\nCriterio | Peso | Proveedor A (fibra) | Proveedor B (híbrido) | Proveedor C (satelital)\nC1 Ancho de banda garantizado | 25 % | 9,5 | 8 | 6\nC2 Disponibilidad SLA del 99,8 % | 20 % | 9 | 8,5 | 7\nC3 Soporte técnico 24/7 en sitio | 15 % | 8,5 | 7 | 6\nC4 Costo mensual por Mbps | 25 % | 8 | 9 | 5\nC5 Experiencia certificada | 15 % | 9 | 8 | 6\n\nFórmula: puntaje sobre 100 = suma de (calificación × peso) ÷ 10.\nEstados: Recomendado si el puntaje es 85 o más; Admisible con observaciones entre 70 y 84,9; No cumple por debajo de 70.\n\"\"\"\n\nInstrucción: En Canvas, construye la matriz ponderada con:\n1. Una tabla con los 5 criterios, su peso y la calificación de cada proveedor, tal como aparecen en los datos.\n2. Una columna de aporte por proveedor (calificación × peso ÷ 10) en cada criterio.\n3. Una fila final con el puntaje sobre 100 de cada proveedor, con un decimal, y su estado según los umbrales.\n4. Una conclusión de dos frases que nombre al proveedor recomendado y la diferencia de puntos con el segundo.\n\nRestricciones:\n- Usa solo las calificaciones y los pesos de los datos, y muestra la fórmula que aplicaste.\n- Si exportas a Hojas de cálculo, los totales deben ser fórmulas, no valores escritos a mano.\n\nFormato: Tabla con encabezados claros, estados en verde, ámbar y rojo, y cifras con coma decimal.",
      "expectedResultSummary": "Los pesos suman 100 % (25 + 20 + 15 + 25 + 15). El aporte del Proveedor A en C1 es 23,75 (9,5 × 25 ÷ 10). Puntaje final: Proveedor A = 88,0; Proveedor B = 82,0; Proveedor C = 59,5. Estados: A Recomendado (verde), B Admisible con observaciones (ámbar) y C No cumple (rojo). La conclusión recomienda al Proveedor A y menciona la diferencia de 6,0 puntos con el Proveedor B. Al exportar a Hojas de cálculo, la celda del total contiene una fórmula (por ejemplo, =SUMAPRODUCTO).",
      "variationPrompts": [
        "Añade un análisis de sensibilidad: ¿qué ocurre si el costo mensual sube al 40% de ponderación?",
        "Genera un informe ejecutivo de 1 párrafo recomendando al proveedor ganador con base en los datos de la matriz."
      ],
      "exercises": [
        {
          "id": "data-a-ventas-tienda",
          "title": "Análisis de ventas anuales con exportación a Sheets y tablero",
          "level": "Intermedio",
          "duration": "30 min",
          "canvasFeature": "Tabla en Canvas + Exportar a Hojas de cálculo + página web",
          "goal": "Transformar 12 meses de ventas en una matriz con totales, participación y crecimiento, llevarla a Google Sheets y convertirla en un tablero visual.",
          "scenario": "La tienda del centro debe decidir qué producto promocionar en 2026 y en qué meses reforzar inventario.",
          "promptParts": {
            "role": "analista comercial de pequeñas unidades productivas",
            "context": "La tienda del centro debe decidir qué producto promocionar en 2026 y en qué meses reforzar el inventario.",
            "data": "VENTAS 2025 — TIENDA AGROINDUSTRIAL DEL CENTRO (miles de COP, datos de práctica)\n\nMes,Cafe,Panela,Queso\nEne,4200,2100,1800\nFeb,3900,2000,1750\nMar,4500,2300,1900\nAbr,4800,2250,2000\nMay,5100,2400,2100\nJun,4700,2600,2050\nJul,5300,2500,2200\nAgo,5600,2700,2300\nSep,5200,2650,2250\nOct,5800,2900,2400\nNov,6200,3100,2600\nDic,7400,3600,3100",
            "instruction": "En Canvas, prepara un análisis con:",
            "task": [
              "Una tabla con el total de cada mes y su variación porcentual frente al mes anterior.",
              "Los totales anuales y la participación de cada producto.",
              "El mes pico, el mes más bajo y el producto que más creció de enero a diciembre.",
              "Tres recomendaciones de inventario y promoción basadas en esas cifras."
            ],
            "constraints": [
              "Cifras en miles de COP con separador de miles y porcentajes con un decimal.",
              "Si haces una proyección, declara el supuesto."
            ],
            "format": "Tabla principal, tabla de resumen y recomendaciones en viñetas."
          },
          "steps": [
            "Activa Canvas en la barra del chat de Gemini, pega el prompt y envíalo.",
            "Revisa el resultado con la lista «Verifica tu resultado» y marca lo que ya cumple.",
            "Usa «Compartir y exportar → Exportar a Hojas de cálculo» para abrir la tabla en Google Sheets.",
            "En Sheets, aplica formato condicional de escala de color a la columna de totales.",
            "Vuelve a Gemini y pide: «Crea un tablero web con barras apiladas por mes y tarjetas con los totales anuales».",
            "En el tablero, selecciona las tarjetas y escribe: «Agrega la participación anual de cada producto»."
          ],
          "expected": [
            "El total anual es 120.250 (café 62.700, panela 31.100, queso 26.450).",
            "El café concentra cerca de 52 % de las ventas.",
            "Diciembre aparece como pico y febrero como mes más bajo.",
            "Las recomendaciones citan cifras de la tabla.",
            "La tabla abre correctamente en Google Sheets.",
            "La página web muestra barras apiladas y tarjetas KPI."
          ],
          "stretch": "Pide: «Agrega un filtro por trimestre y una línea de tendencia sobre el total mensual».",
          "prompt": "Actúa como analista comercial de pequeñas unidades productivas.\n\nContexto: La tienda del centro debe decidir qué producto promocionar en 2026 y en qué meses reforzar el inventario.\n\nDatos:\n\"\"\"\nVENTAS 2025 — TIENDA AGROINDUSTRIAL DEL CENTRO (miles de COP, datos de práctica)\n\nMes,Cafe,Panela,Queso\nEne,4200,2100,1800\nFeb,3900,2000,1750\nMar,4500,2300,1900\nAbr,4800,2250,2000\nMay,5100,2400,2100\nJun,4700,2600,2050\nJul,5300,2500,2200\nAgo,5600,2700,2300\nSep,5200,2650,2250\nOct,5800,2900,2400\nNov,6200,3100,2600\nDic,7400,3600,3100\n\"\"\"\n\nInstrucción: En Canvas, prepara un análisis con:\n1. Una tabla con el total de cada mes y su variación porcentual frente al mes anterior.\n2. Los totales anuales y la participación de cada producto.\n3. El mes pico, el mes más bajo y el producto que más creció de enero a diciembre.\n4. Tres recomendaciones de inventario y promoción basadas en esas cifras.\n\nRestricciones:\n- Cifras en miles de COP con separador de miles y porcentajes con un decimal.\n- Si haces una proyección, declara el supuesto.\n\nFormato: Tabla principal, tabla de resumen y recomendaciones en viñetas."
        },
        {
          "id": "data-b-matriz-riesgos",
          "title": "Matriz de riesgos con mapa de calor interactivo",
          "level": "Intermedio",
          "duration": "25 min",
          "canvasFeature": "Tabla + aplicación web con mapa de calor",
          "goal": "Calcular el nivel de cada riesgo, priorizarlos y visualizarlos en un mapa de calor 5 × 5 donde se pueda consultar cada celda.",
          "scenario": "Antes del comité de seguimiento, el líder del proyecto debe mostrar qué riesgos atender primero y con qué plan.",
          "promptParts": {
            "role": "gestor de proyectos de software con experiencia en gestión de riesgos",
            "context": "El líder del proyecto presentará al comité de seguimiento qué riesgos atender primero y cómo hacerlo.",
            "data": "RIESGOS DEL PROYECTO \"APP DE CITAS DEL CENTRO\" (datos de práctica)\nEscala: probabilidad 1-5 e impacto 1-5. Nivel = probabilidad x impacto.\nCrítico >= 15 | Alto 8-14 | Medio 4-7 | Bajo <= 3\n\nid,riesgo,probabilidad,impacto\nR1,Rotación del líder técnico,2,5\nR2,Retraso en la entrega de requisitos,4,4\nR3,Falla del servidor de pruebas,3,3\nR4,Fuga de datos personales,2,5\nR5,Cambios de alcance sin control,4,3\nR6,Baja adopción por parte de usuarios,3,4\nR7,Dependencia de una API externa,3,5\nR8,Errores de estimación,5,3\nR9,Falta de pruebas automatizadas,4,5\nR10,Corte de energía en el centro,2,2",
            "instruction": "En Canvas, construye la matriz de riesgos:",
            "task": [
              "Calcula el nivel de cada riesgo y clasifícalo con los umbrales de los datos.",
              "Ordena los riesgos de mayor a menor nivel.",
              "Asigna a cada riesgo una respuesta (evitar, mitigar, transferir o aceptar) y una acción concreta."
            ],
            "constraints": [
              "Usa exactamente los umbrales entregados.",
              "Propón acciones realistas para un equipo de cinco personas."
            ],
            "format": "Tabla con las columnas id, riesgo, probabilidad, impacto, nivel, clasificación, respuesta y acción."
          },
          "steps": [
            "Activa Canvas en la barra del chat de Gemini, pega el prompt y envíalo.",
            "Revisa el resultado con la lista «Verifica tu resultado» y marca lo que ya cumple.",
            "Selecciona la columna de acciones y escribe: «Agrega un responsable y una fecha límite a cada acción».",
            "Exporta la tabla con «Exportar a Hojas de cálculo» para el seguimiento semanal.",
            "Pide en el chat: «Crea una aplicación web con un mapa de calor 5 x 5 de estos riesgos; al hacer clic en una celda, muestra sus riesgos y acciones».",
            "En la vista previa, haz clic en la celda de probabilidad 2 e impacto 5."
          ],
          "expected": [
            "Hay 4 riesgos críticos: R9 (20), R2 (16), R7 (15) y R8 (15).",
            "Hay 5 riesgos altos: R5 y R6 (12), R1 y R4 (10) y R3 (9).",
            "R10 (4) es el único riesgo medio y ninguno queda en Bajo.",
            "El mapa muestra 2 riesgos en la celda probabilidad 2 / impacto 5.",
            "Al hacer clic en una celda aparece la lista con acciones.",
            "La tabla se exportó a Hojas de cálculo."
          ],
          "stretch": "Pide: «Calcula el riesgo residual suponiendo que cada acción baja un punto la probabilidad y muestra ambos mapas lado a lado».",
          "prompt": "Actúa como gestor de proyectos de software con experiencia en gestión de riesgos.\n\nContexto: El líder del proyecto presentará al comité de seguimiento qué riesgos atender primero y cómo hacerlo.\n\nDatos:\n\"\"\"\nRIESGOS DEL PROYECTO \"APP DE CITAS DEL CENTRO\" (datos de práctica)\nEscala: probabilidad 1-5 e impacto 1-5. Nivel = probabilidad x impacto.\nCrítico >= 15 | Alto 8-14 | Medio 4-7 | Bajo <= 3\n\nid,riesgo,probabilidad,impacto\nR1,Rotación del líder técnico,2,5\nR2,Retraso en la entrega de requisitos,4,4\nR3,Falla del servidor de pruebas,3,3\nR4,Fuga de datos personales,2,5\nR5,Cambios de alcance sin control,4,3\nR6,Baja adopción por parte de usuarios,3,4\nR7,Dependencia de una API externa,3,5\nR8,Errores de estimación,5,3\nR9,Falta de pruebas automatizadas,4,5\nR10,Corte de energía en el centro,2,2\n\"\"\"\n\nInstrucción: En Canvas, construye la matriz de riesgos:\n1. Calcula el nivel de cada riesgo y clasifícalo con los umbrales de los datos.\n2. Ordena los riesgos de mayor a menor nivel.\n3. Asigna a cada riesgo una respuesta (evitar, mitigar, transferir o aceptar) y una acción concreta.\n\nRestricciones:\n- Usa exactamente los umbrales entregados.\n- Propón acciones realistas para un equipo de cinco personas.\n\nFormato: Tabla con las columnas id, riesgo, probabilidad, impacto, nivel, clasificación, respuesta y acción."
        }
      ],
      "expected": [
        {
          "criterion": "Los pesos suman 100 % (25 + 20 + 15 + 25 + 15).",
          "how": "Suma la columna Peso."
        },
        {
          "criterion": "El aporte del Proveedor A en C1 es 23,75 (9,5 × 25 ÷ 10).",
          "how": "Busca la celda en la columna de aportes de A."
        },
        {
          "criterion": "Puntaje final: Proveedor A = 88,0; Proveedor B = 82,0; Proveedor C = 59,5.",
          "how": "Recalcula al menos uno con tu calculadora."
        },
        {
          "criterion": "Estados: A Recomendado (verde), B Admisible con observaciones (ámbar) y C No cumple (rojo).",
          "how": "Compara cada puntaje con los umbrales de los datos."
        },
        {
          "criterion": "La conclusión recomienda al Proveedor A y menciona la diferencia de 6,0 puntos con el Proveedor B.",
          "how": "Lee el texto debajo de la tabla."
        },
        {
          "criterion": "Al exportar a Hojas de cálculo, la celda del total contiene una fórmula (por ejemplo, =SUMAPRODUCTO).",
          "how": "Haz clic en la celda del total en Google Sheets y mira la barra de fórmulas."
        }
      ],
      "followUps": [],
      "imageSpec": {
        "blocks": [
          {
            "type": "heading",
            "text": "Matriz ponderada · Internet dedicado para el campus"
          },
          {
            "type": "table",
            "headers": [
              "Criterio",
              "Peso",
              "Proveedor A",
              "Proveedor B",
              "Proveedor C"
            ],
            "rows": [
              [
                "C1 Ancho de banda garantizado",
                "25 %",
                "9,5",
                "8",
                "6"
              ],
              [
                "C2 Disponibilidad SLA del 99,8 %",
                "20 %",
                "9",
                "8,5",
                "7"
              ],
              [
                "C3 Soporte técnico 24/7 en sitio",
                "15 %",
                "8,5",
                "7",
                "6"
              ],
              [
                "C4 Costo mensual por Mbps",
                "25 %",
                "8",
                "9",
                "5"
              ],
              [
                "C5 Experiencia certificada",
                "15 %",
                "9",
                "8",
                "6"
              ],
              [
                "Puntaje sobre 100",
                "100 %",
                "88,0",
                "82,0",
                "59,5"
              ],
              [
                "Estado",
                "",
                "Recomendado",
                "Admisible con observaciones",
                "No cumple"
              ]
            ]
          },
          {
            "type": "paragraph",
            "text": "Fórmula: suma de (calificación × peso) ÷ 10."
          },
          {
            "type": "callout",
            "title": "Conclusión",
            "text": "Se recomienda el Proveedor A (88,0). Supera al Proveedor B por 6,0 puntos; el Proveedor C no cumple el mínimo de 70."
          }
        ],
        "file": "canvas_matriz.jpg",
        "docTitle": "Matriz_Ponderada_Conectividad_Campus.gsheet",
        "chat": "Construí la matriz en el Canvas con los aportes de cada criterio, el puntaje sobre 100 y el estado de cada proveedor. Puedes exportarla a Hojas de cálculo con las fórmulas incluidas."
      }
    },
    {
      "id": "flowchart-diagram",
      "number": "4",
      "name": "Diagramación de Procesos y Flujogramas",
      "badge": "Diagramas",
      "icon": "🔀",
      "image": "assets/images/canvas/canvas_diagrama.jpg",
      "imageCaption": "Reconstrucción del resultado esperado, dibujada con los datos del prompt. Tu Canvas puede variar en diseño, no en cifras ni en contenido.",
      "objective": "Mapear flujos de trabajo institucionales, protocolos de soporte técnico y ciclos de vida de software con sintaxis visual Mermaid.",
      "canvasControls": [
        "📝 Ver código Mermaid",
        "📥 Descargar SVG"
      ],
      "masterPrompt": "Actúa como analista de procesos de soporte de TI.\n\nContexto: La mesa de ayuda del centro necesita un diagrama que el personal nuevo entienda en un minuto y que se pueda guardar en el repositorio de documentación.\n\nDatos:\n\"\"\"\nResponsables y pasos (identificador | responsable | paso):\nP1 | Usuario solicitante | Reporta la falla (inicio o fin)\nP2 | Mesa de ayuda Nivel 1 | Registra el ticket y diagnostica\nD1 | Mesa de ayuda Nivel 1 | ¿Resuelto en 15 minutos o menos? (decisión)\nP3 | Mesa de ayuda Nivel 1 | Documenta la solución\nP4 | Mesa de ayuda Nivel 1 | Clasifica la severidad y escala\nP5 | Soporte especializado Nivel 2 | Interviene en sitio o reemplaza el hardware\nP6 | Soporte especializado Nivel 2 | Actualiza la base de conocimiento\nP7 | Usuario solicitante | Verifica la solución y responde la encuesta\nP8 | Mesa de ayuda Nivel 1 | Cierra el ticket (inicio o fin)\n\nConexiones:\nP1 → P2\nP2 → D1\nD1 → P3 (respuesta «Sí»)\nD1 → P4 (respuesta «No»)\nP4 → P5\nP5 → P6\nP3 → P7\nP6 → P7\nP7 → P8\n\"\"\"\n\nInstrucción: En Canvas, genera el diagrama en Mermaid con:\n1. Sintaxis flowchart TD.\n2. Un subgrafo por responsable, con el nombre exacto de los datos.\n3. Los 9 pasos con sus identificadores: D1 como rombo, y P1 y P8 como cápsulas de inicio y fin.\n4. Las 9 conexiones de los datos; las dos salidas de D1 rotuladas «Sí» y «No».\n\nRestricciones:\n- No agregues pasos, responsables ni conexiones que no estén en los datos.\n- El código debe dibujarse sin errores en cualquier visor de Mermaid, por ejemplo mermaid.live.\n\nFormato: Solo el bloque de código Mermaid, seguido de una línea que explique cómo leer el diagrama.",
      "expectedResultSummary": "El código se dibuja sin mensajes de error de sintaxis. Hay 3 carriles (subgrafos): Usuario solicitante, Mesa de ayuda Nivel 1 y Soporte especializado Nivel 2. Aparecen los 9 pasos (P1 a P8 y D1), cada uno en el carril de su responsable. D1 es un rombo con dos salidas: «Sí» hacia P3 y «No» hacia P4. Hay 9 flechas en total y los dos caminos llegan a P7 y luego a P8. El código empieza con «flowchart TD».",
      "variationPrompts": [
        "Agrega un carril para el Proveedor de Garantía en caso de que el equipo requiera cambio de tarjeta madre.",
        "Genera una tabla de Procedimientos Operativos Estándar (SOP) correspondiente a cada caja del diagrama."
      ],
      "exercises": [
        {
          "id": "flow-a-matricula-swimlanes",
          "title": "Flujograma de matrícula por carriles con Mermaid",
          "level": "Intermedio",
          "duration": "25 min",
          "canvasFeature": "Código Mermaid renderizado en vista previa",
          "goal": "Convertir un procedimiento escrito en un diagrama de flujo por carriles, con decisiones y tiempos, que se pueda exportar como imagen.",
          "scenario": "Bienestar y coordinación tienen versiones distintas del proceso de matrícula. Un diagrama único evita confusiones.",
          "promptParts": {
            "role": "analista de procesos que documenta con diagramas Mermaid",
            "context": "Bienestar y coordinación académica tienen versiones distintas del proceso de matrícula. Se necesita un diagrama único para proyectarlo en la reunión de alineación.",
            "data": "PROCESO DE MATRÍCULA DE APRENDICES (descripción de práctica)\n\nActores (carriles): Aspirante | Plataforma de inscripción | Bienestar | Coordinación académica\n\n1. El aspirante se registra en la plataforma de inscripción y elige el programa.\n2. La plataforma valida que el documento no tenga otra matrícula activa.\n   - Si ya tiene matrícula activa: se notifica al aspirante y el proceso termina.\n3. El aspirante presenta la prueba de conocimientos.\n4. Coordinación académica revisa el puntaje.\n   - Puntaje >= 60: pasa a entrevista.\n   - Puntaje < 60: queda en lista de espera y recibe un correo.\n5. Bienestar realiza la entrevista y verifica documentos.\n6. Si faltan documentos, el aspirante tiene 3 días hábiles para cargarlos; si no lo hace, se libera el cupo.\n7. Coordinación académica asigna ficha y horario.\n8. La plataforma envía el correo de bienvenida y el aspirante pasa a ser aprendiz.\n\nTiempos promedio: registro 1 día, prueba 2 días, revisión 1 día, entrevista 3 días, asignación 1 día.",
            "instruction": "En Canvas, crea una página web que muestre el proceso como diagrama de flujo con:",
            "task": [
              "Un carril por actor.",
              "Rombos para las decisiones y nodos de inicio y fin diferenciados.",
              "El camino feliz resaltado.",
              "Una tabla con la duración de cada etapa y el tiempo total del camino feliz."
            ],
            "constraints": [
              "Usa Mermaid cargado desde CDN.",
              "Máximo seis palabras por nodo para que se lea en el proyector."
            ],
            "format": "Un solo archivo HTML ejecutable."
          },
          "steps": [
            "Activa Canvas en la barra del chat de Gemini, pega el prompt y envíalo.",
            "Cuando termine, abre la «Vista previa» del Canvas para usar la aplicación.",
            "Si Mermaid muestra un error, escribe en el chat: «Corrige este error de Mermaid» y pega el mensaje.",
            "En el código, selecciona el nodo de la entrevista y escribe: «Agrega una nota con su duración».",
            "Pide en el chat: «Agrega un botón para descargar el diagrama en SVG» y prueba la descarga.",
            "Pide: «Genera una versión vertical para imprimir en tamaño carta»."
          ],
          "expected": [
            "Hay cuatro carriles con nombre visible.",
            "Aparecen tres decisiones: matrícula activa, puntaje ≥ 60 y documentos completos.",
            "El camino feliz está resaltado y termina en «Aprendiz matriculado».",
            "La tabla muestra un tiempo total de 8 días en el camino feliz.",
            "El botón descarga un archivo SVG válido.",
            "La versión vertical cabe en una hoja tamaño carta."
          ],
          "stretch": "Pide: «Agrega indicadores de cuello de botella: colorea en ámbar la etapa más lenta».",
          "prompt": "Actúa como analista de procesos que documenta con diagramas Mermaid.\n\nContexto: Bienestar y coordinación académica tienen versiones distintas del proceso de matrícula. Se necesita un diagrama único para proyectarlo en la reunión de alineación.\n\nDatos:\n\"\"\"\nPROCESO DE MATRÍCULA DE APRENDICES (descripción de práctica)\n\nActores (carriles): Aspirante | Plataforma de inscripción | Bienestar | Coordinación académica\n\n1. El aspirante se registra en la plataforma de inscripción y elige el programa.\n2. La plataforma valida que el documento no tenga otra matrícula activa.\n   - Si ya tiene matrícula activa: se notifica al aspirante y el proceso termina.\n3. El aspirante presenta la prueba de conocimientos.\n4. Coordinación académica revisa el puntaje.\n   - Puntaje >= 60: pasa a entrevista.\n   - Puntaje < 60: queda en lista de espera y recibe un correo.\n5. Bienestar realiza la entrevista y verifica documentos.\n6. Si faltan documentos, el aspirante tiene 3 días hábiles para cargarlos; si no lo hace, se libera el cupo.\n7. Coordinación académica asigna ficha y horario.\n8. La plataforma envía el correo de bienvenida y el aspirante pasa a ser aprendiz.\n\nTiempos promedio: registro 1 día, prueba 2 días, revisión 1 día, entrevista 3 días, asignación 1 día.\n\"\"\"\n\nInstrucción: En Canvas, crea una página web que muestre el proceso como diagrama de flujo con:\n1. Un carril por actor.\n2. Rombos para las decisiones y nodos de inicio y fin diferenciados.\n3. El camino feliz resaltado.\n4. Una tabla con la duración de cada etapa y el tiempo total del camino feliz.\n\nRestricciones:\n- Usa Mermaid cargado desde CDN.\n- Máximo seis palabras por nodo para que se lea en el proyector.\n\nFormato: Un solo archivo HTML ejecutable."
        },
        {
          "id": "flow-b-secuencia-jwt-gantt",
          "title": "Diagrama de secuencia JWT y cronograma Gantt en una sola página",
          "level": "Avanzado",
          "duration": "30 min",
          "canvasFeature": "Mermaid (sequenceDiagram + gantt) en vista previa",
          "goal": "Documentar el flujo de autenticación con tokens y el plan de implementación en dos diagramas conectados.",
          "scenario": "El equipo de desarrollo debe explicar en la revisión técnica cómo funciona el login y cuándo estará listo.",
          "promptParts": {
            "role": "arquitecto de software que documenta flujos de autenticación",
            "context": "El equipo explicará en la revisión técnica cómo funciona el inicio de sesión y cuándo estará listo.",
            "data": "INICIO DE SESIÓN CON JWT Y PLAN DEL PROYECTO (datos de práctica)\n\nParticipantes: Usuario, App web, API de autenticación, Base de datos, API de citas\n\nFlujo:\n1. Usuario escribe correo y contraseña en la App web.\n2. App web envía POST /auth/login a la API de autenticación.\n3. API consulta el usuario en la Base de datos y compara el hash de la contraseña.\n4. Si es inválido: responde 401 y la App muestra \"Credenciales incorrectas\".\n5. Si es válido: firma un access token (15 min) y un refresh token (7 días) y responde 200.\n6. App web llama GET /citas a la API de citas con el header Authorization: Bearer <token>.\n7. API de citas valida la firma y la expiración; responde 200 con las citas.\n8. Si el access token expiró: responde 401, la App llama POST /auth/refresh y repite la petición.\n\nPlan del proyecto (año 2026):\nTarea,Inicio,Duración\nDiseño del flujo,2026-11-02,5 días\nEndpoint de login,2026-11-09,4 días\nMiddleware de validación,2026-11-13,3 días\nRefresh token,2026-11-18,3 días\nPruebas de seguridad,2026-11-23,4 días\nDespliegue,2026-11-27,1 día",
            "instruction": "En Canvas, crea una página web con dos diagramas Mermaid en pestañas:",
            "task": [
              "Secuencia: el inicio de sesión con sus dos caminos de error (credenciales inválidas y token vencido) y la vigencia de cada token.",
              "Cronograma: un diagrama de Gantt con las tareas, sus fechas y el despliegue como hito."
            ],
            "constraints": [
              "Los códigos HTTP y las fechas deben coincidir con los datos.",
              "Las pestañas deben funcionar con teclado."
            ],
            "format": "Un solo archivo HTML con Mermaid cargado desde CDN."
          },
          "steps": [
            "Activa Canvas en la barra del chat de Gemini, pega el prompt y envíalo.",
            "Cuando termine, abre la «Vista previa» del Canvas para usar la aplicación.",
            "Revisa la pestaña de secuencia y ubica los dos caminos de error.",
            "En el código, selecciona el bloque gantt y escribe: «Marca como crítica la tarea de pruebas de seguridad».",
            "Pide en el chat: «Agrega una pestaña de glosario con JWT, access token, refresh token, firma y código 401, cada uno en una frase».",
            "Usa «Compartir» para enviar el enlace al equipo."
          ],
          "expected": [
            "El diagrama de secuencia muestra los cinco participantes en orden.",
            "Existen las ramas 401 por credenciales y 401 por token expirado con refresh.",
            "Las notas indican 15 minutos y 7 días.",
            "El Gantt muestra seis tareas sin solapamiento y termina el 27/11/2026.",
            "Las pestañas funcionan con teclado.",
            "El glosario explica los cinco términos en una frase cada uno."
          ],
          "stretch": "Pide: «Agrega un diagrama de estados del token: emitido, vigente, expirado, renovado y revocado».",
          "prompt": "Actúa como arquitecto de software que documenta flujos de autenticación.\n\nContexto: El equipo explicará en la revisión técnica cómo funciona el inicio de sesión y cuándo estará listo.\n\nDatos:\n\"\"\"\nINICIO DE SESIÓN CON JWT Y PLAN DEL PROYECTO (datos de práctica)\n\nParticipantes: Usuario, App web, API de autenticación, Base de datos, API de citas\n\nFlujo:\n1. Usuario escribe correo y contraseña en la App web.\n2. App web envía POST /auth/login a la API de autenticación.\n3. API consulta el usuario en la Base de datos y compara el hash de la contraseña.\n4. Si es inválido: responde 401 y la App muestra \"Credenciales incorrectas\".\n5. Si es válido: firma un access token (15 min) y un refresh token (7 días) y responde 200.\n6. App web llama GET /citas a la API de citas con el header Authorization: Bearer <token>.\n7. API de citas valida la firma y la expiración; responde 200 con las citas.\n8. Si el access token expiró: responde 401, la App llama POST /auth/refresh y repite la petición.\n\nPlan del proyecto (año 2026):\nTarea,Inicio,Duración\nDiseño del flujo,2026-11-02,5 días\nEndpoint de login,2026-11-09,4 días\nMiddleware de validación,2026-11-13,3 días\nRefresh token,2026-11-18,3 días\nPruebas de seguridad,2026-11-23,4 días\nDespliegue,2026-11-27,1 día\n\"\"\"\n\nInstrucción: En Canvas, crea una página web con dos diagramas Mermaid en pestañas:\n1. Secuencia: el inicio de sesión con sus dos caminos de error (credenciales inválidas y token vencido) y la vigencia de cada token.\n2. Cronograma: un diagrama de Gantt con las tareas, sus fechas y el despliegue como hito.\n\nRestricciones:\n- Los códigos HTTP y las fechas deben coincidir con los datos.\n- Las pestañas deben funcionar con teclado.\n\nFormato: Un solo archivo HTML con Mermaid cargado desde CDN."
        }
      ],
      "expected": [
        {
          "criterion": "El código se dibuja sin mensajes de error de sintaxis.",
          "how": "Copia el código del Canvas y pégalo en mermaid.live; Canvas puede mostrarlo como texto sin dibujarlo."
        },
        {
          "criterion": "Hay 3 carriles (subgrafos): Usuario solicitante, Mesa de ayuda Nivel 1 y Soporte especializado Nivel 2.",
          "how": "Cuenta los recuadros con título en el diagrama."
        },
        {
          "criterion": "Aparecen los 9 pasos (P1 a P8 y D1), cada uno en el carril de su responsable.",
          "how": "Compara cada nodo con la tabla de datos."
        },
        {
          "criterion": "D1 es un rombo con dos salidas: «Sí» hacia P3 y «No» hacia P4.",
          "how": "Sigue las flechas que salen del rombo."
        },
        {
          "criterion": "Hay 9 flechas en total y los dos caminos llegan a P7 y luego a P8.",
          "how": "Cuenta las líneas «-->» en el código."
        },
        {
          "criterion": "El código empieza con «flowchart TD».",
          "how": "Pasa a la vista de código."
        }
      ],
      "followUps": [],
      "imageSpec": {
        "blocks": [
          {
            "type": "heading",
            "text": "Flujo de la mesa de ayuda (Mermaid · flowchart TD)"
          },
          {
            "type": "flow",
            "lanes": [
              {
                "id": "U",
                "name": "Usuario solicitante",
                "nodes": [
                  {
                    "id": "P1",
                    "label": "Reporta la falla",
                    "shape": "terminal"
                  },
                  {
                    "id": "P7",
                    "label": "Verifica la solución y responde la encuesta"
                  }
                ]
              },
              {
                "id": "N1",
                "name": "Mesa de ayuda Nivel 1",
                "nodes": [
                  {
                    "id": "P2",
                    "label": "Registra el ticket y diagnostica"
                  },
                  {
                    "id": "D1",
                    "label": "¿Resuelto en 15 minutos o menos?",
                    "shape": "decision"
                  },
                  {
                    "id": "P3",
                    "label": "Documenta la solución"
                  },
                  {
                    "id": "P4",
                    "label": "Clasifica la severidad y escala"
                  },
                  {
                    "id": "P8",
                    "label": "Cierra el ticket",
                    "shape": "terminal"
                  }
                ]
              },
              {
                "id": "N2",
                "name": "Soporte especializado Nivel 2",
                "nodes": [
                  {
                    "id": "P5",
                    "label": "Interviene en sitio o reemplaza el hardware"
                  },
                  {
                    "id": "P6",
                    "label": "Actualiza la base de conocimiento"
                  }
                ]
              }
            ],
            "edges": [
              [
                "P1",
                "P2"
              ],
              [
                "P2",
                "D1"
              ],
              [
                "D1",
                "P3",
                "Sí"
              ],
              [
                "D1",
                "P4",
                "No"
              ],
              [
                "P4",
                "P5"
              ],
              [
                "P5",
                "P6"
              ],
              [
                "P3",
                "P7"
              ],
              [
                "P6",
                "P7"
              ],
              [
                "P7",
                "P8"
              ]
            ]
          },
          {
            "type": "paragraph",
            "text": "Lectura: el usuario reporta (P1); Nivel 1 diagnostica y decide en D1. Con «Sí» documenta la solución; con «No» escala a Nivel 2. Ambos caminos terminan en la verificación del usuario (P7) y el cierre (P8)."
          }
        ],
        "file": "canvas_diagrama.jpg",
        "docTitle": "Flujo_Mesa_de_Ayuda.mmd",
        "chat": "Generé el diagrama en Mermaid dentro del Canvas: tres carriles por responsable, nueve pasos y la decisión D1 con sus salidas «Sí» y «No». Pega el código en mermaid.live para comprobar que se dibuja sin errores."
      }
    },
    {
      "id": "visual-mockup",
      "number": "5",
      "name": "Diseño Visual y Mockups de Interfaz (UI)",
      "badge": "Diseño & UI",
      "icon": "🎨",
      "image": "assets/images/canvas/canvas_ui_ux.jpg",
      "imageCaption": "Reconstrucción del resultado esperado, dibujada con los datos del prompt. Tu Canvas puede variar en diseño, no en cifras ni en contenido.",
      "objective": "Definir sistemas de diseño accesibles y generar maquetas conceptuales de interfaces antes de la fase de codificación.",
      "canvasControls": [
        "👁️ Validar contraste",
        "🖼️ Prompt de imagen",
        "🎨 Copiar tokens CSS"
      ],
      "masterPrompt": "Actúa como diseñador de interfaces con experiencia en accesibilidad WCAG 2.2.\n\nContexto: El equipo quiere validar la paleta y la pantalla de inicio antes de diseñar el resto de la aplicación móvil.\n\nDatos:\n\"\"\"\nProducto: aplicación móvil «Mi Portafolio SENA» para aprendices.\nPantalla: inicio del aprendiz, 360 px de ancho.\nContenido de la pantalla:\n- Encabezado: «Mi Portafolio SENA» y «Tecnología en ADSO · Ficha 2871».\n- Tarjeta 1: Evidencias entregadas, 18 de 20.\n- Tarjeta 2: Asistencia, 96 %.\n- Tarjeta 3: Próxima entrega, «Diagrama de procesos», vence el 14 de noviembre.\n- Botón principal: «Subir evidencia».\n\nPaleta (token | HEX | uso):\n--color-primario | #2D5A4C | encabezado y botón\n--color-texto | #1E293B | texto\n--color-fondo | #F8FAF9 | fondo\n--color-alerta | #D97706 | íconos y avisos grandes\n--color-alerta-texto | #B45309 | texto de avisos\n\nPares de contraste que se deben comprobar (texto sobre fondo):\n1. #2D5A4C sobre #F8FAF9\n2. #1E293B sobre #F8FAF9\n3. #FFFFFF sobre #2D5A4C\n4. #D97706 sobre #FFFFFF\n5. #B45309 sobre #FFFFFF\n\"\"\"\n\nInstrucción: En Canvas, crea una página HTML con:\n1. Una sección de tokens con una muestra de color por cada fila de la paleta.\n2. Una tabla que calcule con JavaScript la razón de contraste de los 5 pares con la fórmula de WCAG 2.2, con dos decimales, y el nivel que cumple: AAA, AA, AA texto grande o No cumple.\n3. Una maqueta de la pantalla de inicio de 360 px de ancho con el contenido de los datos; el botón debe mostrar un contorno real con :focus-visible al llegar con la tecla Tab, no un contorno fijo.\n4. Al final de la página, un bloque de texto en inglés que describa la maqueta con los HEX de la paleta; lo copiaré después como descripción de diseño.\n\nRestricciones:\n- Calcula los contrastes en el código; no los escribas a mano.\n- Usa solo los colores de la paleta y no uses librerías externas.\n\nFormato: Un único archivo HTML; la tabla de contraste con las columnas Par, Razón y Nivel.",
      "expectedResultSummary": "La tabla de contraste muestra #2D5A4C sobre #F8FAF9 = 7,48:1 (AAA); #1E293B sobre #F8FAF9 = 13,96:1 (AAA); #FFFFFF sobre #2D5A4C = 7,84:1 (AAA). #D97706 sobre #FFFFFF = 3,19:1 (AA texto grande): solo sirve para íconos o texto grande. #B45309 sobre #FFFFFF = 5,02:1 (AA): sirve para el texto de los avisos. La maqueta muestra Evidencias 18 de 20, Asistencia 96 % y la próxima entrega «Diagrama de procesos». El botón «Subir evidencia» muestra un contorno solo al llegar a él con la tecla Tab, y el CSS usa :focus-visible. El bloque en inglés del final describe la maqueta e incluye #2D5A4C, #1E293B y #F8FAF9.",
      "variationPrompts": [
        "Adapta la paleta de colores al modo oscuro suave (#121816 de fondo y verde salvia luminoso).",
        "Diseña el flujo de estados vacíos (empty state) cuando el aprendiz no tiene evidencias registradas."
      ],
      "exercises": [
        {
          "id": "ui-a-landing-biblioturnos",
          "title": "Landing de reservas con modo oscuro y ocupación en vivo",
          "level": "Intermedio",
          "duration": "30 min",
          "canvasFeature": "Canvas de código + Vista previa responsiva",
          "goal": "Diseñar y construir una página de aterrizaje completa, accesible y con modo oscuro, que muestre la ocupación por franja con un gráfico.",
          "scenario": "La biblioteca quiere lanzar las reservas en línea la próxima semana y necesita una página atractiva y clara para los aprendices.",
          "promptParts": {
            "role": "diseñador UI/UX que también desarrolla frontend",
            "context": "La biblioteca lanzará la próxima semana las reservas en línea de cubículos. La página debe invitar a reservar y ser fácil de usar desde el celular.",
            "data": "LANDING \"BIBLIOTURNOS\" — RESERVA DE CUBÍCULOS DE ESTUDIO (contenido de práctica)\n\nMarca: BiblioTurnos · Biblioteca del Centro de Formación\nLema: \"Tu espacio de estudio, reservado en un minuto\"\nColores: verde salvia #2d5a4c, arena #f5efe6, azul acento #2563eb\nTipografía: sistema (sans-serif)\n\nSecciones:\n1. Encabezado con logo textual y enlaces: Cómo funciona, Horarios, Preguntas.\n2. Héroe con lema, botón \"Reservar ahora\" y contador de cubículos libres hoy.\n3. Tres pasos: Elige franja, Confirma con tu documento, Llega 10 minutos antes.\n4. Ocupación de hoy por franja (cupos de 20 por franja):\n   08:00-10:00: 12 ocupados\n   10:00-12:00: 17 ocupados\n   14:00-16:00: 8 ocupados\n   16:00-18:00: 19 ocupados\n5. Formulario: documento (solo números, 6 a 10 dígitos), franja (lista), aceptar normas.\n6. Preguntas frecuentes (3) en acordeón:\n   - ¿Cuánto dura la reserva? 2 horas.\n   - ¿Puedo cancelar? Sí, hasta 30 minutos antes.\n   - ¿Qué pasa si no llego? La reserva se libera a los 15 minutos.\n7. Pie con horario: lunes a viernes 8:00-18:00.",
            "instruction": "En Canvas, construye la landing con el contenido de los datos, incluyendo:",
            "task": [
              "Un gráfico de ocupación por franja: verde por debajo de 75 %, ámbar entre 75 % y 89 % y rojo desde 90 %.",
              "Un contador de cubículos libres calculado con esos datos.",
              "Un formulario con validación en vivo y mensajes que expliquen cómo corregir.",
              "Un interruptor de modo oscuro."
            ],
            "constraints": [
              "Contraste WCAG AA, foco visible y uso completo con teclado.",
              "Responsiva desde 360 px y sin librerías externas."
            ],
            "format": "Un solo archivo HTML con colores definidos en variables CSS."
          },
          "steps": [
            "Activa Canvas en la barra del chat de Gemini, pega el prompt y envíalo.",
            "Cuando termine, abre la «Vista previa» del Canvas para usar la aplicación.",
            "Escribe un documento con letras en el formulario y lee el mensaje de error.",
            "Activa el modo oscuro y revisa que el gráfico siga legible.",
            "En el código, selecciona la sección principal (héroe) y escribe: «Agrega una ilustración SVG sencilla de un escritorio con libros».",
            "Pide en el chat: «Muestra cómo se ve a 390 px y corrige lo que no quepa»."
          ],
          "expected": [
            "La franja 16:00-18:00 aparece en rojo (95 %) y 10:00-12:00 en ámbar (85 %).",
            "El contador del héroe muestra 24 cubículos libres.",
            "El formulario rechaza documentos fuera de 6 a 10 dígitos con mensaje claro.",
            "El formulario válido muestra una confirmación con la franja elegida.",
            "El modo oscuro cambia todos los colores sin perder contraste.",
            "El acordeón abre y cierra con teclado."
          ],
          "stretch": "Pide: «Agrega una vista de calendario semanal con la ocupación de cada día como mapa de calor».",
          "prompt": "Actúa como diseñador UI/UX que también desarrolla frontend.\n\nContexto: La biblioteca lanzará la próxima semana las reservas en línea de cubículos. La página debe invitar a reservar y ser fácil de usar desde el celular.\n\nDatos:\n\"\"\"\nLANDING \"BIBLIOTURNOS\" — RESERVA DE CUBÍCULOS DE ESTUDIO (contenido de práctica)\n\nMarca: BiblioTurnos · Biblioteca del Centro de Formación\nLema: \"Tu espacio de estudio, reservado en un minuto\"\nColores: verde salvia #2d5a4c, arena #f5efe6, azul acento #2563eb\nTipografía: sistema (sans-serif)\n\nSecciones:\n1. Encabezado con logo textual y enlaces: Cómo funciona, Horarios, Preguntas.\n2. Héroe con lema, botón \"Reservar ahora\" y contador de cubículos libres hoy.\n3. Tres pasos: Elige franja, Confirma con tu documento, Llega 10 minutos antes.\n4. Ocupación de hoy por franja (cupos de 20 por franja):\n   08:00-10:00: 12 ocupados\n   10:00-12:00: 17 ocupados\n   14:00-16:00: 8 ocupados\n   16:00-18:00: 19 ocupados\n5. Formulario: documento (solo números, 6 a 10 dígitos), franja (lista), aceptar normas.\n6. Preguntas frecuentes (3) en acordeón:\n   - ¿Cuánto dura la reserva? 2 horas.\n   - ¿Puedo cancelar? Sí, hasta 30 minutos antes.\n   - ¿Qué pasa si no llego? La reserva se libera a los 15 minutos.\n7. Pie con horario: lunes a viernes 8:00-18:00.\n\"\"\"\n\nInstrucción: En Canvas, construye la landing con el contenido de los datos, incluyendo:\n1. Un gráfico de ocupación por franja: verde por debajo de 75 %, ámbar entre 75 % y 89 % y rojo desde 90 %.\n2. Un contador de cubículos libres calculado con esos datos.\n3. Un formulario con validación en vivo y mensajes que expliquen cómo corregir.\n4. Un interruptor de modo oscuro.\n\nRestricciones:\n- Contraste WCAG AA, foco visible y uso completo con teclado.\n- Responsiva desde 360 px y sin librerías externas.\n\nFormato: Un solo archivo HTML con colores definidos en variables CSS."
        },
        {
          "id": "ui-b-app-reporta-ya",
          "title": "Prototipo móvil navegable de reporte de incidencias",
          "level": "Avanzado",
          "duration": "35 min",
          "canvasFeature": "Canvas de código + prototipo multipantalla",
          "goal": "Construir un prototipo móvil con cuatro pantallas navegables, estados visuales y un panel de estadísticas calculado con los datos de ejemplo.",
          "scenario": "Mantenimiento quiere validar con aprendices una app para reportar daños antes de contratar el desarrollo.",
          "promptParts": {
            "role": "diseñador de producto móvil",
            "context": "Mantenimiento quiere validar la idea con aprendices antes de contratar el desarrollo, así que necesita un prototipo que se sienta real.",
            "data": "APP MÓVIL \"REPORTA YA\" — INCIDENCIAS DE INFRAESTRUCTURA (contenido de práctica)\n\nPantallas:\n1. Inicio: saludo, botón grande \"Reportar incidencia\" y lista de mis reportes.\n2. Nuevo reporte: tipo (Eléctrico, Agua, Mobiliario, Red), ubicación (bloque y salón), descripción, foto (simulada), prioridad (Baja, Media, Alta).\n3. Detalle: estado con línea de tiempo (Recibido, Asignado, En reparación, Resuelto).\n4. Estadísticas: gráfico de dona por tipo y tiempo promedio de solución.\n\nReportes de ejemplo:\nid,tipo,ubicación,prioridad,estado,horas_solución\nINC-01,Eléctrico,Bloque A-201,Alta,Resuelto,6\nINC-02,Red,Bloque C-105,Media,En reparación,\nINC-03,Agua,Baños bloque B,Alta,Resuelto,4\nINC-04,Mobiliario,Biblioteca,Baja,Asignado,\nINC-05,Red,Sala de sistemas 3,Alta,Resuelto,9\nINC-06,Eléctrico,Auditorio,Media,Recibido,",
            "instruction": "En Canvas, crea un prototipo web con apariencia de teléfono que:",
            "task": [
              "Implemente las cuatro pantallas con una barra de navegación inferior.",
              "Cargue los reportes de ejemplo con un indicador de estado legible.",
              "Permita crear un reporte nuevo que aparezca de inmediato en Inicio.",
              "Calcule en Estadísticas la distribución por tipo y el tiempo promedio de solución."
            ],
            "constraints": [
              "Botones de al menos 44 px de alto y texto de al menos 16 px.",
              "El estado se comunica con texto, no solo con color."
            ],
            "format": "Un solo archivo HTML ejecutable, sin frameworks."
          },
          "steps": [
            "Activa Canvas en la barra del chat de Gemini, pega el prompt y envíalo.",
            "Cuando termine, abre la «Vista previa» del Canvas para usar la aplicación.",
            "Navega por las cuatro pantallas con la barra inferior.",
            "Crea un reporte de tipo Agua en «Bloque D-110» con prioridad Alta y búscalo en Inicio.",
            "En el código, selecciona la línea de tiempo del detalle y escribe: «Agrega la fecha y hora de cada cambio de estado».",
            "Pide en el chat: «Agrega un filtro por prioridad en Inicio»."
          ],
          "expected": [
            "Las cuatro pantallas navegan sin recargar.",
            "Inicio muestra los seis reportes con chip de estado legible.",
            "El nuevo reporte aparece con estado Recibido.",
            "La dona muestra 2 Eléctrico, 2 Red, 1 Agua y 1 Mobiliario (antes de crear reportes nuevos).",
            "El promedio de solución es (6 + 4 + 9) / 3 = 6,3 horas.",
            "Los botones cumplen el tamaño mínimo táctil."
          ],
          "stretch": "Pide: «Agrega un filtro por prioridad y una insignia con el número de reportes de prioridad Alta abiertos».",
          "prompt": "Actúa como diseñador de producto móvil.\n\nContexto: Mantenimiento quiere validar la idea con aprendices antes de contratar el desarrollo, así que necesita un prototipo que se sienta real.\n\nDatos:\n\"\"\"\nAPP MÓVIL \"REPORTA YA\" — INCIDENCIAS DE INFRAESTRUCTURA (contenido de práctica)\n\nPantallas:\n1. Inicio: saludo, botón grande \"Reportar incidencia\" y lista de mis reportes.\n2. Nuevo reporte: tipo (Eléctrico, Agua, Mobiliario, Red), ubicación (bloque y salón), descripción, foto (simulada), prioridad (Baja, Media, Alta).\n3. Detalle: estado con línea de tiempo (Recibido, Asignado, En reparación, Resuelto).\n4. Estadísticas: gráfico de dona por tipo y tiempo promedio de solución.\n\nReportes de ejemplo:\nid,tipo,ubicación,prioridad,estado,horas_solución\nINC-01,Eléctrico,Bloque A-201,Alta,Resuelto,6\nINC-02,Red,Bloque C-105,Media,En reparación,\nINC-03,Agua,Baños bloque B,Alta,Resuelto,4\nINC-04,Mobiliario,Biblioteca,Baja,Asignado,\nINC-05,Red,Sala de sistemas 3,Alta,Resuelto,9\nINC-06,Eléctrico,Auditorio,Media,Recibido,\n\"\"\"\n\nInstrucción: En Canvas, crea un prototipo web con apariencia de teléfono que:\n1. Implemente las cuatro pantallas con una barra de navegación inferior.\n2. Cargue los reportes de ejemplo con un indicador de estado legible.\n3. Permita crear un reporte nuevo que aparezca de inmediato en Inicio.\n4. Calcule en Estadísticas la distribución por tipo y el tiempo promedio de solución.\n\nRestricciones:\n- Botones de al menos 44 px de alto y texto de al menos 16 px.\n- El estado se comunica con texto, no solo con color.\n\nFormato: Un solo archivo HTML ejecutable, sin frameworks."
        }
      ],
      "expected": [
        {
          "criterion": "La tabla de contraste muestra #2D5A4C sobre #F8FAF9 = 7,48:1 (AAA); #1E293B sobre #F8FAF9 = 13,96:1 (AAA); #FFFFFF sobre #2D5A4C = 7,84:1 (AAA).",
          "how": "Compáralo con «Validar contraste» de este simulador (también vale con punto decimal)."
        },
        {
          "criterion": "#D97706 sobre #FFFFFF = 3,19:1 (AA texto grande): solo sirve para íconos o texto grande.",
          "how": "Revisa la fila 4 de la tabla."
        },
        {
          "criterion": "#B45309 sobre #FFFFFF = 5,02:1 (AA): sirve para el texto de los avisos.",
          "how": "Revisa la fila 5 de la tabla."
        },
        {
          "criterion": "La maqueta muestra Evidencias 18 de 20, Asistencia 96 % y la próxima entrega «Diagrama de procesos».",
          "how": "Abre la vista previa del Canvas."
        },
        {
          "criterion": "El botón «Subir evidencia» muestra un contorno solo al llegar a él con la tecla Tab, y el CSS usa :focus-visible.",
          "how": "Haz clic en la vista previa, presiona Tab y busca «focus-visible» en el código."
        },
        {
          "criterion": "El bloque en inglés del final describe la maqueta e incluye #2D5A4C, #1E293B y #F8FAF9.",
          "how": "Busca los tres HEX en ese bloque; luego puedes pegarlo en un chat nuevo de Gemini para generar la imagen."
        }
      ],
      "followUps": [],
      "imageSpec": {
        "blocks": [
          {
            "type": "heading",
            "text": "Sistema de diseño · Mi Portafolio SENA"
          },
          {
            "type": "swatches",
            "items": [
              {
                "name": "--color-primario",
                "hex": "#2D5A4C"
              },
              {
                "name": "--color-texto",
                "hex": "#1E293B"
              },
              {
                "name": "--color-fondo",
                "hex": "#F8FAF9"
              },
              {
                "name": "--color-alerta",
                "hex": "#D97706"
              },
              {
                "name": "--color-alerta-texto",
                "hex": "#B45309"
              }
            ]
          },
          {
            "type": "table",
            "headers": [
              "Par (texto sobre fondo)",
              "Razón",
              "Nivel"
            ],
            "rows": [
              [
                "#2D5A4C sobre #F8FAF9",
                "7,48:1",
                "AAA"
              ],
              [
                "#1E293B sobre #F8FAF9",
                "13,96:1",
                "AAA"
              ],
              [
                "#FFFFFF sobre #2D5A4C",
                "7,84:1",
                "AAA"
              ],
              [
                "#D97706 sobre #FFFFFF",
                "3,19:1",
                "AA texto grande"
              ],
              [
                "#B45309 sobre #FFFFFF",
                "5,02:1",
                "AA"
              ]
            ]
          },
          {
            "type": "phone",
            "title": "Mi Portafolio SENA",
            "subtitle": "Tecnología en ADSO · Ficha 2871",
            "cards": [
              "Evidencias entregadas: 18 de 20",
              "Asistencia: 96 %",
              "Próxima entrega: Diagrama de procesos"
            ],
            "button": "Subir evidencia"
          }
        ],
        "file": "canvas_ui_ux.jpg",
        "docTitle": "Sistema_Diseno_Mi_Portafolio.html",
        "chat": "Preparé en el Canvas los tokens de color, la tabla de contraste calculada con la fórmula de WCAG 2.2, la maqueta de la pantalla de inicio y, al final, la descripción en inglés de la maqueta."
      }
    },
    {
      "id": "inline-prompting",
      "number": "6",
      "name": "Edición Quirúrgica por Selección (Inline)",
      "badge": "Edición Inline",
      "icon": "✂️",
      "image": "assets/images/canvas/canvas_inline.jpg",
      "imageCaption": "Reconstrucción del resultado esperado, dibujada con los datos del prompt. Tu Canvas puede variar en diseño, no en cifras ni en contenido.",
      "objective": "Aislar párrafos, cláusulas o funciones de código para pedir mejoras puntuales manteniendo intacto el resto del documento.",
      "canvasControls": [
        "🔄 Comparar antes y después",
        "🕒 Historial de versiones"
      ],
      "masterPrompt": "Actúa como secretario técnico que transcribe las actas del comité académico.\n\nContexto: Necesitas el acta en un Canvas para corregir después solo el punto 3, que el comité consideró confuso y punitivo.\n\nDatos:\n\"\"\"\nACTA DE CONCERTACIÓN · Ficha 2879123 (Técnico en Programación de Software)\n1. Objeto: acordar las condiciones de la evaluación final del módulo de bases de datos.\n2. Fecha de la prueba final: 14 de noviembre de 2026, 8:00 a. m., ambiente 204.\n3. Inasistencia: Los aprendices que no asistan perderán la oportunidad de presentar la prueba final a menos que tengan una excusa válida de un médico u otra autoridad y deben presentarla antes de dos días hábiles.\n4. Compromisos: el instructor publica la rúbrica el 7 de noviembre; los aprendices confirman su asistencia en la plataforma del curso.\n\"\"\"\n\nInstrucción: En Canvas, haz lo siguiente:\n1. Transcribe el acta exactamente como aparece en los datos, con su título y los 4 puntos numerados.\n2. No corrijas ni resumas nada todavía; la edición del punto 3 se hará en el paso 2.\n3. Al final, confirma en el chat cuántos puntos tiene el acta.\n\nRestricciones:\n- Conserva sin cambios las fechas, la hora y los números de ficha y de ambiente.\n\nFormato: Documento con el título en negrita y los puntos numerados del 1 al 4.",
      "expectedResultSummary": "Después del paso 1, el Canvas tiene el acta completa con 4 puntos idénticos a los datos. Después del paso 2, solo cambió el punto 3: ahora tiene exactamente 2 viñetas. Los puntos 1, 2 y 4 siguen iguales: 14 de noviembre de 2026, 8:00 a. m., ambiente 204 y rúbrica el 7 de noviembre. El punto 3 conserva «dos días hábiles» y el soporte de un médico u otra autoridad. La segunda viñeta dice que, con un soporte válido, el aprendiz conserva la oportunidad de presentar la prueba final. No aparecen requisitos nuevos (correos, formatos, fechas o sanciones) y ya no dice «perderán».",
      "variationPrompts": [
        "Selecciona una función JavaScript en Canvas y pide: 'Agrega un bloque try-catch y registra el error en consola con contexto'.",
        "Selecciona un párrafo formal y pide: 'Resume en una sola frase de impacto para la diapositiva de conclusiones'."
      ],
      "exercises": [
        {
          "id": "inline-a-correo-acudientes",
          "title": "Correo a acudientes: corregir tono párrafo por párrafo",
          "level": "Básico",
          "duration": "20 min",
          "canvasFeature": "Selección de texto + ventanita de edición + Cambiar tono",
          "goal": "Practicar la edición quirúrgica: corregir solo los fragmentos problemáticos de un correo sin que Canvas reescriba el resto.",
          "scenario": "La coordinación redactó un correo con tono amenazante. Debe salir hoy, pero los datos de fecha, hora y lugar no pueden cambiar.",
          "promptParts": {
            "role": "editor de comunicaciones institucionales",
            "context": "Voy a practicar la edición por selección en Canvas con un correo que tiene problemas de tono. Necesito el texto original intacto para corregirlo fragmento por fragmento.",
            "data": "ASUNTO: Reunión obligatoria de acudientes\n\nSeñores acudientes:\n\nLes informamos que el rendimiento del grupo es muy malo y que varios aprendices no están cumpliendo con nada. Si no asisten a la reunión, no nos hacemos responsables de lo que pase con la matrícula.\n\nLa reunión será el jueves 6 de noviembre de 2026 a las 6:00 p. m. en el auditorio principal. También se puede asistir por videollamada con el enlace que se enviará el miércoles.\n\nLos temas son: resultados del trimestre, inasistencias, plan de mejoramiento, uso del celular en clase, salida pedagógica de diciembre.\n\nEs obligatorio confirmar. El que no confirme se entiende que no le interesa.\n\nAtentamente,\nCoordinación académica",
            "instruction": "Copia el correo en Canvas sin cambiar ninguna palabra. Luego, en el chat:",
            "task": [
              "Señala los tres fragmentos con más problemas de tono y explica cada uno en una línea, sin reescribirlos."
            ],
            "constraints": [
              "No cambies ninguna palabra del documento en este paso."
            ],
            "format": "Documento en Canvas y una lista breve en el chat."
          },
          "steps": [
            "Activa Canvas en la barra del chat de Gemini, pega el prompt y envíalo.",
            "Selecciona solo el párrafo que empieza «Les informamos…» y escribe: «Reescríbelo con tono empático y propositivo, sin culpar a nadie».",
            "Selecciona la lista de temas y escribe: «Conviértela en viñetas ordenadas por prioridad».",
            "Selecciona la línea «Es obligatorio confirmar…» y escribe: «Pide la confirmación con amabilidad y fija como plazo el miércoles 5 de noviembre».",
            "Usa «Ajustar longitud → Más corto» y observa qué cambia.",
            "Compara con el original: la fecha, la hora y el lugar deben seguir iguales."
          ],
          "expected": [
            "Solo cambiaron los fragmentos seleccionados.",
            "El párrafo inicial ya no contiene juicios como «muy malo» ni amenazas.",
            "Los temas aparecen como lista con viñetas.",
            "La solicitud de confirmación es amable e incluye el miércoles 5 de noviembre.",
            "Fecha (jueves 6 de noviembre de 2026), hora (6:00 p. m.) y lugar quedaron intactos.",
            "El correo final se lee en menos de un minuto."
          ],
          "stretch": "Selecciona el correo completo y pide: «Crea una versión corta para WhatsApp de máximo 400 caracteres».",
          "prompt": "Actúa como editor de comunicaciones institucionales.\n\nContexto: Voy a practicar la edición por selección en Canvas con un correo que tiene problemas de tono. Necesito el texto original intacto para corregirlo fragmento por fragmento.\n\nDatos:\n\"\"\"\nASUNTO: Reunión obligatoria de acudientes\n\nSeñores acudientes:\n\nLes informamos que el rendimiento del grupo es muy malo y que varios aprendices no están cumpliendo con nada. Si no asisten a la reunión, no nos hacemos responsables de lo que pase con la matrícula.\n\nLa reunión será el jueves 6 de noviembre de 2026 a las 6:00 p. m. en el auditorio principal. También se puede asistir por videollamada con el enlace que se enviará el miércoles.\n\nLos temas son: resultados del trimestre, inasistencias, plan de mejoramiento, uso del celular en clase, salida pedagógica de diciembre.\n\nEs obligatorio confirmar. El que no confirme se entiende que no le interesa.\n\nAtentamente,\nCoordinación académica\n\"\"\"\n\nInstrucción: Copia el correo en Canvas sin cambiar ninguna palabra. Luego, en el chat:\n1. Señala los tres fragmentos con más problemas de tono y explica cada uno en una línea, sin reescribirlos.\n\nRestricciones:\n- No cambies ninguna palabra del documento en este paso.\n\nFormato: Documento en Canvas y una lista breve en el chat."
        },
        {
          "id": "inline-b-depurar-funcion",
          "title": "Depurar una app seleccionando solo la función con error",
          "level": "Intermedio",
          "duration": "25 min",
          "canvasFeature": "Canvas de código + selección de función + Vista previa",
          "goal": "Corregir tres errores de una calculadora de promedios seleccionando cada función por separado y comprobando el resultado en la vista previa.",
          "scenario": "Un aprendiz entregó una calculadora que muestra «NaN». Debes corregirla sin reescribir toda la aplicación.",
          "promptParts": {
            "role": "instructor de programación que enseña a depurar",
            "context": "Un aprendiz entregó una calculadora de promedios que muestra «NaN». Quiero practicar la corrección función por función usando la selección de Canvas.",
            "data": "<!DOCTYPE html>\n<html lang=\"es\">\n<head><meta charset=\"UTF-8\"><title>Calculadora de promedio</title></head>\n<body>\n  <h1>Calculadora de promedio</h1>\n  <label for=\"notas\">Notas separadas por coma</label>\n  <input id=\"notas\" value=\"3.8,4.5,4.0\">\n  <button id=\"calcular\">Calcular</button>\n  <p id=\"resultado\"></p>\n  <script>\n    function leerNotas(texto) {\n      return texto.split(\",\");\n    }\n\n    function calcularPromedio(notas) {\n      let suma = 0;\n      for (let i = 0; i <= notas.length; i++) {\n        suma += notas[i];\n      }\n      return suma / notas.length;\n    }\n\n    function estado(promedio) {\n      if (promedio > 3.5) return \"Aprobado\";\n      return \"Plan de mejoramiento\";\n    }\n\n    document.getElementById(\"calcular\").addEventListener(\"click\", function () {\n      const notas = leerNotas(document.getElementById(\"notas\").value);\n      const promedio = calcularPromedio(notas);\n      document.getElementById(\"resultado\").textContent =\n        \"Promedio: \" + promedio + \" - \" + estado(promedio);\n    });\n  </script>\n</body>\n</html>\n\nCasos de prueba esperados:\n\"3.8,4.5,4.0\" -> Promedio 4.1 - Aprobado\n\"3.5,3.5\"     -> Promedio 3.5 - Aprobado\n\"2.0,3.0,4.0\" -> Promedio 3.0 - Plan de mejoramiento",
            "instruction": "Copia el código en Canvas sin cambiar ninguna línea. Luego, en el chat:",
            "task": [
              "Describe en tres viñetas los síntomas que veré en la vista previa, sin revelar la solución."
            ],
            "constraints": [
              "No modifiques el código en este paso."
            ],
            "format": "Código HTML ejecutable en Canvas y viñetas en el chat."
          },
          "steps": [
            "Activa Canvas en la barra del chat de Gemini, pega el prompt y envíalo.",
            "Abre la «Vista previa», presiona «Calcular» y observa el resultado.",
            "Selecciona solo la función calcularPromedio y escribe: «Corrige el error del ciclo sin tocar nada más».",
            "Selecciona solo leerNotas y escribe: «Convierte cada nota a número y descarta los valores vacíos».",
            "Selecciona solo estado y escribe: «Un promedio de 3.5 debe quedar Aprobado».",
            "Prueba en la vista previa los tres casos de prueba de los datos."
          ],
          "expected": [
            "El ciclo usa i < notas.length.",
            "leerNotas devuelve números (Number o parseFloat) y filtra vacíos.",
            "estado usa promedio >= 3.5.",
            "Los tres casos dan ≈ 4.1, 3.5 y 3.0 con el estado correcto (si ves 4.1000000000000005, resuélvelo con el reto extra).",
            "Las líneas que no seleccionaste quedaron iguales.",
            "El gráfico muestra cada nota y la línea de aprobación."
          ],
          "stretch": "Pide: «Muestra el promedio con un decimal usando toFixed y agrega validación para notas fuera de 1.0 a 5.0».",
          "prompt": "Actúa como instructor de programación que enseña a depurar.\n\nContexto: Un aprendiz entregó una calculadora de promedios que muestra «NaN». Quiero practicar la corrección función por función usando la selección de Canvas.\n\nDatos:\n\"\"\"\n<!DOCTYPE html>\n<html lang=\"es\">\n<head><meta charset=\"UTF-8\"><title>Calculadora de promedio</title></head>\n<body>\n  <h1>Calculadora de promedio</h1>\n  <label for=\"notas\">Notas separadas por coma</label>\n  <input id=\"notas\" value=\"3.8,4.5,4.0\">\n  <button id=\"calcular\">Calcular</button>\n  <p id=\"resultado\"></p>\n  <script>\n    function leerNotas(texto) {\n      return texto.split(\",\");\n    }\n\n    function calcularPromedio(notas) {\n      let suma = 0;\n      for (let i = 0; i <= notas.length; i++) {\n        suma += notas[i];\n      }\n      return suma / notas.length;\n    }\n\n    function estado(promedio) {\n      if (promedio > 3.5) return \"Aprobado\";\n      return \"Plan de mejoramiento\";\n    }\n\n    document.getElementById(\"calcular\").addEventListener(\"click\", function () {\n      const notas = leerNotas(document.getElementById(\"notas\").value);\n      const promedio = calcularPromedio(notas);\n      document.getElementById(\"resultado\").textContent =\n        \"Promedio: \" + promedio + \" - \" + estado(promedio);\n    });\n  </script>\n</body>\n</html>\n\nCasos de prueba esperados:\n\"3.8,4.5,4.0\" -> Promedio 4.1 - Aprobado\n\"3.5,3.5\"     -> Promedio 3.5 - Aprobado\n\"2.0,3.0,4.0\" -> Promedio 3.0 - Plan de mejoramiento\n\"\"\"\n\nInstrucción: Copia el código en Canvas sin cambiar ninguna línea. Luego, en el chat:\n1. Describe en tres viñetas los síntomas que veré en la vista previa, sin revelar la solución.\n\nRestricciones:\n- No modifiques el código en este paso.\n\nFormato: Código HTML ejecutable en Canvas y viñetas en el chat."
        }
      ],
      "expected": [
        {
          "criterion": "Después del paso 1, el Canvas tiene el acta completa con 4 puntos idénticos a los datos.",
          "how": "Compara el Canvas con el bloque de datos antes de editar."
        },
        {
          "criterion": "Después del paso 2, solo cambió el punto 3: ahora tiene exactamente 2 viñetas.",
          "how": "Compara con la versión anterior (deshacer o historial de versiones del Canvas)."
        },
        {
          "criterion": "Los puntos 1, 2 y 4 siguen iguales: 14 de noviembre de 2026, 8:00 a. m., ambiente 204 y rúbrica el 7 de noviembre.",
          "how": "Lee los tres puntos sin editar."
        },
        {
          "criterion": "El punto 3 conserva «dos días hábiles» y el soporte de un médico u otra autoridad.",
          "how": "Busca «dos días hábiles» con Ctrl+F."
        },
        {
          "criterion": "La segunda viñeta dice que, con un soporte válido, el aprendiz conserva la oportunidad de presentar la prueba final.",
          "how": "Lee la segunda viñeta."
        },
        {
          "criterion": "No aparecen requisitos nuevos (correos, formatos, fechas o sanciones) y ya no dice «perderán».",
          "how": "Busca «correo», «formato» y «perderán»; no debe haber resultados."
        }
      ],
      "followUps": [
        {
          "label": "PASO 2 · SELECCIONA SOLO EL PUNTO 3 EN EL CANVAS Y ESCRIBE EN EL CUADRO DE EDICIÓN",
          "text": "Reescribe este punto en tono respetuoso y claro, en dos viñetas: 1) cómo y en qué plazo se entrega el soporte de la inasistencia; 2) qué ocurre si el soporte es válido. Conserva el plazo de dos días hábiles y no agregues requisitos, correos ni fechas nuevas."
        }
      ],
      "imageSpec": {
        "blocks": [
          {
            "type": "heading",
            "text": "ACTA DE CONCERTACIÓN · Ficha 2879123"
          },
          {
            "type": "paragraph",
            "text": "1. Objeto: acordar las condiciones de la evaluación final del módulo de bases de datos."
          },
          {
            "type": "paragraph",
            "text": "2. Fecha de la prueba final: 14 de noviembre de 2026, 8:00 a. m., ambiente 204."
          },
          {
            "type": "selection",
            "title": "3. Inasistencia (fragmento editado por selección)",
            "items": [
              "Entrega del soporte: si no puedes asistir, presenta la excusa de un médico u otra autoridad dentro de los dos días hábiles siguientes a la prueba.",
              "Resultado: con un soporte válido, conservas la oportunidad de presentar la prueba final."
            ]
          },
          {
            "type": "paragraph",
            "text": "4. Compromisos: el instructor publica la rúbrica el 7 de noviembre; los aprendices confirman su asistencia en la plataforma del curso."
          }
        ],
        "file": "canvas_inline.jpg",
        "docTitle": "Acta_Concertacion_Ficha_2879123.docx",
        "chat": "Transcribí el acta en el Canvas con sus 4 puntos. Ahora selecciona solo el punto 3 y escribe la instrucción del paso 2 en el cuadro que aparece: cambiaré únicamente ese fragmento."
      }
    },
    {
      "id": "algorithm-refactor",
      "number": "7",
      "name": "Auditoría, Explicación y Refactorización de Algoritmos",
      "badge": "Algoritmos & Python",
      "icon": "⚡",
      "image": "assets/images/canvas/canvas_algoritmo.jpg",
      "imageCaption": "Reconstrucción del resultado esperado, dibujada con los datos del prompt. Tu Canvas puede variar en diseño, no en cifras ni en contenido.",
      "objective": "Depurar cuellos de botella de rendimiento, optimizar la complejidad asintótica de funciones críticas y generar documentación técnica verificable.",
      "canvasControls": [
        "⚡ Solución de referencia",
        "🧪 Pruebas de referencia"
      ],
      "masterPrompt": "Actúa como ingeniero de software que revisa el rendimiento de código Python.\n\nContexto: La función agrupa las entregas de cada corte (10.000 registros) y se volvió lenta. Se necesita una versión eficiente que devuelva exactamente el mismo resultado.\n\nDatos:\n\"\"\"\nFunción actual (Python 3.11):\n\ndef agrupar_por_estado(entregas):\n    resumen = {\"al_dia\": [], \"en_progreso\": [], \"apoyo\": []}\n    vistos = []\n    for e in entregas:\n        if e[\"documento\"] in vistos:  # se ignora el documento repetido\n            continue\n        vistos.append(e[\"documento\"])\n        if e[\"puntaje\"] >= 70:\n            resumen[\"al_dia\"].append(e[\"nombre\"])\n        elif e[\"puntaje\"] >= 50:\n            resumen[\"en_progreso\"].append(e[\"nombre\"])\n        else:\n            resumen[\"apoyo\"].append(e[\"nombre\"])\n    return resumen\n\nEntrada de ejemplo:\nentregas = [\n    {\"documento\": \"101\", \"nombre\": \"Ana\", \"puntaje\": 70.0},\n    {\"documento\": \"102\", \"nombre\": \"Luis\", \"puntaje\": 69.9},\n    {\"documento\": \"103\", \"nombre\": \"Marta\", \"puntaje\": 50.0},\n    {\"documento\": \"104\", \"nombre\": \"Jorge\", \"puntaje\": 49.9},\n    {\"documento\": \"101\", \"nombre\": \"Ana\", \"puntaje\": 95.0},\n    {\"documento\": \"105\", \"nombre\": \"Sara\", \"puntaje\": 0.0},\n    {\"documento\": \"106\", \"nombre\": \"Iván\", \"puntaje\": 100.0},\n]\n\"\"\"\n\nInstrucción: En Canvas, entrega:\n1. Una explicación de dos frases de por qué la función actual es O(n²), señalando la línea responsable.\n2. La función refactorizada en O(n), con el mismo nombre, type hints y docstring en español.\n3. Un archivo de pruebas con pytest que cubra la lista vacía, un documento repetido y los límites 0, 49.9, 50, 69.9 y 70.\n4. Un bloque final que imprima el resultado con la entrada de ejemplo.\n\nRestricciones:\n- Conserva el comportamiento: si un documento se repite, cuenta su primer registro.\n- Usa solo la biblioteca estándar de Python.\n\nFormato: La explicación primero y después dos bloques de código: la función y las pruebas.",
      "expectedResultSummary": "La explicación señala la línea «if e[\"documento\"] in vistos»: buscar en una lista recorre sus elementos, y eso ocurre dentro del ciclo. La versión nueva usa un set (conjunto) para vistos y conserva el nombre agrupar_por_estado. Con la entrada de ejemplo imprime {'al_dia': ['Ana', 'Iván'], 'en_progreso': ['Luis', 'Marta'], 'apoyo': ['Jorge', 'Sara']}. Las pruebas cubren la lista vacía, el documento repetido y los 5 límites, y «python -m pytest» termina sin fallos. La función tiene type hints y un docstring en español que menciona la complejidad O(n).",
      "variationPrompts": [
        "Pídele a Canvas: 'Convierte la función a un generador perezoso (yield) para procesar flujos masivos de 1 millón de registros sin desbordar memoria'.",
        "Selecciona el bucle principal y pide: 'Paraleliza el cálculo usando concurrent.futures con un pool de hilos'."
      ],
      "exercises": [
        {
          "id": "algo-a-duplicados-on",
          "title": "De O(n²) a O(n): refactorizar y visualizar la diferencia",
          "level": "Intermedio",
          "duration": "30 min",
          "canvasFeature": "Canvas de código Python + página HTML con gráfico",
          "goal": "Refactorizar una búsqueda de duplicados cuadrática a lineal, explicarla y demostrar la mejora con un gráfico interactivo.",
          "scenario": "El script que detecta documentos duplicados en la base de aprendices tarda demasiado. Hay que justificar el cambio ante el equipo.",
          "promptParts": {
            "role": "ingeniero de software que enseña complejidad algorítmica",
            "context": "Esta función detecta documentos duplicados, pero con la base real de 10.000 registros tarda varios segundos. Necesito una versión eficiente y entender por qué lo es.",
            "data": "def documentos_duplicados(documentos):\n    duplicados = []\n    for i in range(len(documentos)):\n        for j in range(i + 1, len(documentos)):\n            if documentos[i] == documentos[j] and documentos[i] not in duplicados:\n                duplicados.append(documentos[i])\n    return duplicados\n\n\nmuestra = [\"1001\", \"1002\", \"1003\", \"1002\", \"1004\", \"1001\", \"1005\", \"1002\"]\nprint(documentos_duplicados(muestra))  # Salida esperada: ['1001', '1002']",
            "instruction": "En Canvas, entrega:",
            "task": [
              "Una versión en Python con complejidad O(n) que conserve el orden de primera aparición.",
              "Tres pruebas con assert: la muestra, una lista vacía y una lista sin duplicados.",
              "Una explicación breve de por qué la versión original es O(n²) y la nueva O(n)."
            ],
            "constraints": [
              "Solo biblioteca estándar de Python.",
              "Explica para un aprendiz de primer trimestre, sin fórmulas innecesarias."
            ],
            "format": "Código Python con docstring, anotaciones de tipo y comentarios breves."
          },
          "steps": [
            "Activa Canvas en la barra del chat de Gemini, pega el prompt y envíalo.",
            "Revisa el resultado con la lista «Verifica tu resultado» y marca lo que ya cumple.",
            "Selecciona el bloque de pruebas y escribe: «Agrega una medición de tiempo con 10.000 elementos usando time.perf_counter».",
            "Pide en el chat: «Crea una página web con un gráfico de operaciones contra n para ambas versiones, con escala logarítmica y un control para elegir n».",
            "Abre la «Vista previa», mueve el control hasta 10.000 y activa la escala logarítmica.",
            "Explica con tus palabras por qué las curvas se separan tanto."
          ],
          "expected": [
            "La función refactorizada devuelve ['1001', '1002'] con la muestra.",
            "La lista vacía y la lista sin duplicados devuelven [].",
            "Para n = 10.000, la versión original hace 49.995.000 comparaciones y la nueva cerca de 10.000.",
            "El gráfico muestra ambas curvas y la escala logarítmica funciona.",
            "El control de n actualiza el número de comparaciones al instante.",
            "La explicación es comprensible sin conocimientos previos de notación O."
          ],
          "stretch": "Pide: «Agrega una tercera versión con collections.Counter y compárala en la misma tabla».",
          "prompt": "Actúa como ingeniero de software que enseña complejidad algorítmica.\n\nContexto: Esta función detecta documentos duplicados, pero con la base real de 10.000 registros tarda varios segundos. Necesito una versión eficiente y entender por qué lo es.\n\nDatos:\n\"\"\"\ndef documentos_duplicados(documentos):\n    duplicados = []\n    for i in range(len(documentos)):\n        for j in range(i + 1, len(documentos)):\n            if documentos[i] == documentos[j] and documentos[i] not in duplicados:\n                duplicados.append(documentos[i])\n    return duplicados\n\n\nmuestra = [\"1001\", \"1002\", \"1003\", \"1002\", \"1004\", \"1001\", \"1005\", \"1002\"]\nprint(documentos_duplicados(muestra))  # Salida esperada: ['1001', '1002']\n\"\"\"\n\nInstrucción: En Canvas, entrega:\n1. Una versión en Python con complejidad O(n) que conserve el orden de primera aparición.\n2. Tres pruebas con assert: la muestra, una lista vacía y una lista sin duplicados.\n3. Una explicación breve de por qué la versión original es O(n²) y la nueva O(n).\n\nRestricciones:\n- Solo biblioteca estándar de Python.\n- Explica para un aprendiz de primer trimestre, sin fórmulas innecesarias.\n\nFormato: Código Python con docstring, anotaciones de tipo y comentarios breves."
        },
        {
          "id": "algo-b-visualizador-ordenamiento",
          "title": "Visualizador animado: burbuja contra merge sort",
          "level": "Avanzado",
          "duration": "35 min",
          "canvasFeature": "Canvas de código + animación en vista previa",
          "goal": "Crear un visualizador que ejecuta dos algoritmos de ordenamiento lado a lado, con contadores en vivo, para comprender su eficiencia.",
          "scenario": "En la clase de algoritmia, los aprendices entienden mejor viendo las comparaciones que leyendo pseudocódigo.",
          "promptParts": {
            "role": "desarrollador de material educativo interactivo para algoritmia",
            "context": "Los aprendices entienden mejor un algoritmo cuando ven cada comparación. Quiero comparar burbuja y merge sort con el mismo arreglo.",
            "data": "ARREGLO DE PRUEBA (16 valores):\n[38, 27, 43, 3, 9, 82, 10, 55, 21, 64, 17, 49, 70, 5, 33, 91]\n\nResultado ordenado esperado:\n[3, 5, 9, 10, 17, 21, 27, 33, 38, 43, 49, 55, 64, 70, 82, 91]\n\nAlgoritmos a comparar:\n1. Burbuja, sin parada temprana.\n2. Merge sort.\n\nColores sugeridos:\n- Barra normal: gris pizarra\n- Barras comparándose: ámbar\n- Barras ya ordenadas: verde salvia",
            "instruction": "En Canvas, crea un visualizador web que:",
            "task": [
              "Muestre los dos algoritmos lado a lado, con barras proporcionales a los valores.",
              "Permita iniciar, pausar, avanzar paso a paso y reiniciar, con control de velocidad.",
              "Cuente las comparaciones de cada algoritmo en vivo.",
              "Use los colores sugeridos para el estado de cada barra.",
              "Al terminar, indique cuántas veces más comparaciones hizo burbuja."
            ],
            "constraints": [
              "Implementa los algoritmos como generadores de JavaScript.",
              "Anuncia los contadores con aria-live y no uses librerías externas."
            ],
            "format": "Un solo archivo HTML ejecutable."
          },
          "steps": [
            "Activa Canvas en la barra del chat de Gemini, pega el prompt y envíalo.",
            "Cuando termine, abre la «Vista previa» del Canvas para usar la aplicación.",
            "Presiona «Iniciar» con velocidad media y espera a que ambos terminen.",
            "Reinicia y usa «Paso a paso» en merge sort; explica en voz alta qué mezcla en cada paso.",
            "En el código, selecciona la función de burbuja y escribe: «Agrega parada temprana y muestra cuántas comparaciones ahorra».",
            "Pide en el chat: «Agrega un botón que genere un arreglo aleatorio de 16 valores»."
          ],
          "expected": [
            "Ambos paneles terminan con el arreglo ordenado esperado.",
            "Burbuja registra 120 comparaciones sin parada temprana.",
            "Merge sort registra 64 comparaciones o menos.",
            "Los colores cambian según el estado de cada barra.",
            "Pausa, paso a paso y reinicio funcionan.",
            "El botón de arreglo aleatorio genera una nueva ejecución que termina ordenada."
          ],
          "stretch": "Pide: «Agrega un modo carrera con 100 barras y un gráfico final de comparaciones por algoritmo».",
          "prompt": "Actúa como desarrollador de material educativo interactivo para algoritmia.\n\nContexto: Los aprendices entienden mejor un algoritmo cuando ven cada comparación. Quiero comparar burbuja y merge sort con el mismo arreglo.\n\nDatos:\n\"\"\"\nARREGLO DE PRUEBA (16 valores):\n[38, 27, 43, 3, 9, 82, 10, 55, 21, 64, 17, 49, 70, 5, 33, 91]\n\nResultado ordenado esperado:\n[3, 5, 9, 10, 17, 21, 27, 33, 38, 43, 49, 55, 64, 70, 82, 91]\n\nAlgoritmos a comparar:\n1. Burbuja, sin parada temprana.\n2. Merge sort.\n\nColores sugeridos:\n- Barra normal: gris pizarra\n- Barras comparándose: ámbar\n- Barras ya ordenadas: verde salvia\n\"\"\"\n\nInstrucción: En Canvas, crea un visualizador web que:\n1. Muestre los dos algoritmos lado a lado, con barras proporcionales a los valores.\n2. Permita iniciar, pausar, avanzar paso a paso y reiniciar, con control de velocidad.\n3. Cuente las comparaciones de cada algoritmo en vivo.\n4. Use los colores sugeridos para el estado de cada barra.\n5. Al terminar, indique cuántas veces más comparaciones hizo burbuja.\n\nRestricciones:\n- Implementa los algoritmos como generadores de JavaScript.\n- Anuncia los contadores con aria-live y no uses librerías externas.\n\nFormato: Un solo archivo HTML ejecutable."
        }
      ],
      "expected": [
        {
          "criterion": "La explicación señala la línea «if e[\"documento\"] in vistos»: buscar en una lista recorre sus elementos, y eso ocurre dentro del ciclo.",
          "how": "Lee la explicación antes del código."
        },
        {
          "criterion": "La versión nueva usa un set (conjunto) para vistos y conserva el nombre agrupar_por_estado.",
          "how": "Busca «set(» en el código."
        },
        {
          "criterion": "Con la entrada de ejemplo imprime {'al_dia': ['Ana', 'Iván'], 'en_progreso': ['Luis', 'Marta'], 'apoyo': ['Jorge', 'Sara']}.",
          "how": "Copia el código a tu equipo o a Google Colab y ejecútalo."
        },
        {
          "criterion": "Las pruebas cubren la lista vacía, el documento repetido y los 5 límites, y «python -m pytest» termina sin fallos.",
          "how": "Guarda la función como clasificador.py y las pruebas como test_clasificador.py, y ejecuta pytest. Si Gemini entrega todo en un solo archivo, ejecuta pytest sobre ese archivo."
        },
        {
          "criterion": "La función tiene type hints y un docstring en español que menciona la complejidad O(n).",
          "how": "Lee la firma y el docstring."
        }
      ],
      "followUps": [],
      "imageSpec": {
        "blocks": [
          {
            "type": "heading",
            "text": "clasificador.py · de O(n²) a O(n)"
          },
          {
            "type": "paragraph",
            "text": "Causa: «if e[\"documento\"] in vistos» busca en una lista dentro del ciclo. Solución: vistos pasa a ser un set."
          },
          {
            "type": "code",
            "lines": [
              "from typing import Any",
              "",
              "",
              "def agrupar_por_estado(entregas: list[dict[str, Any]]) -> dict[str, list[str]]:",
              "    \"\"\"Agrupa a los aprendices por estado según su puntaje.",
              "",
              "    Si un documento aparece varias veces, solo cuenta su primer registro.",
              "    Complejidad O(n): buscar en un set toma tiempo constante en promedio.",
              "    \"\"\"",
              "    resumen: dict[str, list[str]] = {\"al_dia\": [], \"en_progreso\": [], \"apoyo\": []}",
              "    vistos: set[str] = set()",
              "    for e in entregas:",
              "        if e[\"documento\"] in vistos:",
              "            continue",
              "        vistos.add(e[\"documento\"])",
              "        if e[\"puntaje\"] >= 70:",
              "            resumen[\"al_dia\"].append(e[\"nombre\"])",
              "        elif e[\"puntaje\"] >= 50:",
              "            resumen[\"en_progreso\"].append(e[\"nombre\"])",
              "        else:",
              "            resumen[\"apoyo\"].append(e[\"nombre\"])",
              "    return resumen"
            ]
          },
          {
            "type": "callout",
            "title": "Salida con la entrada de ejemplo",
            "text": "{'al_dia': ['Ana', 'Iván'], 'en_progreso': ['Luis', 'Marta'], 'apoyo': ['Jorge', 'Sara']}"
          },
          {
            "type": "bullets",
            "items": [
              "test_lista_vacia",
              "test_limites[0.0, 49.9, 50.0, 69.9, 70.0]",
              "test_documento_repetido_cuenta_el_primero"
            ]
          }
        ],
        "file": "canvas_algoritmo.jpg",
        "docTitle": "clasificador.py",
        "chat": "Refactoricé la función en el Canvas: el cuello de botella era buscar en una lista dentro del ciclo, y ahora uso un set. Incluí las pruebas con pytest y un bloque que imprime el resultado de la entrada de ejemplo."
      }
    },
    {
      "id": "rubric-curriculum",
      "number": "8",
      "name": "Guías Didácticas y Rúbricas Analíticas de Evaluación",
      "badge": "Didáctica & Rúbricas",
      "icon": "📚",
      "image": "assets/images/canvas/canvas_rubrica.jpg",
      "imageCaption": "Reconstrucción del resultado esperado, dibujada con los datos del prompt. Tu Canvas puede variar en diseño, no en cifras ni en contenido.",
      "objective": "Co-diseñar guías de aprendizaje completas, criterios observables y rúbricas analíticas estructuradas para formación técnica por competencias.",
      "canvasControls": [
        "💬 Preguntas de referencia",
        "📄 Exportar a Google Docs"
      ],
      "masterPrompt": "Actúa como diseñador curricular que construye instrumentos de evaluación por competencias.\n\nContexto: El equipo instructor necesita una rúbrica que dos evaluadores apliquen al mismo repositorio y obtengan la misma nota.\n\nDatos:\n\"\"\"\nPrograma: Tecnólogo en Análisis y Desarrollo de Software (datos de práctica).\nCompetencia: Construir el sistema que cumpla con los requisitos de la solución informática.\nResultado de aprendizaje: Codificar los módulos del backend según la arquitectura definida y verificarlos con pruebas automatizadas.\nEvidencia de producto: repositorio del backend con su informe de pruebas.\n\nCriterios y pesos:\nC1 Separación de capas (dominio, aplicación e infraestructura) | 30 %\nC2 Pruebas automatizadas con aserciones | 35 %\nC3 Manejo de errores HTTP con el formato de RFC 9457 | 35 %\n\nNiveles de desempeño (nota de 0,0 a 5,0):\nExcelente: de 4,5 a 5,0\nBueno: de 3,8 a 4,4\nEn desarrollo: de 3,0 a 3,7\nRequiere refuerzo: menos de 3,0\n\nNota final = suma de (nota del criterio × peso).\nCaso para calibrar: un aprendiz obtiene 4,8 en C1; 3,5 en C2; 4,0 en C3.\n\"\"\"\n\nInstrucción: En Canvas, redacta la guía de evaluación con:\n1. La identificación curricular copiada de los datos.\n2. Una rúbrica en una tabla: 3 filas (C1, C2 y C3, con su peso) y 4 columnas de nivel con el rango de nota en el encabezado.\n3. La calificación del caso para calibrar, con la operación paso a paso y el nivel que resulta.\n4. Tres preguntas de retroalimentación, una por criterio, que el instructor le haga al aprendiz.\n\nRestricciones:\n- Cada descriptor debe nombrar algo observable en el repositorio o en el informe (archivos, pruebas, códigos HTTP), no adjetivos como «bueno» o «adecuado».\n- Los pesos deben sumar 100 %.\n\nFormato: Documento con títulos numerados y la rúbrica en una sola tabla.",
      "expectedResultSummary": "La rúbrica tiene 3 criterios × 4 niveles = 12 descriptores. Los pesos 30 %, 35 % y 35 % suman 100 %. Cada descriptor nombra algo que se puede comprobar: archivos, importaciones, pruebas, fallos o códigos HTTP. La nota del caso es 4,8 × 0,30 + 3,5 × 0,35 + 4,0 × 0,35 = 1,44 + 1,225 + 1,40 = 4,065 ≈ 4,1 (Bueno). Hay 3 preguntas abiertas, una por criterio, que no se responden con «sí» o «no». La competencia y el resultado de aprendizaje coinciden palabra por palabra con los datos.",
      "variationPrompts": [
        "Pide a Canvas: 'Convierte esta rúbrica analítica en una lista de chequeo binaria (Cumple / No Cumple) para autoevaluación rápida del aprendiz'.",
        "Adapta la rúbrica para evaluar proyectos interdisciplinarios que integren desarrollo backend con diseño UI/UX."
      ],
      "exercises": [
        {
          "id": "rubric-a-calculadora-radar",
          "title": "Rúbrica analítica con calculadora de nota y gráfico radar",
          "level": "Intermedio",
          "duration": "30 min",
          "canvasFeature": "Tabla de rúbrica + aplicación web + Exportar a Hojas de cálculo",
          "goal": "Redactar una rúbrica con descriptores observables y convertirla en una calculadora que muestre la nota y el perfil del aprendiz en un radar.",
          "scenario": "Tres instructores califican el mismo proyecto y obtienen notas distintas. Una rúbrica precisa y una calculadora común reducen la diferencia.",
          "promptParts": {
            "role": "especialista en evaluación por competencias en formación técnica",
            "context": "Tres instructores califican el mismo proyecto y obtienen notas distintas. Necesitamos una rúbrica que cualquiera aplique de la misma forma.",
            "data": "RÚBRICA DE PROYECTO: API REST DE GESTIÓN DE CITAS (datos de práctica)\n\nCriterios y pesos:\n1. Diseño de endpoints y recursos: 30 %\n2. Validación de datos y manejo de errores: 25 %\n3. Documentación OpenAPI: 20 %\n4. Pruebas automatizadas: 25 %\n\nNiveles y puntaje (se admiten valores intermedios):\nExcelente = 5 | Bueno = 4 | Aceptable = 3 | Insuficiente = 1\n\nRegla de resultado:\nNota final = suma(puntaje x peso). Aprobado si la nota final >= 3,5.\nSi algún criterio queda por debajo de 3, se exige plan de mejoramiento aunque la nota apruebe.\n\nEvaluación del aprendiz 1:\nDiseño 4, Validación 3, Documentación 5, Pruebas 2",
            "instruction": "En Canvas, construye:",
            "task": [
              "La rúbrica de 4 criterios por 4 niveles, con un descriptor observable en cada celda.",
              "El cálculo paso a paso de la nota del aprendiz 1 y su resultado según la regla.",
              "Una retroalimentación de tres líneas para el aprendiz 1, centrada en su criterio más bajo."
            ],
            "constraints": [
              "Cada descriptor nombra una evidencia verificable en el código o la entrega; evita adjetivos como «bueno» o «adecuado».",
              "Tono respetuoso y orientado a la mejora."
            ],
            "format": "Tabla de rúbrica, seguida del cálculo y la retroalimentación."
          },
          "steps": [
            "Activa Canvas en la barra del chat de Gemini, pega el prompt y envíalo.",
            "Revisa el resultado con la lista «Verifica tu resultado» y marca lo que ya cumple.",
            "Selecciona una celda que te parezca ambigua y escribe: «Hazla verificable con una evidencia concreta».",
            "Exporta la rúbrica con «Exportar a Hojas de cálculo».",
            "Pide en el chat: «Crea una calculadora web: elijo el nivel de cada criterio, veo la nota en vivo y un radar del perfil frente al nivel esperado de 4».",
            "En la vista previa, carga los valores del aprendiz 1 y luego sube Pruebas a 4."
          ],
          "expected": [
            "La rúbrica tiene 16 descriptores observables.",
            "La nota del aprendiz 1 es 3,45 y el resultado es «Plan de mejoramiento».",
            "Con Pruebas en 4, la nota sube a 3,95 y el resultado pasa a «Aprobado».",
            "El radar muestra el perfil del aprendiz y la serie esperada.",
            "La retroalimentación del aprendiz 1 se enfoca en Pruebas, su criterio más bajo.",
            "La rúbrica quedó en Google Sheets."
          ],
          "stretch": "Pide: «Permite registrar 5 aprendices y muestra un radar promedio del grupo con el criterio más débil resaltado».",
          "prompt": "Actúa como especialista en evaluación por competencias en formación técnica.\n\nContexto: Tres instructores califican el mismo proyecto y obtienen notas distintas. Necesitamos una rúbrica que cualquiera aplique de la misma forma.\n\nDatos:\n\"\"\"\nRÚBRICA DE PROYECTO: API REST DE GESTIÓN DE CITAS (datos de práctica)\n\nCriterios y pesos:\n1. Diseño de endpoints y recursos: 30 %\n2. Validación de datos y manejo de errores: 25 %\n3. Documentación OpenAPI: 20 %\n4. Pruebas automatizadas: 25 %\n\nNiveles y puntaje (se admiten valores intermedios):\nExcelente = 5 | Bueno = 4 | Aceptable = 3 | Insuficiente = 1\n\nRegla de resultado:\nNota final = suma(puntaje x peso). Aprobado si la nota final >= 3,5.\nSi algún criterio queda por debajo de 3, se exige plan de mejoramiento aunque la nota apruebe.\n\nEvaluación del aprendiz 1:\nDiseño 4, Validación 3, Documentación 5, Pruebas 2\n\"\"\"\n\nInstrucción: En Canvas, construye:\n1. La rúbrica de 4 criterios por 4 niveles, con un descriptor observable en cada celda.\n2. El cálculo paso a paso de la nota del aprendiz 1 y su resultado según la regla.\n3. Una retroalimentación de tres líneas para el aprendiz 1, centrada en su criterio más bajo.\n\nRestricciones:\n- Cada descriptor nombra una evidencia verificable en el código o la entrega; evita adjetivos como «bueno» o «adecuado».\n- Tono respetuoso y orientado a la mejora.\n\nFormato: Tabla de rúbrica, seguida del cálculo y la retroalimentación."
        },
        {
          "id": "rubric-b-guia-quiz-git",
          "title": "Guía didáctica de Git con cuestionario autocalificable",
          "level": "Básico",
          "duration": "30 min",
          "canvasFeature": "Documento + menú Crear → Cuestionario + página web",
          "goal": "Producir una guía didáctica estructurada por momentos y un cuestionario interactivo con retroalimentación inmediata.",
          "scenario": "Mañana inicia la competencia de control de versiones y el instructor necesita guía, actividad práctica y evaluación diagnóstica.",
          "promptParts": {
            "role": "diseñador instruccional para formación en desarrollo de software",
            "context": "Mañana inicia la competencia de control de versiones con 25 aprendices de primer trimestre que nunca han usado Git.",
            "data": "GUÍA DIDÁCTICA: FUNDAMENTOS DE GIT (contenido de práctica)\n\nResultado de aprendizaje: Versionar el código fuente de un proyecto con Git aplicando un flujo de ramas básico.\nDuración: 4 horas. Grupo: 25 aprendices de primer trimestre.\n\nConceptos clave:\n1. Repositorio: carpeta cuyo historial controla Git.\n2. git init: crea un repositorio nuevo.\n3. git add: agrega cambios al área de preparación (staging).\n4. git commit -m: guarda una instantánea con un mensaje.\n5. git status: muestra el estado de los archivos.\n6. git log --oneline: lista el historial resumido.\n7. Rama (branch): línea de trabajo independiente.\n8. git switch -c: crea una rama y cambia a ella.\n9. git merge: integra una rama en la actual.\n10. Conflicto: dos cambios en la misma línea que Git no puede unir solo.\n\nProducto esperado: repositorio con 5 commits, una rama \"feature/login\" integrada y un conflicto resuelto.",
            "instruction": "En Canvas, redacta la guía didáctica con:",
            "task": [
              "Los cinco momentos de la sesión con su duración: reflexión inicial, contextualización, apropiación, transferencia y evaluación.",
              "Una práctica paso a paso que lleve al producto esperado, incluido un conflicto y su resolución.",
              "Una tabla de errores frecuentes con su causa y su solución.",
              "Una lista de chequeo de la evidencia."
            ],
            "constraints": [
              "Usa comandos reales y vigentes de Git (git switch en lugar de git checkout).",
              "Explica cada término técnico la primera vez que aparezca."
            ],
            "format": "Guía con un título por momento y la práctica en bloques de comandos."
          },
          "steps": [
            "Activa Canvas en la barra del chat de Gemini, pega el prompt y envíalo.",
            "Revisa el resultado con la lista «Verifica tu resultado» y marca lo que ya cumple.",
            "Selecciona la práctica y escribe: «Agrega la salida esperada de la terminal después de cada comando».",
            "Abre «Crear → Cuestionario» para generar una evaluación a partir de la guía.",
            "Pide en el chat: «Crea un cuestionario web de 10 preguntas con retroalimentación inmediata, puntaje en un gráfico de dona y opción de reiniciar con las preguntas mezcladas».",
            "Responde el cuestionario web con dos errores a propósito y revisa el puntaje.",
            "Exporta la guía a Documentos de Google."
          ],
          "expected": [
            "Los cinco momentos suman 240 minutos.",
            "La práctica produce 5 commits, la rama feature/login y un conflicto resuelto.",
            "La tabla de errores tiene al menos 4 filas con causa y solución.",
            "El cuestionario tiene 10 preguntas con retroalimentación.",
            "Con dos errores, el puntaje es 8/10 y la dona lo refleja.",
            "El botón reiniciar mezcla las preguntas."
          ],
          "stretch": "Pide: «Agrega un simulador visual de ramas que dibuje los commits como círculos conectados al ejecutar comandos escritos».",
          "prompt": "Actúa como diseñador instruccional para formación en desarrollo de software.\n\nContexto: Mañana inicia la competencia de control de versiones con 25 aprendices de primer trimestre que nunca han usado Git.\n\nDatos:\n\"\"\"\nGUÍA DIDÁCTICA: FUNDAMENTOS DE GIT (contenido de práctica)\n\nResultado de aprendizaje: Versionar el código fuente de un proyecto con Git aplicando un flujo de ramas básico.\nDuración: 4 horas. Grupo: 25 aprendices de primer trimestre.\n\nConceptos clave:\n1. Repositorio: carpeta cuyo historial controla Git.\n2. git init: crea un repositorio nuevo.\n3. git add: agrega cambios al área de preparación (staging).\n4. git commit -m: guarda una instantánea con un mensaje.\n5. git status: muestra el estado de los archivos.\n6. git log --oneline: lista el historial resumido.\n7. Rama (branch): línea de trabajo independiente.\n8. git switch -c: crea una rama y cambia a ella.\n9. git merge: integra una rama en la actual.\n10. Conflicto: dos cambios en la misma línea que Git no puede unir solo.\n\nProducto esperado: repositorio con 5 commits, una rama \"feature/login\" integrada y un conflicto resuelto.\n\"\"\"\n\nInstrucción: En Canvas, redacta la guía didáctica con:\n1. Los cinco momentos de la sesión con su duración: reflexión inicial, contextualización, apropiación, transferencia y evaluación.\n2. Una práctica paso a paso que lleve al producto esperado, incluido un conflicto y su resolución.\n3. Una tabla de errores frecuentes con su causa y su solución.\n4. Una lista de chequeo de la evidencia.\n\nRestricciones:\n- Usa comandos reales y vigentes de Git (git switch en lugar de git checkout).\n- Explica cada término técnico la primera vez que aparezca.\n\nFormato: Guía con un título por momento y la práctica en bloques de comandos."
        }
      ],
      "expected": [
        {
          "criterion": "La rúbrica tiene 3 criterios × 4 niveles = 12 descriptores.",
          "how": "Cuenta las celdas de la tabla."
        },
        {
          "criterion": "Los pesos 30 %, 35 % y 35 % suman 100 %.",
          "how": "Suma la columna de pesos."
        },
        {
          "criterion": "Cada descriptor nombra algo que se puede comprobar: archivos, importaciones, pruebas, fallos o códigos HTTP.",
          "how": "Lee cada celda y pregúntate cómo lo verificarías en el repositorio."
        },
        {
          "criterion": "La nota del caso es 4,8 × 0,30 + 3,5 × 0,35 + 4,0 × 0,35 = 1,44 + 1,225 + 1,40 = 4,065 ≈ 4,1 (Bueno).",
          "how": "Repite la operación con tu calculadora."
        },
        {
          "criterion": "Hay 3 preguntas abiertas, una por criterio, que no se responden con «sí» o «no».",
          "how": "Lee la última sección."
        },
        {
          "criterion": "La competencia y el resultado de aprendizaje coinciden palabra por palabra con los datos.",
          "how": "Compara con el bloque de datos."
        }
      ],
      "followUps": [],
      "imageSpec": {
        "blocks": [
          {
            "type": "heading",
            "text": "Guía de evaluación · Rúbrica analítica del backend"
          },
          {
            "type": "paragraph",
            "text": "Resultado de aprendizaje: Codificar los módulos del backend según la arquitectura definida y verificarlos con pruebas automatizadas."
          },
          {
            "type": "table",
            "headers": [
              "Criterio",
              "Peso",
              "Excelente (4,5–5,0)",
              "Bueno (3,8–4,4)",
              "En desarrollo (3,0–3,7)",
              "Requiere refuerzo (< 3,0)"
            ],
            "rows": [
              [
                "C1 Separación de capas (dominio, aplicación e infraestructura)",
                "30 %",
                "Ninguna clase de dominio importa el framework web ni el ORM; las dependencias apuntan hacia el dominio.",
                "Las capas existen y solo una clase de dominio importa infraestructura.",
                "Las capas existen, pero varias clases de dominio importan infraestructura.",
                "No hay carpetas ni módulos que separen las capas."
              ],
              [
                "C2 Pruebas automatizadas con aserciones",
                "35 %",
                "Cada función de negocio tiene pruebas con aserciones y el informe muestra 0 fallos.",
                "Todas las rutas tienen pruebas; faltan casos de error en algunas funciones.",
                "Hay pruebas solo para el flujo exitoso.",
                "No hay pruebas o las pruebas no tienen aserciones."
              ],
              [
                "C3 Manejo de errores HTTP con el formato de RFC 9457",
                "35 %",
                "Todos los errores responden con el formato de RFC 9457 (type, title, status, detail) y el código HTTP correcto.",
                "El formato se aplica, pero algún código HTTP no corresponde al error.",
                "Los errores se capturan con mensajes genéricos sin formato común.",
                "Las excepciones llegan al cliente con trazas internas."
              ]
            ]
          },
          {
            "type": "callout",
            "title": "Caso para calibrar",
            "text": "4,8 × 0,30 + 3,5 × 0,35 + 4,0 × 0,35 = 1,44 + 1,225 + 1,40 = 4,065 ≈ 4,1 (Bueno)"
          },
          {
            "type": "bullets",
            "items": [
              "C1: Si mañana cambias la base de datos, ¿qué archivos de tu proyecto tendrías que modificar y por qué?",
              "C2: ¿Qué prueba de tu suite fallaría si alguien elimina la validación de una nota negativa?",
              "C3: Cuando un recurso no existe, ¿qué responde tu API y cómo lo decidiste?"
            ]
          }
        ],
        "file": "canvas_rubrica.jpg",
        "docTitle": "Rubrica_Backend_Arquitectura_Pruebas.gdoc",
        "chat": "Redacté en el Canvas la guía de evaluación con la identificación curricular, la rúbrica de 3 criterios y 4 niveles, el cálculo del caso para calibrar y tres preguntas de retroalimentación."
      }
    },
    {
      "id": "sql-pipeline",
      "number": "9",
      "name": "Consultas SQL Complejas, ETL y Modelado Relacional",
      "badge": "SQL & Pipelines",
      "icon": "🗄️",
      "image": "assets/images/canvas/canvas_sql.jpg",
      "imageCaption": "Reconstrucción del resultado esperado, dibujada con los datos del prompt. Tu Canvas puede variar en diseño, no en cifras ni en contenido.",
      "objective": "Construir consultas analíticas de alto rendimiento, diseñar pipelines de transformación de datos relacionales y optimizar planes de ejecución.",
      "canvasControls": [
        "🗄️ Consulta de referencia",
        "📋 Resultado esperado"
      ],
      "masterPrompt": "Actúa como analista de bases de datos PostgreSQL.\n\nContexto: El reporte mensual por ficha tarda demasiado con 5.000 aprendices. Antes de llevar la nueva consulta a producción, se valida con la muestra de los datos.\n\nDatos:\n\"\"\"\nCREATE TABLE aprendices (id INT PRIMARY KEY, nombre TEXT NOT NULL, ficha_id INT NOT NULL);\nCREATE TABLE entregas (aprendiz_id INT REFERENCES aprendices(id), calificacion NUMERIC(2,1));\nCREATE TABLE registro_asistencia (aprendiz_id INT REFERENCES aprendices(id), fecha DATE, asistio BOOLEAN);\n\nINSERT INTO aprendices VALUES (1,'Ana',2871),(2,'Luis',2871),(3,'Marta',2871),(4,'Jorge',2872),(5,'Sara',2872);\nINSERT INTO entregas VALUES (1,4.5),(1,4.1),(2,3.8),(2,4.2),(3,4.6),(4,3.0),(4,3.4),(5,4.9);\nINSERT INTO registro_asistencia VALUES\n(1,'2026-10-05',true),(1,'2026-10-12',true),(1,'2026-10-19',true),(1,'2026-10-26',true),\n(2,'2026-10-05',true),(2,'2026-10-12',false),(2,'2026-10-19',true),(2,'2026-10-26',true),\n(3,'2026-10-05',true),(3,'2026-10-12',true),(3,'2026-10-19',true),(3,'2026-10-26',true),\n(4,'2026-10-05',true),(4,'2026-10-12',false),(4,'2026-10-19',false),(4,'2026-10-26',true);\n-- Sara (id 5) no tiene registros de asistencia en octubre.\n\"\"\"\n\nInstrucción: En Canvas, escribe en SQL:\n1. Una consulta con dos CTE: una para el promedio de notas y otra para la asistencia del 2026-10-01 al 2026-10-31.\n2. El puesto de cada aprendiz dentro de su ficha con ROW_NUMBER(), ordenado por promedio de mayor a menor.\n3. El porcentaje de asistencia con un decimal; si un aprendiz no tiene registros, debe aparecer con NULL y sin error de división por cero.\n4. La sentencia que crea un índice compuesto en registro_asistencia (aprendiz_id, fecha) que incluya asistio.\n5. La tabla de resultado que esperas con los datos de muestra.\n\nRestricciones:\n- No uses subconsultas correlacionadas.\n- Usa ROUND(…, 1) para el promedio y el porcentaje, y ordena por ficha y puesto.\n\nFormato: Un bloque SQL comentado y, debajo, la tabla de resultado esperada.",
      "expectedResultSummary": "La consulta usa WITH con dos CTE y ROW_NUMBER() OVER (PARTITION BY ficha_id ORDER BY promedio DESC). Usa LEFT JOIN y NULLIF (o CASE) para que Sara aparezca con NULL en asistencia. Resultado con la muestra: 2871 → 1 Marta 4,6 · 100,0; 2 Ana 4,3 · 100,0; 3 Luis 4,0 · 75,0. 2872 → 1 Sara 4,9 · NULL; 2 Jorge 3,2 · 50,0. El índice es CREATE INDEX … ON registro_asistencia (aprendiz_id, fecha) INCLUDE (asistio). No hay subconsultas dentro de SELECT ni de WHERE.",
      "variationPrompts": [
        "Pide a Canvas: 'Adapta esta consulta a una vista materializada (MATERIALIZED VIEW) con política de refresco concurrente'.",
        "Selecciona la función de ventana y pide: 'Calcula además el percentil de calificación (PERCENT_RANK) para identificar aprendices destacados'."
      ],
      "exercises": [
        {
          "id": "sql-a-biblioteca-dashboard",
          "title": "Consultas SQL de biblioteca ejecutadas en el navegador con tablero",
          "level": "Avanzado",
          "duration": "35 min",
          "canvasFeature": "Canvas de código SQL + página HTML con sql.js y gráficos",
          "goal": "Escribir consultas con JOIN, agregaciones y funciones de ventana, y ejecutarlas en el navegador para alimentar un tablero visual.",
          "scenario": "La biblioteca necesita saber qué libros rotan más, quién tiene préstamos vencidos y qué programa usa más el servicio.",
          "promptParts": {
            "role": "ingeniero de datos que enseña SQL",
            "context": "La biblioteca necesita saber qué libros rotan más, quién tiene préstamos vencidos y qué programa usa más el servicio. La fecha de corte es el 30 de octubre de 2026.",
            "data": "-- BASE DE DATOS DE PRÉSTAMOS DE BIBLIOTECA (datos de práctica, PostgreSQL)\nCREATE TABLE usuarios (id INT PRIMARY KEY, nombre VARCHAR(60), programa VARCHAR(40));\nCREATE TABLE libros (id INT PRIMARY KEY, titulo VARCHAR(80), categoria VARCHAR(40));\nCREATE TABLE prestamos (\n  id INT PRIMARY KEY,\n  libro_id INT REFERENCES libros(id),\n  usuario_id INT REFERENCES usuarios(id),\n  fecha_prestamo DATE NOT NULL,\n  fecha_limite DATE NOT NULL,\n  fecha_devolucion DATE\n);\n\nINSERT INTO usuarios VALUES\n(1,'Ana Torres','ADSO'),(2,'Luis Pérez','ADSO'),(3,'Marta Gil','Contabilidad'),\n(4,'Jorge Ríos','Contabilidad'),(5,'Sara Mejía','Multimedia'),(6,'Iván Cruz','Multimedia');\n\nINSERT INTO libros VALUES\n(1,'Código limpio','Programación'),(2,'Bases de datos relacionales','Programación'),\n(3,'Contabilidad básica','Finanzas'),(4,'Diseño de experiencia de usuario','Diseño'),\n(5,'Python para todos','Programación');\n\nINSERT INTO prestamos VALUES\n(1,1,1,'2026-09-01','2026-09-15','2026-09-08'),\n(2,1,2,'2026-09-10','2026-09-24','2026-09-20'),\n(3,1,5,'2026-10-01','2026-10-15',NULL),\n(4,2,1,'2026-09-03','2026-09-17','2026-09-12'),\n(5,2,2,'2026-10-05','2026-10-19',NULL),\n(6,3,3,'2026-09-02','2026-09-16','2026-09-30'),\n(7,3,4,'2026-10-02','2026-10-16','2026-10-10'),\n(8,4,5,'2026-09-15','2026-09-29','2026-09-25'),\n(9,4,6,'2026-10-08','2026-10-22',NULL),\n(10,5,1,'2026-10-12','2026-10-26','2026-10-20'),\n(11,5,2,'2026-10-14','2026-10-28',NULL),\n(12,1,6,'2026-10-16','2026-10-30','2026-10-24');",
            "instruction": "En Canvas, escribe consultas PostgreSQL que respondan:",
            "task": [
              "¿Qué libros se prestan más? Usa RANK().",
              "¿Qué préstamos están vencidos y con cuántos días de atraso?",
              "¿Cuántos préstamos tiene cada programa?",
              "¿Cuál es el promedio de días de préstamo de los libros devueltos, en general y por programa?",
              "¿Qué préstamos se devolvieron después de la fecha límite?"
            ],
            "constraints": [
              "Antes de cada consulta, escribe un comentario con la pregunta de negocio.",
              "Usa JOIN explícitos y alias legibles."
            ],
            "format": "Un script .sql comentado."
          },
          "steps": [
            "Activa Canvas en la barra del chat de Gemini, pega el prompt y envíalo.",
            "Revisa el resultado con la lista «Verifica tu resultado» y marca lo que ya cumple.",
            "Selecciona la consulta de vencidos y escribe: «Agrega el nombre del usuario y ordena por días de atraso».",
            "Pide en el chat: «Crea una página web que cargue sql.js desde CDN, cree esta base adaptada a SQLite, ejecute las consultas y muestre tarjetas, un gráfico por programa y un área para probar consultas propias».",
            "Abre la «Vista previa» y ejecuta en el área de consultas: SELECT COUNT(*) FROM prestamos;",
            "Si la página no carga, escribe: «Corrige la carga de sql.js» y pega el error."
          ],
          "expected": [
            "El ranking pone «Código limpio» en 1 (4 préstamos) y los otros cuatro libros empatan en 2 (2 préstamos).",
            "Hay 4 préstamos vencidos con 15, 11, 8 y 2 días de atraso.",
            "Por programa: ADSO 6, Multimedia 4 y Contabilidad 2.",
            "El promedio general de días de los devueltos es 11,0.",
            "Solo el préstamo 6 se devolvió con retraso (14 días).",
            "El tablero ejecuta consultas propias y muestra el resultado en tabla."
          ],
          "stretch": "Pide: «Agrega una vista materializada de indicadores mensuales y un gráfico de líneas de préstamos por semana».",
          "prompt": "Actúa como ingeniero de datos que enseña SQL.\n\nContexto: La biblioteca necesita saber qué libros rotan más, quién tiene préstamos vencidos y qué programa usa más el servicio. La fecha de corte es el 30 de octubre de 2026.\n\nDatos:\n\"\"\"\n-- BASE DE DATOS DE PRÉSTAMOS DE BIBLIOTECA (datos de práctica, PostgreSQL)\nCREATE TABLE usuarios (id INT PRIMARY KEY, nombre VARCHAR(60), programa VARCHAR(40));\nCREATE TABLE libros (id INT PRIMARY KEY, titulo VARCHAR(80), categoria VARCHAR(40));\nCREATE TABLE prestamos (\n  id INT PRIMARY KEY,\n  libro_id INT REFERENCES libros(id),\n  usuario_id INT REFERENCES usuarios(id),\n  fecha_prestamo DATE NOT NULL,\n  fecha_limite DATE NOT NULL,\n  fecha_devolucion DATE\n);\n\nINSERT INTO usuarios VALUES\n(1,'Ana Torres','ADSO'),(2,'Luis Pérez','ADSO'),(3,'Marta Gil','Contabilidad'),\n(4,'Jorge Ríos','Contabilidad'),(5,'Sara Mejía','Multimedia'),(6,'Iván Cruz','Multimedia');\n\nINSERT INTO libros VALUES\n(1,'Código limpio','Programación'),(2,'Bases de datos relacionales','Programación'),\n(3,'Contabilidad básica','Finanzas'),(4,'Diseño de experiencia de usuario','Diseño'),\n(5,'Python para todos','Programación');\n\nINSERT INTO prestamos VALUES\n(1,1,1,'2026-09-01','2026-09-15','2026-09-08'),\n(2,1,2,'2026-09-10','2026-09-24','2026-09-20'),\n(3,1,5,'2026-10-01','2026-10-15',NULL),\n(4,2,1,'2026-09-03','2026-09-17','2026-09-12'),\n(5,2,2,'2026-10-05','2026-10-19',NULL),\n(6,3,3,'2026-09-02','2026-09-16','2026-09-30'),\n(7,3,4,'2026-10-02','2026-10-16','2026-10-10'),\n(8,4,5,'2026-09-15','2026-09-29','2026-09-25'),\n(9,4,6,'2026-10-08','2026-10-22',NULL),\n(10,5,1,'2026-10-12','2026-10-26','2026-10-20'),\n(11,5,2,'2026-10-14','2026-10-28',NULL),\n(12,1,6,'2026-10-16','2026-10-30','2026-10-24');\n\"\"\"\n\nInstrucción: En Canvas, escribe consultas PostgreSQL que respondan:\n1. ¿Qué libros se prestan más? Usa RANK().\n2. ¿Qué préstamos están vencidos y con cuántos días de atraso?\n3. ¿Cuántos préstamos tiene cada programa?\n4. ¿Cuál es el promedio de días de préstamo de los libros devueltos, en general y por programa?\n5. ¿Qué préstamos se devolvieron después de la fecha límite?\n\nRestricciones:\n- Antes de cada consulta, escribe un comentario con la pregunta de negocio.\n- Usa JOIN explícitos y alias legibles.\n\nFormato: Un script .sql comentado."
        },
        {
          "id": "sql-b-limpieza-asistencia",
          "title": "Pipeline de limpieza de asistencia con diagrama entidad-relación",
          "level": "Avanzado",
          "duration": "35 min",
          "canvasFeature": "Canvas de código SQL + Mermaid erDiagram + tablero de calidad",
          "goal": "Diseñar un pipeline ETL que limpia un archivo con errores reales, documentarlo con un diagrama entidad-relación y medir la calidad antes y después.",
          "scenario": "El reporte de asistencia llega de varias sedes con formatos distintos y la coordinación no confía en los porcentajes.",
          "promptParts": {
            "role": "ingeniero de datos especializado en calidad de datos",
            "context": "El reporte de asistencia llega de varias sedes con formatos distintos y la coordinación no confía en los porcentajes.",
            "data": "ARCHIVO asistencia_cruda.csv (14 filas, datos de práctica con errores a propósito)\ndocumento,nombre,fecha,estado\n1001, ana torres ,2026-10-01,P\n1001,Ana Torres,01/10/2026,presente\n1002,LUIS PÉREZ,2026-10-01,A\n1003,marta gil,2026/10/01,Presente\n1004,Jorge Ríos,2026-10-01,\n1005,Sara Mejía,2026-10-01,tarde\n1001,Ana Torres,2026-10-02,P\n1002,Luis Pérez,02-10-2026,P\n1003,Marta Gil,2026-10-02,ausente\n1004,jorge rios,2026-10-02,P\n1005,Sara Mejía,2026-10-02,P\n,Sin Documento,2026-10-02,P\n1006,Iván Cruz,2026-13-02,P\n1003,Marta Gil,2026-10-02,A\n\nTABLA MAESTRA aprendices (nombres oficiales):\n1001,Ana Torres | 1002,Luis Pérez | 1003,Marta Gil | 1004,Jorge Ríos | 1005,Sara Mejía | 1006,Iván Cruz\n\nREGLAS DE LIMPIEZA:\n1. Fechas válidas en formatos AAAA-MM-DD, AAAA/MM/DD, DD/MM/AAAA y DD-MM-AAAA; una fecha imposible se descarta.\n2. Filas sin documento se descartan.\n3. El nombre se toma de la tabla maestra.\n4. Estados: P, presente, Presente -> Presente | A, ausente -> Ausente | tarde -> Tarde | vacío -> Sin registro.\n5. Un mismo documento y fecha cuenta una sola vez (se conserva la primera fila).",
            "instruction": "En Canvas, escribe en PostgreSQL un pipeline de limpieza por etapas:",
            "task": [
              "Carga del archivo crudo en una tabla de staging con todas las columnas como texto.",
              "Limpieza según las reglas: fechas, nombres desde la tabla maestra y estados.",
              "Deduplicación por documento y fecha con ROW_NUMBER().",
              "Tabla final de asistencia y tabla de rechazos con su motivo.",
              "Porcentaje de asistencia: Presente más Tarde sobre el total limpio."
            ],
            "constraints": [
              "Cada etapa debe poder ejecutarse de nuevo sin duplicar datos.",
              "Un comentario por cada regla aplicada."
            ],
            "format": "Script .sql dividido por etapas."
          },
          "steps": [
            "Activa Canvas en la barra del chat de Gemini, pega el prompt y envíalo.",
            "Revisa el resultado con la lista «Verifica tu resultado» y marca lo que ya cumple.",
            "Selecciona la consulta que normaliza fechas y escribe: «Agrega una prueba por cada formato admitido».",
            "Pide en el chat: «Crea una página web con el diagrama entidad-relación en Mermaid y un tablero de calidad: filas crudas, limpias, rechazadas por motivo y una dona de estados».",
            "Abre la «Vista previa» y compara el tablero con tus resultados.",
            "Pide: «Convierte el pipeline en una función almacenada que reciba la fecha de carga»."
          ],
          "expected": [
            "De 14 filas crudas quedan 10 limpias y 4 rechazadas.",
            "Rechazos: 2 duplicados, 1 sin documento y 1 fecha imposible (2026-13-02).",
            "Los estados limpios son: 6 Presente, 2 Ausente, 1 Tarde y 1 Sin registro.",
            "La asistencia (Presente + Tarde) es 70 %.",
            "Los nombres finales coinciden con la tabla maestra.",
            "El diagrama entidad-relación muestra las tres tablas y sus relaciones."
          ],
          "stretch": "Pide: «Agrega una regla para detectar aprendices con dos ausencias seguidas y muéstralos en una alerta del tablero».",
          "prompt": "Actúa como ingeniero de datos especializado en calidad de datos.\n\nContexto: El reporte de asistencia llega de varias sedes con formatos distintos y la coordinación no confía en los porcentajes.\n\nDatos:\n\"\"\"\nARCHIVO asistencia_cruda.csv (14 filas, datos de práctica con errores a propósito)\ndocumento,nombre,fecha,estado\n1001, ana torres ,2026-10-01,P\n1001,Ana Torres,01/10/2026,presente\n1002,LUIS PÉREZ,2026-10-01,A\n1003,marta gil,2026/10/01,Presente\n1004,Jorge Ríos,2026-10-01,\n1005,Sara Mejía,2026-10-01,tarde\n1001,Ana Torres,2026-10-02,P\n1002,Luis Pérez,02-10-2026,P\n1003,Marta Gil,2026-10-02,ausente\n1004,jorge rios,2026-10-02,P\n1005,Sara Mejía,2026-10-02,P\n,Sin Documento,2026-10-02,P\n1006,Iván Cruz,2026-13-02,P\n1003,Marta Gil,2026-10-02,A\n\nTABLA MAESTRA aprendices (nombres oficiales):\n1001,Ana Torres | 1002,Luis Pérez | 1003,Marta Gil | 1004,Jorge Ríos | 1005,Sara Mejía | 1006,Iván Cruz\n\nREGLAS DE LIMPIEZA:\n1. Fechas válidas en formatos AAAA-MM-DD, AAAA/MM/DD, DD/MM/AAAA y DD-MM-AAAA; una fecha imposible se descarta.\n2. Filas sin documento se descartan.\n3. El nombre se toma de la tabla maestra.\n4. Estados: P, presente, Presente -> Presente | A, ausente -> Ausente | tarde -> Tarde | vacío -> Sin registro.\n5. Un mismo documento y fecha cuenta una sola vez (se conserva la primera fila).\n\"\"\"\n\nInstrucción: En Canvas, escribe en PostgreSQL un pipeline de limpieza por etapas:\n1. Carga del archivo crudo en una tabla de staging con todas las columnas como texto.\n2. Limpieza según las reglas: fechas, nombres desde la tabla maestra y estados.\n3. Deduplicación por documento y fecha con ROW_NUMBER().\n4. Tabla final de asistencia y tabla de rechazos con su motivo.\n5. Porcentaje de asistencia: Presente más Tarde sobre el total limpio.\n\nRestricciones:\n- Cada etapa debe poder ejecutarse de nuevo sin duplicar datos.\n- Un comentario por cada regla aplicada.\n\nFormato: Script .sql dividido por etapas."
        }
      ],
      "expected": [
        {
          "criterion": "La consulta usa WITH con dos CTE y ROW_NUMBER() OVER (PARTITION BY ficha_id ORDER BY promedio DESC).",
          "how": "Lee el bloque SQL."
        },
        {
          "criterion": "Usa LEFT JOIN y NULLIF (o CASE) para que Sara aparezca con NULL en asistencia.",
          "how": "Busca «LEFT JOIN» y «NULLIF» en el código."
        },
        {
          "criterion": "Resultado con la muestra: 2871 → 1 Marta 4,6 · 100,0; 2 Ana 4,3 · 100,0; 3 Luis 4,0 · 75,0. 2872 → 1 Sara 4,9 · NULL; 2 Jorge 3,2 · 50,0.",
          "how": "Ejecuta el esquema y la consulta en PostgreSQL (local o en un editor en línea) y compara fila por fila."
        },
        {
          "criterion": "El índice es CREATE INDEX … ON registro_asistencia (aprendiz_id, fecha) INCLUDE (asistio).",
          "how": "Lee la sentencia del índice."
        },
        {
          "criterion": "No hay subconsultas dentro de SELECT ni de WHERE.",
          "how": "Busca «(SELECT» en el código; solo debe aparecer dentro de las CTE."
        }
      ],
      "followUps": [],
      "imageSpec": {
        "blocks": [
          {
            "type": "heading",
            "text": "Reporte mensual por ficha · PostgreSQL"
          },
          {
            "type": "code",
            "lines": [
              "-- Promedio de notas por aprendiz",
              "WITH notas AS (",
              "  SELECT aprendiz_id, ROUND(AVG(calificacion), 1) AS promedio",
              "  FROM entregas",
              "  GROUP BY aprendiz_id",
              "),",
              "-- Asistencia de octubre de 2026",
              "asistencia AS (",
              "  SELECT aprendiz_id,",
              "         COUNT(*) AS total,",
              "         COUNT(*) FILTER (WHERE asistio) AS asistidas",
              "  FROM registro_asistencia",
              "  WHERE fecha BETWEEN DATE '2026-10-01' AND DATE '2026-10-31'",
              "  GROUP BY aprendiz_id",
              ")",
              "SELECT a.ficha_id,",
              "       ROW_NUMBER() OVER (PARTITION BY a.ficha_id ORDER BY n.promedio DESC) AS puesto,",
              "       a.nombre,",
              "       n.promedio,",
              "       ROUND(100.0 * s.asistidas / NULLIF(s.total, 0), 1) AS pct_asistencia",
              "FROM aprendices a",
              "JOIN notas n ON n.aprendiz_id = a.id",
              "LEFT JOIN asistencia s ON s.aprendiz_id = a.id",
              "ORDER BY a.ficha_id, puesto;",
              "",
              "-- Índice para filtrar por aprendiz y fecha sin leer la tabla completa",
              "CREATE INDEX CONCURRENTLY idx_asistencia_aprendiz_fecha",
              "  ON registro_asistencia (aprendiz_id, fecha) INCLUDE (asistio);"
            ]
          },
          {
            "type": "subheading",
            "text": "Resultado esperado con la muestra"
          },
          {
            "type": "table",
            "headers": [
              "ficha_id",
              "puesto",
              "nombre",
              "promedio",
              "pct_asistencia"
            ],
            "rows": [
              [
                "2871",
                "1",
                "Marta",
                "4,6",
                "100,0"
              ],
              [
                "2871",
                "2",
                "Ana",
                "4,3",
                "100,0"
              ],
              [
                "2871",
                "3",
                "Luis",
                "4,0",
                "75,0"
              ],
              [
                "2872",
                "1",
                "Sara",
                "4,9",
                "NULL"
              ],
              [
                "2872",
                "2",
                "Jorge",
                "3,2",
                "50,0"
              ]
            ]
          }
        ],
        "file": "canvas_sql.jpg",
        "docTitle": "Reporte_Mensual_Por_Ficha.sql",
        "chat": "Escribí en el Canvas la consulta con dos CTE, el puesto por ficha con ROW_NUMBER(), el porcentaje de asistencia protegido con NULLIF, el índice compuesto y la tabla de resultado que debe dar la muestra."
      }
    },
    {
      "id": "executive-report",
      "number": "10",
      "name": "Informes Ejecutivos Multifuente y Dictamen Técnico",
      "badge": "Auditoría & Dictámenes",
      "icon": "⚖️",
      "image": "assets/images/canvas/canvas_informe.jpg",
      "imageCaption": "Reconstrucción del resultado esperado, dibujada con los datos del prompt. Tu Canvas puede variar en diseño, no en cifras ni en contenido.",
      "objective": "Cruzar múltiples auditorías e informes densos para extraer conclusiones directivas, matrices de riesgo y planes de acción con valor legal y técnico.",
      "canvasControls": [
        "⚖️ Ver regla del semáforo",
        "📄 Exportar a Google Docs"
      ],
      "masterPrompt": "Actúa como auditor de sistemas que redacta dictámenes para comités directivos.\n\nContexto: El comité directivo decide el lunes si libera el pago final del contrato. Necesita un dictamen de una página basado solo en los extractos de los informes.\n\nDatos:\n\"\"\"\nContrato: CT-2026-014, plataforma de gestión académica (datos de práctica).\nFecha de corte: 30 de octubre de 2026.\nAvance certificado por la interventoría: 94 %.\nPagos: 80 % desembolsado; 20 % final pendiente.\n\nExtractos de los informes:\n[INF-1, p. 12] Interventoría técnica: Los tokens JWT de acceso no expiran y no existe un mecanismo de revocación. Impacto: Alto. Probabilidad: Media.\n[INF-2, p. 31] Rendimiento: La tabla bitacora_auditoria tiene 2,1 millones de filas sin índices; la consulta de auditoría tarda 6,8 s. Impacto: Medio. Probabilidad: Media.\n[INF-3, p. 7] Protección de datos: La autorización de tratamiento de datos está implementada; falta publicar el aviso de privacidad en el formulario de inscripción. Impacto: Medio. Probabilidad: Baja.\n\nRegla del semáforo: Bajo o Baja = 1; Medio o Media = 2; Alto o Alta = 3. Puntaje = impacto × probabilidad. De 1 a 2: VERDE; de 3 a 4: ÁMBAR; de 6 a 9: ROJO.\nRegla de decisión: si hay al menos un riesgo ROJO, el pago final queda retenido hasta que se subsane; si no, se aprueba con observaciones.\nPlazo de subsanación: 10 días hábiles desde la notificación.\n\"\"\"\n\nInstrucción: En Canvas, redacta el dictamen técnico con:\n1. Encabezado con el contrato, el objeto, la fecha de corte, el avance certificado y el estado de los pagos.\n2. Síntesis en tres párrafos cortos: cumplimiento general, hallazgos y decisión.\n3. Matriz de riesgos con las columnas Hallazgo, Fuente, Impacto, Probabilidad, Puntaje y Semáforo, calculada con la regla de los datos.\n4. Decisión y plazo de subsanación según la regla de decisión, con una acción recomendada por hallazgo.\n\nRestricciones:\n- Usa solo los hechos de los extractos, cita la fuente de cada uno (por ejemplo [INF-1, p. 12]) y presenta las acciones como recomendaciones.\n- Calcula la matriz antes de redactar la síntesis: los colores que mencione la síntesis deben coincidir con la matriz.\n- Escribe los porcentajes como texto normal (94 %), sin notación matemática, y no pases de una página (unas 450 palabras).\n\nFormato: Documento formal en español de Colombia, con la matriz en una tabla.",
      "expectedResultSummary": "El encabezado dice CT-2026-014, 30 de octubre de 2026, avance del 94 % y pagos: 80 % desembolsado y 20 % pendiente, con el signo % visible. La síntesis menciona los mismos colores que la matriz: 1 ROJO, 1 ÁMBAR y 1 VERDE. La matriz tiene 3 filas: Tokens JWT sin expiración ni revocación = 3 × 2 = 6 · ROJO; Bitácora de auditoría sin índices = 2 × 2 = 4 · ÁMBAR; Aviso de privacidad (Ley 1581 de 2012) = 2 × 1 = 2 · VERDE. Cada hallazgo cita su fuente: [INF-1, p. 12], [INF-2, p. 31], [INF-3, p. 7]. La decisión aplica la regla: El pago final del 20 % queda retenido hasta que se subsane el riesgo ROJO. Plazo de subsanación: 10 días hábiles desde la notificación. No aparecen hechos que no estén en los extractos (otras tecnologías, cifras, normas o fechas). Cabe en una página: unas 450 palabras o menos.",
      "variationPrompts": [
        "Haz clic en 'Ajustar longitud > Muy conciso' para generar un borrador de correo para el director de centro.",
        "Pide a Canvas: 'Genera el cuadro de compromisos con responsables y fechas para la reunión de seguimiento'."
      ],
      "exercises": [
        {
          "id": "report-a-mesa-ayuda",
          "title": "Informe ejecutivo de mesa de ayuda con infografía y resumen en audio",
          "level": "Intermedio",
          "duration": "30 min",
          "canvasFeature": "Documento + Crear → Infografía + Crear → Resumen en audio",
          "goal": "Convertir seis meses de indicadores en un informe ejecutivo con conclusiones causales y producir dos formatos de difusión desde el mismo Canvas.",
          "scenario": "La dirección quiere saber si las inversiones de agosto y septiembre mejoraron el servicio y necesita compartirlo con todo el personal.",
          "promptParts": {
            "role": "auditor de servicios TI que informa a la alta dirección",
            "context": "La dirección quiere saber si el portal de autoservicio y el segundo técnico mejoraron el servicio, y compartirá el resultado con todo el personal.",
            "data": "MESA DE AYUDA TIC — MAYO A OCTUBRE DE 2026 (datos de práctica)\n\nMes,Abiertos,Resueltos,Tiempo_promedio_horas,Satisfaccion_1a5\nMay,120,110,18,4.1\nJun,135,128,16,4.2\nJul,160,140,22,3.8\nAgo,150,149,14,4.4\nSep,142,140,12,4.5\nOct,128,127,10,4.6\n\nCategorías de octubre:\nAcceso y contraseñas,45\nRed y wifi,30\nEquipos,25\nSoftware,20\nOtros,8\n\nHechos relevantes:\n- Julio: inicio de trimestre con 300 aprendices nuevos y caída del servidor de autenticación (2 días).\n- Agosto: se publicó un portal de autoservicio para restablecer contraseñas.\n- Septiembre: se contrató un segundo técnico de soporte.",
            "instruction": "En Canvas, redacta un informe ejecutivo con:",
            "task": [
              "Un resumen de máximo cinco líneas con las tres cifras más importantes.",
              "Una tabla mensual con la tasa de resolución y el tiempo promedio.",
              "Una comparación entre mayo-julio y agosto-octubre.",
              "Qué cambios pueden atribuirse a cada hecho relevante y cuáles podrían ser coincidencia.",
              "Tres decisiones recomendadas."
            ],
            "constraints": [
              "No afirmes causalidad sin advertir sus límites.",
              "Cifras con un decimal y formato colombiano."
            ],
            "format": "Títulos numerados, una tabla y un cuadro de decisiones."
          },
          "steps": [
            "Activa Canvas en la barra del chat de Gemini, pega el prompt y envíalo.",
            "Revisa el resultado con la lista «Verifica tu resultado» y marca lo que ya cumple.",
            "Selecciona la sección de hechos relevantes y escribe: «Separa la evidencia fuerte de las coincidencias en dos columnas».",
            "Abre «Crear → Infografía» para la versión visual.",
            "Abre «Crear → Resumen en audio» para una versión que el personal pueda escuchar.",
            "Exporta el informe a Documentos de Google."
          ],
          "expected": [
            "El tiempo promedio baja de 18 a 10 horas (−44,4 %).",
            "La satisfacción sube de 4,1 a 4,6.",
            "El promedio de tiempo antes (mayo-julio) es 18,7 h y después (agosto-octubre) 12,0 h.",
            "Acceso y contraseñas es la principal categoría de octubre (35,2 %).",
            "El informe advierte los límites de atribuir causalidad.",
            "Se generaron la infografía y el resumen en audio."
          ],
          "stretch": "Pide: «Crea una página web con un tablero de los seis meses, un gráfico combinado de barras y líneas y un filtro por periodo».",
          "prompt": "Actúa como auditor de servicios TI que informa a la alta dirección.\n\nContexto: La dirección quiere saber si el portal de autoservicio y el segundo técnico mejoraron el servicio, y compartirá el resultado con todo el personal.\n\nDatos:\n\"\"\"\nMESA DE AYUDA TIC — MAYO A OCTUBRE DE 2026 (datos de práctica)\n\nMes,Abiertos,Resueltos,Tiempo_promedio_horas,Satisfaccion_1a5\nMay,120,110,18,4.1\nJun,135,128,16,4.2\nJul,160,140,22,3.8\nAgo,150,149,14,4.4\nSep,142,140,12,4.5\nOct,128,127,10,4.6\n\nCategorías de octubre:\nAcceso y contraseñas,45\nRed y wifi,30\nEquipos,25\nSoftware,20\nOtros,8\n\nHechos relevantes:\n- Julio: inicio de trimestre con 300 aprendices nuevos y caída del servidor de autenticación (2 días).\n- Agosto: se publicó un portal de autoservicio para restablecer contraseñas.\n- Septiembre: se contrató un segundo técnico de soporte.\n\"\"\"\n\nInstrucción: En Canvas, redacta un informe ejecutivo con:\n1. Un resumen de máximo cinco líneas con las tres cifras más importantes.\n2. Una tabla mensual con la tasa de resolución y el tiempo promedio.\n3. Una comparación entre mayo-julio y agosto-octubre.\n4. Qué cambios pueden atribuirse a cada hecho relevante y cuáles podrían ser coincidencia.\n5. Tres decisiones recomendadas.\n\nRestricciones:\n- No afirmes causalidad sin advertir sus límites.\n- Cifras con un decimal y formato colombiano.\n\nFormato: Títulos numerados, una tabla y un cuadro de decisiones."
        },
        {
          "id": "report-b-dictamen-nube",
          "title": "Dictamen técnico de proveedores con matriz ponderada y análisis de sensibilidad",
          "level": "Avanzado",
          "duration": "35 min",
          "canvasFeature": "Documento + aplicación web con radar y pesos ajustables",
          "goal": "Emitir un dictamen técnico defendible con una matriz ponderada y demostrar con una herramienta interactiva cómo cambia la decisión si cambian los pesos.",
          "scenario": "El comité de compras debe elegir proveedor de nube. Los puntajes son muy cercanos y alguien preguntará qué pasa si la seguridad pesa más.",
          "promptParts": {
            "role": "consultor de arquitectura en la nube con experiencia en contratación pública",
            "context": "El comité de compras elegirá proveedor de nube. Los puntajes son cercanos y el comité preguntará qué pasa si la seguridad pesa más.",
            "data": "EVALUACIÓN DE PROVEEDORES DE NUBE PARA LA PLATAFORMA DE FORMACIÓN (datos de práctica)\nLos nombres se anonimizan como Proveedor A, B y C.\n\nCriterio,Peso,Proveedor A,Proveedor B,Proveedor C\nCosto anual,30%,4,5,3\nSoporte en español,15%,3,4,5\nSeguridad y certificaciones,25%,5,3,4\nFacilidad de administración,15%,4,4,3\nRegión de datos y latencia,15%,3,4,5\n\nEscala: 1 (muy deficiente) a 5 (excelente).\nCosto anual estimado (COP): A 96.000.000 | B 78.000.000 | C 110.000.000\nRequisito no negociable: los datos personales deben cumplir la Ley 1581 de 2012 de Colombia.",
            "instruction": "En Canvas, redacta el dictamen técnico con:",
            "task": [
              "La metodología y la matriz ponderada con el cálculo de cada proveedor.",
              "Un análisis de sensibilidad con Seguridad en 35 % y Costo en 20 %, sin cambiar los demás pesos.",
              "El costo anual por punto de calidad de cada proveedor.",
              "La recomendación final con las condiciones contractuales mínimas para cumplir la Ley 1581 de 2012."
            ],
            "constraints": [
              "Muestra cada cálculo.",
              "No uses marcas comerciales reales."
            ],
            "format": "Documento con títulos numerados y tablas."
          },
          "steps": [
            "Activa Canvas en la barra del chat de Gemini, pega el prompt y envíalo.",
            "Revisa el resultado con la lista «Verifica tu resultado» y marca lo que ya cumple.",
            "Selecciona la recomendación y escribe: «Agrega una cláusula de salida y portabilidad de datos».",
            "Pide en el chat: «Crea una aplicación web con un control por cada peso (la suma debe ser 100 %), ranking en vivo, un radar de los tres proveedores y un aviso cuando cambie el ganador».",
            "En la vista previa, mueve los pesos hasta que cambie el ganador.",
            "Exporta el dictamen a Documentos de Google para su firma."
          ],
          "expected": [
            "La matriz base da B 4,05, A 3,95 y C 3,85.",
            "Con Seguridad 35 % y Costo 20 %: A 4,05, C 3,95 y B 3,85.",
            "El costo por punto es menor en B (≈ 19,3 millones por punto).",
            "La recomendación incluye condiciones de la Ley 1581 de 2012.",
            "La app recalcula el ranking y el radar al mover los pesos.",
            "La app avisa cuando cambia el proveedor ganador."
          ],
          "stretch": "Pide: «Agrega una simulación de Monte Carlo que varíe los pesos ±5 puntos y muestre en qué porcentaje de casos gana cada proveedor».",
          "prompt": "Actúa como consultor de arquitectura en la nube con experiencia en contratación pública.\n\nContexto: El comité de compras elegirá proveedor de nube. Los puntajes son cercanos y el comité preguntará qué pasa si la seguridad pesa más.\n\nDatos:\n\"\"\"\nEVALUACIÓN DE PROVEEDORES DE NUBE PARA LA PLATAFORMA DE FORMACIÓN (datos de práctica)\nLos nombres se anonimizan como Proveedor A, B y C.\n\nCriterio,Peso,Proveedor A,Proveedor B,Proveedor C\nCosto anual,30%,4,5,3\nSoporte en español,15%,3,4,5\nSeguridad y certificaciones,25%,5,3,4\nFacilidad de administración,15%,4,4,3\nRegión de datos y latencia,15%,3,4,5\n\nEscala: 1 (muy deficiente) a 5 (excelente).\nCosto anual estimado (COP): A 96.000.000 | B 78.000.000 | C 110.000.000\nRequisito no negociable: los datos personales deben cumplir la Ley 1581 de 2012 de Colombia.\n\"\"\"\n\nInstrucción: En Canvas, redacta el dictamen técnico con:\n1. La metodología y la matriz ponderada con el cálculo de cada proveedor.\n2. Un análisis de sensibilidad con Seguridad en 35 % y Costo en 20 %, sin cambiar los demás pesos.\n3. El costo anual por punto de calidad de cada proveedor.\n4. La recomendación final con las condiciones contractuales mínimas para cumplir la Ley 1581 de 2012.\n\nRestricciones:\n- Muestra cada cálculo.\n- No uses marcas comerciales reales.\n\nFormato: Documento con títulos numerados y tablas."
        }
      ],
      "expected": [
        {
          "criterion": "El encabezado dice CT-2026-014, 30 de octubre de 2026, avance del 94 % y pagos: 80 % desembolsado y 20 % pendiente, con el signo % visible.",
          "how": "Lee el encabezado; si ves «94» sin «%», Gemini usó notación matemática y debes pedirle que la quite."
        },
        {
          "criterion": "La síntesis menciona los mismos colores que la matriz: 1 ROJO, 1 ÁMBAR y 1 VERDE.",
          "how": "Compara el segundo y el tercer párrafo de la síntesis con la columna Semáforo."
        },
        {
          "criterion": "La matriz tiene 3 filas: Tokens JWT sin expiración ni revocación = 3 × 2 = 6 · ROJO; Bitácora de auditoría sin índices = 2 × 2 = 4 · ÁMBAR; Aviso de privacidad (Ley 1581 de 2012) = 2 × 1 = 2 · VERDE.",
          "how": "Recalcula cada puntaje con la regla del semáforo."
        },
        {
          "criterion": "Cada hallazgo cita su fuente: [INF-1, p. 12], [INF-2, p. 31], [INF-3, p. 7].",
          "how": "Busca «INF-» con Ctrl+F; deben aparecer las tres fuentes."
        },
        {
          "criterion": "La decisión aplica la regla: El pago final del 20 % queda retenido hasta que se subsane el riesgo ROJO. Plazo de subsanación: 10 días hábiles desde la notificación.",
          "how": "Lee el último párrafo y la sección de decisión."
        },
        {
          "criterion": "No aparecen hechos que no estén en los extractos (otras tecnologías, cifras, normas o fechas).",
          "how": "Subraya cada cifra del dictamen y búscala en los datos."
        },
        {
          "criterion": "Cabe en una página: unas 450 palabras o menos.",
          "how": "Exporta a Google Docs y revisa el número de páginas."
        }
      ],
      "followUps": [],
      "imageSpec": {
        "blocks": [
          {
            "type": "heading",
            "text": "DICTAMEN TÉCNICO · CONTRATO CT-2026-014"
          },
          {
            "type": "paragraph",
            "text": "Corte: 30 de octubre de 2026 · Avance certificado: 94 % · Pagos: 80 % desembolsado, 20 % pendiente."
          },
          {
            "type": "callout",
            "title": "Síntesis",
            "text": "Tres hallazgos: tokens JWT sin expiración [INF-1, p. 12], bitácora sin índices [INF-2, p. 31] y aviso de privacidad pendiente [INF-3, p. 7]."
          },
          {
            "type": "table",
            "headers": [
              "Hallazgo",
              "Fuente",
              "Impacto",
              "Probabilidad",
              "Puntaje",
              "Semáforo"
            ],
            "rows": [
              [
                "Tokens JWT sin expiración ni revocación",
                "INF-1, p. 12",
                "Alto",
                "Media",
                "6",
                "ROJO"
              ],
              [
                "Bitácora de auditoría sin índices",
                "INF-2, p. 31",
                "Medio",
                "Media",
                "4",
                "ÁMBAR"
              ],
              [
                "Aviso de privacidad (Ley 1581 de 2012)",
                "INF-3, p. 7",
                "Medio",
                "Baja",
                "2",
                "VERDE"
              ]
            ]
          },
          {
            "type": "callout",
            "title": "Decisión",
            "text": "El pago final del 20 % queda retenido hasta que se subsane el riesgo ROJO. Plazo de subsanación: 10 días hábiles desde la notificación."
          }
        ],
        "file": "canvas_informe.jpg",
        "docTitle": "Dictamen_Tecnico_CT-2026-014.pdf",
        "chat": "Redacté el dictamen en el Canvas a partir de los tres extractos: encabezado del contrato, síntesis, matriz de riesgos calculada con la regla del semáforo y la decisión sobre el pago final con su plazo."
      }
    }
  ]
}
