{"openapi":"3.0.3","info":{"title":"Lumbre · API para agentes","version":"v1","description":"API abierta para crear, validar, leer y organizar objetos de aprendizaje interactivos (formato lumbre-lo\/1.0). Pensada para agentes IA: descubre los tipos de bloque en \/api\/v1\/block-types, valida sin publicar en \/api\/v1\/objects\/validate y publica en \/api\/v1\/objects. Autenticación por clave de agente (Bearer); los endpoints de descubrimiento y esquema son públicos.","license":{"name":"Uso interno · Lumbre"}},"servers":[{"url":"https:\/\/univ.lumbre.lat","description":"Instancia Lumbre"}],"tags":[{"name":"Knowledge — Public","description":"Lumbre Knowledge Gateway: público, gratuito y SOLO LECTURA, sin clave. Mismos datos y atribución que el MCP \/mcp\/knowledge. Attribution required."},{"name":"Descubrimiento","description":"Público, sin clave: esquema, tipos de bloque, taxonomías y OpenAPI."},{"name":"Objetos","description":"Crear, validar, listar y leer objetos de aprendizaje."},{"name":"Libros","description":"Crear, validar, listar, leer y editar LIBROS de lectura (entidad books): lecturas cortas (short_read), libros largos de texto y cómics\/PDF de lectura directa. Misma clave de agente que los objetos."},{"name":"Laboratorios","description":"Crear, validar y listar laboratorios interactivos (formato lumbre-lab\/1.0)."},{"name":"Estructura","description":"Crear y organizar categorías, cursos y rutas."},{"name":"Cura de grupos","description":"Analizar y ajustar de golpe UN GRUPO completo (curso, categoría o ruta): huecos de contenido y salud de medios en todos sus objetos, y tanda de ops en masa."},{"name":"Medios","description":"Validación server-side de video (oembed) e imagen (mime real + dimensiones + apta como portada) antes de incrustarlas: el agente no necesita acceso a YouTube ni arriesgar links muertos."},{"name":"Resultados","description":"Guardar el resultado del alumno en un bloque puntuado."}],"security":[{"bearerAuth":[]}],"paths":{"\/openapi.json":{"get":{"tags":["Descubrimiento"],"summary":"Documento OpenAPI 3.0.3 de esta API.","responses":{"200":{"description":"Documento OpenAPI."}},"security":[]}},"\/api\/docs":{"get":{"tags":["Descubrimiento"],"summary":"Visor HTML ligero de la API (lee \/openapi.json).","responses":{"200":{"description":"Página HTML."}},"security":[]}},"\/api\/v1\/objects\/schema":{"get":{"tags":["Descubrimiento"],"summary":"Esquema machine-readable del formato de objeto (meta, bloques, reglas, publicación).","responses":{"200":{"description":"Esquema del formato."}},"security":[]}},"\/api\/v1\/block-types":{"get":{"tags":["Descubrimiento"],"summary":"Lista resumida de todos los tipos de bloque disponibles.","responses":{"200":{"description":"Lista de tipos de bloque.","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/BlockTypeList"}}}}},"security":[]}},"\/api\/v1\/block-types\/{type}":{"get":{"tags":["Descubrimiento"],"summary":"Detalle de un tipo de bloque: JSON Schema, ejemplo, cuándo usarlo y forma de la respuesta.","responses":{"200":{"description":"Detalle del bloque.","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/BlockTypeDetail"}}}}},"requestBody":{"required":true,"content":{"application\/json":{"schema":[{"name":"type","in":"path","required":true,"description":"Nombre del tipo de bloque.","schema":{"type":"string","example":"heading"}}]}}},"security":[]}},"\/api\/v1\/taxonomies":{"get":{"tags":["Descubrimiento"],"summary":"Enumeraciones del dominio: materias, niveles cognitivos, niveles educativos y dificultades.","responses":{"200":{"description":"Taxonomías.","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Taxonomies"}}}}},"security":[]}},"\/api\/v1\/structure\/schema":{"get":{"tags":["Descubrimiento"],"summary":"Esquema machine-readable de la API de estructura (categorías, cursos, rutas).","responses":{"200":{"description":"Esquema de estructura."}},"security":[]}},"\/api\/v1\/knowledge":{"get":{"tags":["Knowledge — Public"],"summary":"Descubrimiento del gateway: qué es Lumbre, conteos reales del catálogo, interfaces (MCP\/REST), tools y límites.","responses":{"200":{"description":"Metadatos del gateway con source\/attribution\/usage."}},"security":[]}},"\/api\/v1\/knowledge\/search":{"get":{"tags":["Knowledge — Public"],"summary":"Busca en TODO el catálogo público: objetos, labs, habilidades, cursos, categorías y rutas. Cada ítem trae canonical_url, citation y suggested_attribution.","responses":{"200":{"description":"Resultados normalizados."}},"requestBody":{"required":true,"content":{"application\/json":{"schema":[{"name":"q","in":"query","required":false,"description":"Texto de búsqueda.","schema":{"type":"string","example":"fotosíntesis"}},{"name":"resource_type","in":"query","required":false,"description":"learning_object,lab,skill,course,category,route (csv).","schema":{"type":"string"}},{"name":"age","in":"query","required":false,"description":"Edad del estudiante (filtra por rangos).","schema":{"type":"string"}},{"name":"subject","in":"query","required":false,"description":"Materia exacta.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Resultados (1-50).","schema":{"type":"string"}}]}}},"security":[]}},"\/api\/v1\/knowledge\/resource\/{type}\/{slug}":{"get":{"tags":["Knowledge — Public"],"summary":"Un recurso concreto por tipo + slug\/id\/uid\/URL canónica. Los OA traen bloques saneados (nunca respuestas de ejercicios) y multimedia marcada external:true cuando enlaza fuera.","responses":{"200":{"description":"Recurso con atribución."}},"requestBody":{"required":true,"content":{"application\/json":{"schema":[{"name":"type","in":"path","required":true,"description":"learning_object|lab|skill|course|category|route.","schema":{"type":"string","example":"learning_object"}},{"name":"slug","in":"path","required":true,"description":"Slug, id, uid o canonical_url.","schema":{"type":"string","example":"fotosintesis"}}]}}},"security":[]}},"\/api\/v1\/knowledge\/context":{"get":{"tags":["Knowledge — Public"],"summary":"Contexto educativo listo para un LLM: explicaciones REALES de objetos publicados, ejemplos, prerrequisitos, temas siguientes y fuentes. Nada se genera con IA en la llamada.","responses":{"200":{"description":"Contexto con sources[] y attribution.required=true."}},"requestBody":{"required":true,"content":{"application\/json":{"schema":[{"name":"q","in":"query","required":false,"description":"Tema a explicar.","schema":{"type":"string","example":"fracciones equivalentes"}},{"name":"age","in":"query","required":false,"description":"Edad objetivo.","schema":{"type":"string"}},{"name":"purpose","in":"query","required":false,"description":"explain|study|practice|teach.","schema":{"type":"string"}}]}}},"security":[]}},"\/api\/v1\/knowledge\/related":{"get":{"tags":["Knowledge — Public"],"summary":"Recursos relacionados de uno ancla (grafo content_relations + materia\/etiquetas).","responses":{"200":{"description":"Relacionados con atribución."}},"requestBody":{"required":true,"content":{"application\/json":{"schema":[{"name":"resource","in":"query","required":false,"description":"Slug, id o canonical_url.","schema":{"type":"string"}}]}}},"security":[]}},"\/api\/v1\/knowledge\/prerequisites":{"get":{"tags":["Knowledge — Public"],"summary":"Qué saber antes de un tema\/skill\/OA, derivado del grafo real de prerrequisitos.","responses":{"200":{"description":"Prerrequisitos."}},"requestBody":{"required":true,"content":{"application\/json":{"schema":[{"name":"resource","in":"query","required":false,"description":"Slug o código.","schema":{"type":"string"}},{"name":"q","in":"query","required":false,"description":"Tema si no hay referencia concreta.","schema":{"type":"string"}}]}}},"security":[]}},"\/api\/v1\/knowledge\/labs":{"get":{"tags":["Knowledge — Public"],"summary":"Laboratorios públicos por concepto\/materia\/grado\/edad, con acción practice y canonical_url.","responses":{"200":{"description":"Lista de labs."}},"requestBody":{"required":true,"content":{"application\/json":{"schema":[{"name":"q","in":"query","required":false,"description":"Concepto.","schema":{"type":"string","example":"ecuaciones"}},{"name":"age","in":"query","required":false,"description":"Edad.","schema":{"type":"string"}}]}}},"security":[]}},"\/api\/v1\/knowledge\/paths":{"get":{"tags":["Knowledge — Public"],"summary":"Ruta de aprendizaje para un objetivo: ruta editorial si existe; si no, secuencia derivada del grafo marcada generated_from_graph:true (nunca se presenta como oficial).","responses":{"200":{"description":"Pasos con origen declarado."}},"requestBody":{"required":true,"content":{"application\/json":{"schema":[{"name":"goal","in":"query","required":false,"description":"Objetivo de aprendizaje.","schema":{"type":"string","example":"aprender fracciones"}}]}}},"security":[]}},"\/api\/v1\/knowledge\/curriculum":{"get":{"tags":["Knowledge — Public"],"summary":"Evidencia curricular REAL almacenada (SEP México y más): sólo afirma alineaciones con verificación; ausencia = no verificada, jamás inventada.","responses":{"200":{"description":"Alineaciones con strength verificado."}},"requestBody":{"required":true,"content":{"application\/json":{"schema":[{"name":"q","in":"query","required":false,"description":"Tema o requisito.","schema":{"type":"string"}},{"name":"oa","in":"query","required":false,"description":"Slug del OA.","schema":{"type":"string"}},{"name":"requirement_code","in":"query","required":false,"description":"Código oficial.","schema":{"type":"string","example":"MX-PRI-G1-LEN-P01"}}]}}},"security":[]}},"\/api\/v1\/objects":{"post":{"tags":["Objetos"],"summary":"Crea (o actualiza por slug) un objeto de aprendizaje. Con draft:true se guarda como borrador y devuelve preview_url.","responses":{"201":{"description":"Objeto creado.","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/CreateObjectResponse"}}}}},"requestBody":{"required":true,"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/CreateObjectRequest"}}}}},"get":{"tags":["Objetos"],"summary":"Lista los objetos de la cuenta dueña de la clave (?q= texto; el personal puede usar ?all=1).","responses":{"200":{"description":"Lista de objetos.","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/ObjectList"}}}}}}},"\/api\/v1\/objects\/validate":{"post":{"tags":["Objetos"],"summary":"Valida un documento (y sus assets declarados) SIN publicarlo. Devuelve valid + errors[].","responses":{"200":{"description":"Resultado de la validación.","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/ValidateResponse"}}}}},"requestBody":{"required":true,"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/ValidateRequest"}}}}}},"\/api\/v1\/objects\/{slug}":{"get":{"tags":["Objetos"],"summary":"Lee el documento publicado de un objeto (?assets=1 añade el base64 de cada recurso y de la portada en cover_b64).","responses":{"200":{"description":"Objeto leído.","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/ObjectDetail"}}}}},"requestBody":{"required":true,"content":{"application\/json":{"schema":[{"name":"slug","in":"path","required":true,"description":"Slug del objeto.","schema":{"type":"string","example":"mi-objeto"}},{"name":"assets","in":"query","required":false,"description":"Con 1 añade assets_b64 y cover_b64.","schema":{"type":"string"}}]}}}}},"\/api\/v1\/objects\/{slug}\/patch":{"post":{"tags":["Objetos"],"summary":"Edición parcial sin reescribir el documento: aplica ops ordenadas sobre el payload actual, revalida y republica una versión nueva (los assets existentes se reempaquetan solos). Con \"cover\" y \"ops\" vacío solo se cambia la portada.","responses":{"201":{"description":"Patch publicado.","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/PatchObjectResponse"}}}}},"requestBody":{"required":true,"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/PatchObjectRequest"}}}}}},"\/api\/v1\/objects\/{slug}\/finalize":{"post":{"tags":["Objetos"],"summary":"Finaliza una solicitud de creación hecha con una AUTORIZACIÓN TEMPORAL (\/crear-objeto, credencial Bearer de un solo uso). Verifica que el objeto existe, pertenece al solicitante, superó la validación y lleva la visibilidad autorizada; lo asigna a los grupos que el docente eligió (para que llegue a los alumnos inscritos) y encola el correo al dueño. Body: {}. Idempotente y CIERRA la credencial.","responses":{"200":{"description":"Solicitud finalizada: objeto verificado, grupos asignados y correo encolado."}}}},"\/api\/v1\/books\/schema":{"get":{"tags":["Libros"],"summary":"Esquema machine-readable del contrato de libros (kinds, campos, formatos de archivo, límites, ejemplos).","responses":{"200":{"description":"Esquema de libros."}},"security":[]}},"\/api\/v1\/books":{"post":{"tags":["Libros"],"summary":"Crea (o actualiza por slug) un LIBRO de lectura: lectura corta (kind=short), libro largo de texto (kind=book) o cómic\/PDF de lectura directa (kind=comic). El contenido va en \"text\", \"chapters\" o \"file\" (txt\/pdf\/docx\/epub en base64). Reutiliza el mismo pipeline del Estudio (importers, normalizador, control de calidad y guardas de derechos).","responses":{"201":{"description":"Libro creado o actualizado.","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/BookResult"}}}}},"requestBody":{"required":true,"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/CreateBookRequest"}}}}},"get":{"tags":["Libros"],"summary":"Lista los libros de la cuenta dueña de la clave (?q= texto; el personal puede usar ?all=1).","responses":{"200":{"description":"Lista de libros.","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/BookList"}}}}},"requestBody":{"required":true,"content":{"application\/json":{"schema":[{"name":"q","in":"query","required":false,"description":"Filtro por título o slug.","schema":{"type":"string"}},{"name":"all","in":"query","required":false,"description":"1 = todos (solo personal).","schema":{"type":"string"}}]}}}}},"\/api\/v1\/books\/validate":{"post":{"tags":["Libros"],"summary":"Corre el control de calidad (extracción + ImportQuality) sobre \"text\"\/\"chapters\"\/\"file\" SIN guardar nada. Devuelve el veredicto green\/yellow\/red, palabras\/capítulos detectados y el motivo de rechazo si lo hay.","responses":{"200":{"description":"Resultado de la validación.","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/ValidateBookResponse"}}}}},"requestBody":{"required":true,"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/CreateBookRequest"}}}}}},"\/api\/v1\/books\/{slug}":{"get":{"tags":["Libros"],"summary":"Lee la ficha de un libro (metadatos + portada). Con ?content=1 incluye los capítulos con su texto; con ?cover=1 añade \"cover_b64\". Sólo dueño o personal.","responses":{"200":{"description":"Libro leído.","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/BookDetail"}}}}},"requestBody":{"required":true,"content":{"application\/json":{"schema":[{"name":"slug","in":"path","required":true,"description":"Slug del libro.","schema":{"type":"string","example":"mi-lectura"}},{"name":"content","in":"query","required":false,"description":"1 = incluye capítulos.","schema":{"type":"string"}},{"name":"cover","in":"query","required":false,"description":"1 = portada en base64.","schema":{"type":"string"}}]}}}},"post":{"tags":["Libros"],"summary":"Edita un libro existente: metadatos, visibilidad, portada, taxonomía y (si es de texto) reemplazo del contenido. No reemplaza el DOCUMENTO subido (PDF\/EPUB\/DOCX).","responses":{"200":{"description":"Libro actualizado.","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/BookResult"}}}}},"requestBody":{"required":true,"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/CreateBookRequest"}}}}}},"\/api\/v1\/structure\/categories":{"get":{"tags":["Estructura"],"summary":"Lista las categorías que la cuenta puede administrar.","responses":{"200":{"description":"Lista de categorías.","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/StructureList"}}}}}},"post":{"tags":["Estructura"],"summary":"Crea o localiza (idempotente por slug) una categoría.","responses":{"201":{"description":"Categoría creada o localizada."}},"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","description":"Categoría: name, slug?, description?, parent?, privacy?.","additionalProperties":true}}}}}},"\/api\/v1\/structure\/courses":{"get":{"tags":["Estructura"],"summary":"Lista los cursos que la cuenta puede administrar.","responses":{"200":{"description":"Lista de cursos.","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/StructureList"}}}}}},"post":{"tags":["Estructura"],"summary":"Crea o localiza un curso (opcionalmente bajo una categoría).","responses":{"201":{"description":"Curso creado o localizado."}},"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","description":"Curso: name, slug?, description?, category?, privacy?.","additionalProperties":true}}}}}},"\/api\/v1\/structure\/routes":{"get":{"tags":["Estructura"],"summary":"Lista las rutas (colecciones) que la cuenta puede administrar.","responses":{"200":{"description":"Lista de rutas.","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/StructureList"}}}}}},"post":{"tags":["Estructura"],"summary":"Crea o localiza una ruta (opcionalmente asignada a un curso).","responses":{"201":{"description":"Ruta creada o localizada."}},"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","description":"Ruta: name, slug?, objective?, course?, stages?, visibility?, difficulty?.","additionalProperties":true}}}}}},"\/api\/v1\/structure\/routes\/{slug}\/items":{"post":{"tags":["Estructura"],"summary":"Coloca un objeto o libro en una ruta.","responses":{"201":{"description":"Elemento colocado."}},"requestBody":{"required":true,"content":{"application\/json":{"schema":[{"name":"slug","in":"path","required":true,"description":"Slug de la ruta.","schema":{"type":"string"}}]}}}}},"\/api\/v1\/structure\/courses\/{slug}\/routes":{"post":{"tags":["Estructura"],"summary":"Asigna una ruta existente a un curso.","responses":{"200":{"description":"Ruta asignada."}},"requestBody":{"required":true,"content":{"application\/json":{"schema":[{"name":"slug","in":"path","required":true,"description":"Slug del curso.","schema":{"type":"string"}}]}}}}},"\/api\/v1\/groups\/schema":{"get":{"tags":["Descubrimiento"],"summary":"Esquema machine-readable de la API de cura de grupos (análisis y apply masivo).","responses":{"200":{"description":"Esquema de grupos."}},"security":[]}},"\/api\/v1\/groups\/{scope}\/{ref}\/analysis":{"get":{"tags":["Cura de grupos"],"summary":"Analiza UN GRUPO completo (scope = course|category|route, ref = slug o id): huecos de contenido de cada objeto y, con check_media=1, salud de sus medios (videos oembed rotos, assets ausentes en disco, portada caída). Devuelve summary + lista paginada por objeto.","responses":{"200":{"description":"Diagnóstico del grupo."}},"requestBody":{"required":true,"content":{"application\/json":{"schema":[{"name":"scope","in":"path","required":true,"description":"course | category | route.","schema":{"type":"string","example":"course"}},{"name":"ref","in":"path","required":true,"description":"Slug o id del grupo.","schema":{"type":"string","example":"matematicas-primaria"}},{"name":"aspects","in":"query","required":false,"description":"csv de aspectos a auditar.","schema":{"type":"string"}},{"name":"check_media","in":"query","required":false,"description":"1 = re-valida medios (caro).","schema":{"type":"string"}},{"name":"subject","in":"query","required":false,"description":"Filtro por materia.","schema":{"type":"string"}},{"name":"q","in":"query","required":false,"description":"Filtro por texto.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Objetos por página (≤100).","schema":{"type":"string"}},{"name":"page","in":"query","required":false,"description":"Página.","schema":{"type":"string"}}]}}}}},"\/api\/v1\/groups\/{scope}\/{ref}\/apply":{"post":{"tags":["Cura de grupos"],"summary":"Aplica una TANDA de ops (las mismas que \/objects\/{slug}\/patch) a todos los objetos del grupo que pasen el filtro (match.missing \/ match.slugs), uno a uno y aislados; cada objeto modificado publica una versión nueva. dry_run:true previsualiza sin publicar.","responses":{"207":{"description":"Resultado de la tanda (mezcla de éxitos y fallos por objeto)."}},"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","description":"ops[], match{missing|slugs}, exclude_slugs[], dry_run, max.","additionalProperties":true}}}}}},"\/api\/v1\/media\/video":{"get":{"tags":["Medios"],"summary":"Busca y valida videos de YouTube POR el agente (?q=) o verifica un id concreto (?id=&provider=). Sólo se ofrecen candidatos VIVOS y embebibles (oembed oficial).","responses":{"200":{"description":"Candidatos vivos o verificación."}},"requestBody":{"required":true,"content":{"application\/json":{"schema":[{"name":"q","in":"query","required":false,"description":"Texto de búsqueda.","schema":{"type":"string"}},{"name":"id","in":"query","required":false,"description":"Id de video a verificar.","schema":{"type":"string"}},{"name":"provider","in":"query","required":false,"description":"youtube (por defecto) | vimeo.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Candidatos (1-8).","schema":{"type":"string"}}]}}}}},"\/api\/v1\/media\/image":{"get":{"tags":["Medios"],"summary":"Valida una IMAGEN: ?src=URL|data-URI descarga y confirma que es una imagen viva (mime real, dimensiones, peso, apta como portada); o ?object=slug&src=assets\/… comprueba que un recurso ya publicado existe en disco.","responses":{"200":{"description":"Resultado de la validación."}},"requestBody":{"required":true,"content":{"application\/json":{"schema":[{"name":"src","in":"query","required":false,"description":"URL http(s), data-URI o ruta assets\/….","schema":{"type":"string","example":"https:\/\/example.com\/img.png"}},{"name":"object","in":"query","required":false,"description":"Slug o id del objeto (con src=assets\/…).","schema":{"type":"string"}}]}}}}},"\/api\/v1\/labs":{"get":{"tags":["Laboratorios"],"summary":"Lista los laboratorios publicados (filtros level, subject, grade, difficulty, q).","responses":{"200":{"description":"Lista de laboratorios.","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/LabList"}}}}},"security":[]},"post":{"tags":["Laboratorios"],"summary":"Crea o actualiza (por uid) un laboratorio. Con draft:true se guarda como borrador.","responses":{"201":{"description":"Laboratorio creado o actualizado.","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/CreateLabResponse"}}}}},"requestBody":{"required":true,"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/CreateLabRequest"}}}}}},"\/api\/v1\/labs\/schema":{"get":{"tags":["Laboratorios"],"summary":"Catálogo machine-legible del motor: componentes aprobados (native, motor y primaria), reglas de meta y de actividad.","responses":{"200":{"description":"Esquema del formato de laboratorio."}},"security":[]}},"\/api\/v1\/labs\/validate":{"post":{"tags":["Laboratorios"],"summary":"Valida una definición lumbre-lab\/1.0 SIN almacenarla. Devuelve ok + errors[].","responses":{"200":{"description":"Resultado de la validación."}},"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","description":"Documento: {\"lab\": {format, meta, activities}} o pelado.","additionalProperties":true}}}}}},"\/api\/v1\/labs\/{slug}":{"get":{"tags":["Laboratorios"],"summary":"Lee la definición completa de un laboratorio publicado (meta + activities, con uid y version).","responses":{"200":{"description":"Laboratorio leído."}},"requestBody":{"required":true,"content":{"application\/json":{"schema":[{"name":"slug","in":"path","required":true,"description":"Slug del laboratorio.","schema":{"type":"string","example":"lab-algebra"}}]}}},"security":[]}},"\/api\/lo-results":{"post":{"tags":["Resultados"],"summary":"Guarda el resultado del alumno en un bloque (requiere sesión del estudiante, no clave de agente).","responses":{"200":{"description":"Resultado guardado."}},"requestBody":{"required":true,"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/LoResultRequest"}}}}}},"\/api\/v1\/lab-results":{"post":{"tags":["Resultados"],"summary":"Guarda el intento del estudiante en una actividad de un laboratorio (requiere sesión; progreso local por activity_ref).","responses":{"200":{"description":"Intento guardado."}},"requestBody":{"required":true,"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/LabResultRequest"}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Clave de agente creada en https:\/\/univ.lumbre.lat\/object-studio (panel «API para agentes»). También se acepta la cabecera X-Api-Token."}},"schemas":{"BlockHeading":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/heading.json","title":"Bloque «heading»","description":"Título de sección dentro de la lección.","type":"object","properties":{"type":{"const":"heading","description":"Tipo de bloque (siempre \"heading\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"text":{"type":"string","maxLength":180,"description":"Texto del encabezado."},"level":{"type":"integer","description":"Nivel jerárquico.","minimum":1,"maximum":3}},"required":["type","text"],"additionalProperties":false},"BlockText":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/text.json","title":"Bloque «text»","description":"Bloque de prosa; admite **negrita** y *cursiva* y párrafos separados por línea en blanco.","type":"object","properties":{"type":{"const":"text","description":"Tipo de bloque (siempre \"text\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"text":{"type":"string","maxLength":5000,"description":"Texto del párrafo (o varios, separados por una línea en blanco)."}},"required":["type","text"],"additionalProperties":false},"BlockList":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/list.json","title":"Bloque «list»","description":"Lista de viñetas o numerada.","type":"object","properties":{"type":{"const":"list","description":"Tipo de bloque (siempre \"list\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"ordered":{"type":"boolean","description":"true = numerada (ol); false u omitido = viñetas (ul)."},"items":{"type":"array","items":{"type":"string","maxLength":500,"description":"Elemento de texto."},"description":"Elementos de la lista.","minItems":1,"maxItems":30}},"required":["type","items"],"additionalProperties":false},"BlockCallout":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/callout.json","title":"Bloque «callout»","description":"Recuadro que destaca una idea clave, un consejo o una advertencia.","type":"object","properties":{"type":{"const":"callout","description":"Tipo de bloque (siempre \"callout\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"tone":{"type":"string","enum":["info","tip","warning","important"],"description":"Tono del recuadro."},"title":{"type":"string","maxLength":120,"description":"Título corto opcional."},"text":{"type":"string","maxLength":1500,"description":"Texto del recuadro."}},"required":["type","text"],"additionalProperties":false},"BlockQuote":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/quote.json","title":"Bloque «quote»","description":"Cita textual con su atribución opcional.","type":"object","properties":{"type":{"const":"quote","description":"Tipo de bloque (siempre \"quote\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"text":{"type":"string","maxLength":1500,"description":"Texto citado."},"cite":{"type":"string","maxLength":200,"description":"Autoría o procedencia de la cita."}},"required":["type","text"],"additionalProperties":false},"BlockCode":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/code.json","title":"Bloque «code»","description":"Bloque de código fuente con resaltado de sintaxis, números de línea y botón de copiar. Presenta ejemplos de programación de forma profesional; NO ejecuta nada.","type":"object","properties":{"type":{"const":"code","description":"Tipo de bloque (siempre \"code\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"language":{"type":"string","enum":["python","javascript","typescript","php","sql","html","css","json","java","cpp","csharp","bash","markdown","texto"],"description":"Lenguaje del fragmento; controla el resaltado de sintaxis. Use «texto» para contenido plano."},"title":{"type":"string","maxLength":180,"description":"Título opcional del bloque (encabezado sobre la ventana de código)."},"filename":{"type":"string","maxLength":120,"description":"Nombre de archivo opcional mostrado en la cabecera (p. ej. «hola.py»)."},"code":{"type":"string","maxLength":8000,"description":"Código fuente literal (≤8000 caracteres y ≤120 líneas). El texto se escapa al renderizar; nunca se ejecuta."},"highlight_lines":{"type":"array","items":{"type":"integer","description":"Número de línea (base 1).","minimum":1,"maximum":120},"description":"Líneas que se destacan visualmente (p. ej. el bug o la clave).","minItems":1,"maxItems":60},"caption":{"type":"string","maxLength":500,"description":"Pie explicativo opcional bajo el bloque."}},"required":["type","language","code"],"additionalProperties":false},"BlockImage":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/image.json","title":"Bloque «image»","description":"Imagen suelta con texto alternativo.","type":"object","properties":{"type":{"const":"image","description":"Tipo de bloque (siempre \"image\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"src":{"type":"string","pattern":"^assets\/[A-Za-z0-9._\/-]+$","description":"Imagen. Ruta relativa dentro del paquete (assets\/…); el archivo debe ir incluido en el ZIP."},"alt":{"type":"string","maxLength":300,"description":"Texto alternativo (accesibilidad)."},"caption":{"type":"string","maxLength":500,"description":"Pie de imagen opcional."},"width":{"type":"integer","description":"Ancho relativo (1 = angosto … 5 = completo).","minimum":1,"maximum":5}},"required":["type","src","alt"],"additionalProperties":false},"BlockFigure":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/figure.json","title":"Bloque «figure»","description":"Imagen con pie de imagen obligatorio (diagrama, esquema, mapa).","type":"object","properties":{"type":{"const":"figure","description":"Tipo de bloque (siempre \"figure\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"src":{"type":"string","pattern":"^assets\/[A-Za-z0-9._\/-]+$","description":"Imagen de la figura. Ruta relativa dentro del paquete (assets\/…); el archivo debe ir incluido en el ZIP."},"alt":{"type":"string","maxLength":300,"description":"Texto alternativo (accesibilidad)."},"caption":{"type":"string","maxLength":500,"description":"Pie de figura (obligatorio)."}},"required":["type","src","alt","caption"],"additionalProperties":false},"BlockAudio":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/audio.json","title":"Bloque «audio»","description":"Reproductor de audio incluido en el paquete.","type":"object","properties":{"type":{"const":"audio","description":"Tipo de bloque (siempre \"audio\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"src":{"type":"string","pattern":"^assets\/[A-Za-z0-9._\/-]+$","description":"Archivo de audio. Ruta relativa dentro del paquete (assets\/…); el archivo debe ir incluido en el ZIP."},"caption":{"type":"string","maxLength":500,"description":"Descripción opcional."}},"required":["type","src"],"additionalProperties":false},"BlockVideo":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/video.json","title":"Bloque «video»","description":"Video incluido en el paquete (archivo propio).","type":"object","properties":{"type":{"const":"video","description":"Tipo de bloque (siempre \"video\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"src":{"type":"string","pattern":"^assets\/[A-Za-z0-9._\/-]+$","description":"Archivo de video. Ruta relativa dentro del paquete (assets\/…); el archivo debe ir incluido en el ZIP."},"poster":{"type":"string","pattern":"^assets\/[A-Za-z0-9._\/-]+$","description":"Imagen de portada opcional. Ruta relativa dentro del paquete (assets\/…); el archivo debe ir incluido en el ZIP."},"caption":{"type":"string","maxLength":500,"description":"Descripción opcional."}},"required":["type","src"],"additionalProperties":false},"BlockEmbed":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/embed.json","title":"Bloque «embed»","description":"Video en línea de un proveedor en lista blanca; la plataforma arma el reproductor sin subir archivos.","type":"object","properties":{"type":{"const":"embed","description":"Tipo de bloque (siempre \"embed\")."},"id":{"type":"string","pattern":"^[A-Za-z0-9_-]{4,60}$","description":"Identificador del video (lo que va tras \"v=\" o \"youtu.be\/\")."},"provider":{"type":"string","enum":["youtube","vimeo"],"description":"Proveedor del video."},"title":{"type":"string","maxLength":180,"description":"Título opcional."},"caption":{"type":"string","maxLength":500,"description":"Pie opcional."}},"required":["type","provider","id"],"additionalProperties":false},"BlockLink":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/link.json","title":"Bloque «link»","description":"Enlace externo a otra página o recurso.","type":"object","properties":{"type":{"const":"link","description":"Tipo de bloque (siempre \"link\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"url":{"type":"string","format":"uri","pattern":"^https?:\/\/","maxLength":500,"description":"Destino del enlace."},"label":{"type":"string","maxLength":180,"description":"Texto visible del enlace."},"note":{"type":"string","maxLength":300,"description":"Nota aclaratoria opcional."}},"required":["type","url","label"],"additionalProperties":false},"BlockTable":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/table.json","title":"Bloque «table»","description":"Tabla de datos; cada fila debe tener tantas celdas como columnas.","type":"object","properties":{"type":{"const":"table","description":"Tipo de bloque (siempre \"table\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"caption":{"type":"string","maxLength":500,"description":"Título de la tabla."},"columns":{"type":"array","items":{"type":"string","maxLength":120,"description":"Elemento de texto."},"description":"Nombres de columna.","minItems":1,"maxItems":12},"rows":{"type":"array","items":{"type":"array","items":{"type":"string","maxLength":300,"description":"Celda."},"description":"Fila.","minItems":1,"maxItems":12},"description":"Filas de la tabla.","minItems":1,"maxItems":100}},"required":["type","columns","rows"],"additionalProperties":false},"BlockChart":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/chart.json","title":"Bloque «chart»","description":"Gráfico estadístico dibujado por el runtime (SVG, sin librerías).","type":"object","properties":{"type":{"const":"chart","description":"Tipo de bloque (siempre \"chart\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"chart":{"type":"string","enum":["bar","hbar","line","area","pie","donut","radar"],"description":"Tipo de gráfico."},"title":{"type":"string","maxLength":180,"description":"Título del gráfico."},"unit":{"type":"string","maxLength":24,"description":"Unidad de los valores (p. ej. \"km\", \"%\")."},"x_label":{"type":"string","maxLength":80,"description":"Etiqueta del eje X."},"y_label":{"type":"string","maxLength":80,"description":"Etiqueta del eje Y."},"source":{"type":"string","maxLength":300,"description":"Fuente de los datos."},"labels":{"type":"array","items":{"type":"string","maxLength":80,"description":"Elemento de texto."},"description":"Etiquetas de cada categoría\/punto.","minItems":1,"maxItems":60},"series":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","maxLength":80,"description":"Nombre de la serie."},"values":{"type":"array","items":{"type":"number"},"description":"Valores numéricos.","minItems":1,"maxItems":60}},"required":["values"],"additionalProperties":false,"description":"Una serie de datos."},"description":"Series del gráfico.","minItems":1,"maxItems":8}},"required":["type","chart","labels","series"],"additionalProperties":false},"BlockMindmap":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/mindmap.json","title":"Bloque «mindmap»","description":"Mapa mental radial. Acepta Markdown (md) o la pareja center+branches.","type":"object","properties":{"type":{"const":"mindmap","description":"Tipo de bloque (siempre \"mindmap\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"title":{"type":"string","maxLength":180,"description":"Título del mapa."},"md":{"type":"string","description":"Markdown del mapa: al menos un encabezado (# título); los niveles # \/ ## \/ ### son las ramas."},"center":{"oneOf":[{"type":"string","maxLength":48,"description":"Etiqueta del nodo central."},{"type":"object","properties":{"label":{"type":"string","maxLength":48,"description":"Etiqueta del nodo central."}},"required":["label"],"additionalProperties":false,"description":"Nodo central."}],"description":"Nodo central: {label} o el texto directamente."},"branches":{"type":"array","items":{"type":"object","description":"Rama {label, children?}.","properties":{"label":{"type":"string","maxLength":48,"description":"Etiqueta de la rama."},"children":{"type":"array","description":"Subramas (máx. 3 niveles)."}},"required":["label"]},"description":"Ramas del mapa.","minItems":2,"maxItems":9}},"required":["type"],"additionalProperties":false},"BlockGraph":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/graph.json","title":"Bloque «graph»","description":"Graficador de funciones en el plano cartesiano; el runtime evalúa las expresiones con su mini-cálculadora.","type":"object","properties":{"type":{"const":"graph","description":"Tipo de bloque (siempre \"graph\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"title":{"type":"string","maxLength":180,"description":"Título del gráfico."},"x_label":{"type":"string","maxLength":40,"description":"Etiqueta del eje X."},"y_label":{"type":"string","maxLength":40,"description":"Etiqueta del eje Y."},"x_range":{"type":"array","items":{"type":"number"},"minItems":2,"maxItems":2,"description":"Rango visible del eje X [min, max]."},"y_range":{"type":"array","items":{"type":"number"},"minItems":2,"maxItems":2,"description":"Rango visible del eje Y [min, max]."},"curves":{"type":"array","items":{"type":"object","properties":{"expr":{"type":"string","maxLength":120,"description":"Expresión en x (p. ej. \"sin(x)*x\")."},"label":{"type":"string","maxLength":40,"description":"Etiqueta de la curva."}},"required":["expr"],"additionalProperties":false,"description":"Una curva."},"description":"Curvas a dibujar.","minItems":1,"maxItems":4}},"required":["type","curves"],"additionalProperties":false},"BlockFormula":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/formula.json","title":"Bloque «formula»","description":"Fórmula matemática como árbol inerte que el renderer traduce a MathML nativo.","type":"object","properties":{"type":{"const":"formula","description":"Tipo de bloque (siempre \"formula\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"title":{"type":"string","maxLength":180,"description":"Título de la fórmula."},"math":{"oneOf":[{"type":"string"},{"type":"object"},{"type":"array"}],"description":"Árbol inerte de fórmulas. Nodo = \"cadena\" (sólo glifos, sin llaves\/ángulos\/comillas) | {row:[…]} | {frac:{num,den}} | {sub:{base,idx}} | {sup:{base,exp}} | {sqrt:…} | {op:{name: sum|prod|int|lim|coprod|bigcup|bigcap, from?, to?, body}} | {txt:\"palabra\"}. Máx. 200 nodos \/ profundidad 8. El renderer lo traduce a MathML nativo."},"caption":{"type":"string","maxLength":500,"description":"Pie de fórmula opcional."},"display":{"type":"boolean","description":"true = fórmula en línea aparte (display); false u omitido = en línea."}},"required":["type","math"],"additionalProperties":false},"BlockMcq":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/mcq.json","title":"Bloque «mcq»","description":"Pregunta de opción múltiple con una sola respuesta correcta.","type":"object","properties":{"type":{"const":"mcq","description":"Tipo de bloque (siempre \"mcq\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"prompt":{"type":"string","maxLength":600,"description":"Enunciado de la pregunta."},"options":{"type":"array","items":{"type":"string","maxLength":300,"description":"Elemento de texto."},"description":"Opciones (2–6); la correcta se indica con correct_index.","minItems":2,"maxItems":6},"correct_index":{"type":"integer","description":"Índice (base 0) de la opción correcta.","minimum":0,"maximum":5},"explanation":{"type":"string","maxLength":1000,"description":"Retroalimentación breve que se revela al responder."},"evidence":{"type":"string","maxLength":1000,"description":"Cita textual tomada del propio objeto que justifica la respuesta correcta. OBLIGATORIA en todo bloque puntuado."},"level":{"type":"string","enum":["comprension","inferencia","aplicacion"],"description":"Nivel cognitivo: comprension, inferencia o aplicacion."}},"required":["type","prompt","options","correct_index"],"additionalProperties":false},"BlockTrueFalse":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/true_false.json","title":"Bloque «true_false»","description":"Afirmación que el alumno marca como verdadera o falsa.","type":"object","properties":{"type":{"const":"true_false","description":"Tipo de bloque (siempre \"true_false\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"prompt":{"type":"string","maxLength":600,"description":"Afirmación a juzgar."},"answer":{"type":"boolean","description":"true si la afirmación es correcta; false si no lo es."},"explanation":{"type":"string","maxLength":1000,"description":"Retroalimentación breve que se revela al responder."},"evidence":{"type":"string","maxLength":1000,"description":"Cita textual tomada del propio objeto que justifica la respuesta correcta. OBLIGATORIA en todo bloque puntuado."},"level":{"type":"string","enum":["comprension","inferencia","aplicacion"],"description":"Nivel cognitivo: comprension, inferencia o aplicacion."}},"required":["type","prompt","answer"],"additionalProperties":false},"BlockFillBlank":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/fill_blank.json","title":"Bloque «fill_blank»","description":"Texto con huecos «___» que el alumno completa; cada hueco tiene su respuesta.","type":"object","properties":{"type":{"const":"fill_blank","description":"Tipo de bloque (siempre \"fill_blank\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"text":{"type":"string","maxLength":2000,"description":"Texto con uno o más huecos marcados con «___»."},"answers":{"type":"array","items":{"type":"string","maxLength":60,"description":"Elemento de texto."},"description":"Respuestas válidas, en el orden de los huecos.","minItems":1,"maxItems":10},"explanation":{"type":"string","maxLength":1000,"description":"Retroalimentación breve que se revela al responder."},"evidence":{"type":"string","maxLength":1000,"description":"Cita textual tomada del propio objeto que justifica la respuesta correcta. OBLIGATORIA en todo bloque puntuado."},"level":{"type":"string","enum":["comprension","inferencia","aplicacion"],"description":"Nivel cognitivo: comprension, inferencia o aplicacion."}},"required":["type","text","answers"],"additionalProperties":false},"BlockMatch":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/match.json","title":"Bloque «match»","description":"Emparejar elementos de dos columnas; cada par left↔right es correcto.","type":"object","properties":{"type":{"const":"match","description":"Tipo de bloque (siempre \"match\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"prompt":{"type":"string","maxLength":600,"description":"Instrucción opcional del emparejamiento."},"pairs":{"type":"array","items":{"type":"object","properties":{"left":{"type":"string","maxLength":200,"description":"Elemento de la columna izquierda."},"right":{"type":"string","maxLength":200,"description":"Elemento de la columna derecha."}},"required":["left","right"],"additionalProperties":false,"description":"Un par correcto."},"description":"Pares correctos (2–12).","minItems":2,"maxItems":12},"evidence":{"type":"string","maxLength":1000,"description":"Cita textual tomada del propio objeto que justifica la respuesta correcta. OBLIGATORIA en todo bloque puntuado."},"level":{"type":"string","enum":["comprension","inferencia","aplicacion"],"description":"Nivel cognitivo: comprension, inferencia o aplicacion."}},"required":["type","pairs"],"additionalProperties":false},"BlockSequence":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/sequence.json","title":"Bloque «sequence»","description":"Ordenar: el alumno coloca los elementos en la secuencia correcta (arrastrando o con los botones ↑\/↓). Accesible con teclado.","type":"object","properties":{"type":{"const":"sequence","description":"Tipo de bloque (siempre \"sequence\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"prompt":{"type":"string","maxLength":600,"description":"Instrucción opcional."},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","maxLength":40,"description":"Identificador único del elemento (se referencia en correct_order)."},"text":{"type":"string","maxLength":200,"description":"Texto del elemento."}},"required":["id","text"],"additionalProperties":false,"description":"Un elemento a ordenar."},"description":"Elementos (2–20).","minItems":2,"maxItems":20},"correct_order":{"type":"array","items":{"type":"string","maxLength":40,"description":"Id de un elemento (de items)."},"description":"Orden correcto: permutación exacta de los id de items.","minItems":2,"maxItems":20},"explanation":{"type":"string","maxLength":1000,"description":"Retroalimentación breve que se revela al responder."},"evidence":{"type":"string","maxLength":1000,"description":"Cita textual tomada del propio objeto que justifica la respuesta correcta. OBLIGATORIA en todo bloque puntuado."},"level":{"type":"string","enum":["comprension","inferencia","aplicacion"],"description":"Nivel cognitivo: comprension, inferencia o aplicacion."}},"required":["type","items","correct_order"],"additionalProperties":false},"BlockNumericAnswer":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/numeric_answer.json","title":"Bloque «numeric_answer»","description":"Respuesta numérica con tolerancia (absoluta o porcentual) y unidad opcional.","type":"object","properties":{"type":{"const":"numeric_answer","description":"Tipo de bloque (siempre \"numeric_answer\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"prompt":{"type":"string","maxLength":600,"description":"Enunciado de la pregunta."},"answer":{"type":"number","description":"Valor numérico correcto."},"tolerance":{"type":"number","description":"Margen absoluto aceptado (≥0). P. ej. 0.5 admite ±0.5."},"tolerance_percent":{"type":"number","description":"Margen porcentual sobre «answer» (≥0). P. ej. 5 admite ±5 %."},"unit":{"type":"string","maxLength":24,"description":"Unidad esperada (p. ej. \"km\", \"°C\")."},"unit_optional":{"type":"boolean","description":"true = la unidad no se evalúa (sólo el número); false u omitido = si hay «unit», también se comprueba."},"min":{"type":"number","description":"Límite inferior aceptado en la entrada (debe ser menor que «max»)."},"max":{"type":"number","description":"Límite superior aceptado en la entrada (debe ser mayor que «min»)."},"explanation":{"type":"string","maxLength":1000,"description":"Retroalimentación breve que se revela al responder."},"evidence":{"type":"string","maxLength":1000,"description":"Cita textual tomada del propio objeto que justifica la respuesta correcta. OBLIGATORIA en todo bloque puntuado."},"level":{"type":"string","enum":["comprension","inferencia","aplicacion"],"description":"Nivel cognitivo: comprension, inferencia o aplicacion."}},"required":["type","prompt","answer"],"additionalProperties":false},"BlockErrorDetective":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/error_detective.json","title":"Bloque «error_detective»","description":"Detectar el error: entre varias líneas o pasos, el alumno marca cuál(es) contienen un error.","type":"object","properties":{"type":{"const":"error_detective","description":"Tipo de bloque (siempre \"error_detective\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"prompt":{"type":"string","maxLength":600,"description":"Instrucción opcional."},"lines":{"type":"array","items":{"type":"object","properties":{"text":{"type":"string","maxLength":1000,"description":"Texto de la línea o paso."}},"required":["text"],"additionalProperties":false,"description":"Una línea o paso."},"description":"Líneas\/pasos entre los que hay que detectar el error (2–30).","minItems":2,"maxItems":30},"answer":{"oneOf":[{"type":"integer","description":"Índice (base 0) de la única línea errónea.","minimum":0,"maximum":29},{"type":"array","items":{"type":"integer","description":"Índice (base 0) de una línea errónea.","minimum":0,"maximum":29},"description":"Índices de las líneas erróneas (1–30).","minItems":1,"maxItems":30}],"description":"Índice (entero) o lista de índices (base 0) de las líneas\/pasos erróneos."},"explanation":{"type":"string","maxLength":1000,"description":"Retroalimentación breve que se revela al responder."},"evidence":{"type":"string","maxLength":1000,"description":"Cita textual tomada del propio objeto que justifica la respuesta correcta. OBLIGATORIA en todo bloque puntuado."},"level":{"type":"string","enum":["comprension","inferencia","aplicacion"],"description":"Nivel cognitivo: comprension, inferencia o aplicacion."}},"required":["type","lines","answer"],"additionalProperties":false},"BlockConfidence":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/confidence.json","title":"Bloque «confidence»","description":"Opción múltiple en la que el alumno declara además su nivel de confianza (escala fija de 4 niveles: nada, poco, bastante y muy seguro).","type":"object","properties":{"type":{"const":"confidence","description":"Tipo de bloque (siempre \"confidence\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"prompt":{"type":"string","maxLength":600,"description":"Enunciado de la pregunta."},"options":{"type":"array","items":{"type":"string","maxLength":300,"description":"Elemento de texto."},"description":"Opciones (2–6); la correcta se indica con correct_index.","minItems":2,"maxItems":6},"correct_index":{"type":"integer","description":"Índice (base 0) de la opción correcta.","minimum":0,"maximum":5},"explanation":{"type":"string","maxLength":1000,"description":"Retroalimentación breve que se revela al responder."},"evidence":{"type":"string","maxLength":1000,"description":"Cita textual tomada del propio objeto que justifica la respuesta correcta. OBLIGATORIA en todo bloque puntuado."},"level":{"type":"string","enum":["comprension","inferencia","aplicacion"],"description":"Nivel cognitivo: comprension, inferencia o aplicacion."}},"required":["type","prompt","options","correct_index"],"additionalProperties":false},"BlockPrediction":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/prediction.json","title":"Bloque «prediction»","description":"El alumno predice el resultado de un fenómeno o experimento; al responder se revela lo que realmente ocurre (texto y\/o imagen), con una reflexión opcional.","type":"object","properties":{"type":{"const":"prediction","description":"Tipo de bloque (siempre \"prediction\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"prompt":{"type":"string","maxLength":600,"description":"Enunciado: qué se le pide predecir al alumno."},"options":{"type":"array","items":{"type":"string","maxLength":300,"description":"Elemento de texto."},"description":"Resultados posibles (2–6); el real se indica con correct_index.","minItems":2,"maxItems":6},"correct_index":{"type":"integer","description":"Índice (base 0) del resultado real.","minimum":0,"maximum":5},"reveal":{"type":"object","properties":{"text":{"type":"string","maxLength":2000,"description":"Texto que revela el resultado real."},"image":{"type":"string","pattern":"^assets\/[A-Za-z0-9._\/-]+$","description":"Imagen opcional del resultado. Ruta relativa dentro del paquete (assets\/…); el archivo debe ir incluido en el ZIP."},"explanation":{"type":"string","maxLength":1000,"description":"Explicación opcional de por qué ocurre."}},"required":[],"additionalProperties":false,"description":"Lo que se muestra tras responder: el resultado real (texto y\/o imagen) y su explicación."},"reflection":{"type":"string","maxLength":600,"description":"Pregunta de reflexión opcional tras la revelación."},"evidence":{"type":"string","maxLength":1000,"description":"Cita textual tomada del propio objeto que justifica la respuesta correcta. OBLIGATORIA en todo bloque puntuado."},"level":{"type":"string","enum":["comprension","inferencia","aplicacion"],"description":"Nivel cognitivo: comprension, inferencia o aplicacion."}},"required":["type","prompt","options","correct_index","reveal"],"additionalProperties":false},"BlockTextHighlight":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/text_highlight.json","title":"Bloque «text_highlight»","description":"Resaltar: el alumno toca las palabras o frases del texto que cumplen un criterio, opcionalmente agrupándolas por categorías.","type":"object","properties":{"type":{"const":"text_highlight","description":"Tipo de bloque (siempre \"text_highlight\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"prompt":{"type":"string","maxLength":600,"description":"Instrucción: qué hay que resaltar."},"text":{"type":"string","maxLength":5000,"description":"Texto sobre el que se marca."},"unit":{"type":"string","enum":["word","sentence"],"description":"Unidad que se marca: \"word\" (palabras) o \"sentence\" (frases)."},"answer":{"oneOf":[{"type":"array","items":{"type":"integer","description":"Índice (base 0) de una unidad.","minimum":0,"maximum":2000},"description":"Unidades correctas (forma simple, sin categorías).","minItems":1,"maxItems":500},{"type":"array","items":{"type":"object","properties":{"category":{"type":"string","maxLength":80,"description":"Nombre de la categoría (de «categories»)."},"matches":{"type":"array","items":{"type":"integer","description":"Índice (base 0).","minimum":0,"maximum":2000},"description":"Unidades de esa categoría.","minItems":1,"maxItems":500}},"required":["category","matches"],"additionalProperties":false,"description":"Un grupo por categoría."},"description":"Marcado por categorías (usa «categories»).","minItems":1,"maxItems":60}],"description":"Respuesta correcta: lista de índices (base 0) o lista de grupos {category, matches}."},"categories":{"type":"array","items":{"type":"string","maxLength":80,"description":"Elemento de texto."},"description":"Nombres de categoría (2–5); obliga a que «answer» use grupos {category,matches}.","minItems":2,"maxItems":5},"evidence":{"type":"string","maxLength":1000,"description":"Cita textual tomada del propio objeto que justifica la respuesta correcta. OBLIGATORIA en todo bloque puntuado."},"level":{"type":"string","enum":["comprension","inferencia","aplicacion"],"description":"Nivel cognitivo: comprension, inferencia o aplicacion."}},"required":["type","text","unit","answer"],"additionalProperties":false},"BlockWordsearch":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/wordsearch.json","title":"Bloque «wordsearch»","description":"Sopa de letras de repaso; el runtime DIBUJA el tablero (genera la rejilla al vuelo en 4 direcciones) y valida las palabras trazadas. Con mode=\"clues\", la leyenda muestra pistas\/definiciones y la palabra se revela al encontrarla.","type":"object","properties":{"type":{"const":"wordsearch","description":"Tipo de bloque (siempre \"wordsearch\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"title":{"type":"string","maxLength":180,"description":"Título de la sopa."},"instructions":{"type":"string","maxLength":500,"description":"Instrucciones opcionales."},"grid":{"type":"array","items":{"type":"string","pattern":"^[A-ZÑ]+$","description":"Fila de mayúsculas sin tildes (todas del mismo largo, 4–15)."},"description":"Filas del tablero (4–12).","minItems":4,"maxItems":12},"words":{"type":"array","items":{"type":"string","maxLength":40,"description":"Elemento de texto."},"description":"Palabras a encontrar (cada una trazada en línea recta en el grid).","minItems":3,"maxItems":14},"mode":{"type":"string","enum":["words","clues"],"description":"\"words\" (por omisión) muestra las palabras; \"clues\" muestra pistas\/definiciones y revela la palabra al encontrarla."},"clues":{"type":"array","items":{"type":"string","maxLength":200,"description":"Pista o definición de la palabra (misma posición que en words)."},"description":"Pistas, una por cada palabra (úsalo con mode=\"clues\").","minItems":3,"maxItems":14}},"required":["type","grid","words"],"additionalProperties":false},"BlockMemory":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/memory.json","title":"Bloque «memory»","description":"Juego de memoria: emparejar cartas volteándolas de dos en dos.","type":"object","properties":{"type":{"const":"memory","description":"Tipo de bloque (siempre \"memory\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"title":{"type":"string","maxLength":180,"description":"Título del juego."},"instructions":{"type":"string","maxLength":500,"description":"Instrucciones opcionales."},"pairs":{"type":"array","items":{"type":"object","properties":{"a":{"type":"string","maxLength":120,"description":"Cara A."},"b":{"type":"string","maxLength":120,"description":"Cara B."}},"required":["a","b"],"additionalProperties":false,"description":"Un par de cartas."},"description":"Pares de cartas (3–8).","minItems":3,"maxItems":8},"level":{"type":"string","enum":["comprension","inferencia","aplicacion"],"description":"Nivel cognitivo: comprension, inferencia o aplicacion."}},"required":["type","pairs"],"additionalProperties":false},"BlockSort":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/sort.json","title":"Bloque «sort»","description":"Clasificar: arrastrar (o tocar) cada elemento a su categoría. Accesible con teclado.","type":"object","properties":{"type":{"const":"sort","description":"Tipo de bloque (siempre \"sort\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"prompt":{"type":"string","maxLength":600,"description":"Instrucción opcional."},"categories":{"type":"array","items":{"type":"string","maxLength":80,"description":"Elemento de texto."},"description":"Nombres de categoría (2–5).","minItems":2,"maxItems":5},"items":{"type":"array","items":{"type":"object","properties":{"text":{"type":"string","maxLength":120,"description":"Texto del elemento."},"category":{"type":"string","maxLength":80,"description":"Categoría correcta (idéntica a un nombre de categories)."}},"required":["text","category"],"additionalProperties":false,"description":"Un elemento a clasificar."},"description":"Elementos (4–14).","minItems":4,"maxItems":14},"evidence":{"type":"string","maxLength":1000,"description":"Cita textual tomada del propio objeto que justifica la respuesta correcta. OBLIGATORIA en todo bloque puntuado."},"level":{"type":"string","enum":["comprension","inferencia","aplicacion"],"description":"Nivel cognitivo: comprension, inferencia o aplicacion."}},"required":["type","categories","items"],"additionalProperties":false},"BlockHangman":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/hangman.json","title":"Bloque «hangman»","description":"Ahorcado educativo: el alumno descubre una palabra o expresión letra a letra guiado por una pista. Teclado en pantalla y físico; dos identidades (clásica con monito esquemático y alternativa amable sin ahorcamiento). Acepta vocales con o sin tilde, distingue la Ñ, no penaliza repetir y al terminar explica el concepto y permite otra ronda.","type":"object","properties":{"type":{"const":"hangman","description":"Tipo de bloque (siempre \"hangman\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"title":{"type":"string","maxLength":180,"description":"Título del juego."},"instructions":{"type":"string","maxLength":500,"description":"Instrucciones opcionales."},"word":{"type":"string","maxLength":80,"description":"Palabra o expresión a descubrir (mínimo 2 letras; espacios y signos se muestran y no se adivinan)."},"clue":{"type":"string","maxLength":500,"description":"Pista educativa que orienta sin revelar la palabra."},"explanation":{"type":"string","maxLength":1000,"description":"Explicación del concepto que se muestra al terminar (acierto o derrota)."},"attempts":{"type":"integer","description":"Fallos permitidos antes de perder (3–12; por omisión 6).","minimum":3,"maximum":12},"variant":{"type":"string","enum":["classic","friendly"],"description":"\"classic\" = monito esquemático; \"friendly\" (por omisión) = alternativa visual sin ahorcamiento."},"level":{"type":"string","enum":["comprension","inferencia","aplicacion"],"description":"Nivel cognitivo: comprension, inferencia o aplicacion."}},"required":["type","word","clue"],"additionalProperties":false},"BlockAdventure":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/adventure.json","title":"Bloque «adventure»","description":"Aventura de progreso: un personaje recorre un camino hacia una meta respondiendo retos. Cada acierto lo acerca; cada error daña el escenario (rompe una tabla, hunde una plataforma, drena energía, revienta un globo, tambalea la torre). Al agotar los intentos cae con una animación cómica, sin contenido gráfico. Cinco identidades visuales comparten la misma lógica.","type":"object","properties":{"type":{"const":"adventure","description":"Tipo de bloque (siempre \"adventure\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"title":{"type":"string","maxLength":180,"description":"Título del juego."},"instructions":{"type":"string","maxLength":500,"description":"Instrucciones opcionales."},"theme":{"type":"string","enum":["bridge","lava","space","balloon","tower"],"description":"Identidad visual: bridge=puente de cocodrilos, lava=el suelo es lava, space=rescate espacial, balloon=globo en peligro, tower=torre tambaleante."},"goal":{"type":"string","maxLength":120,"description":"Nombre de la meta a la que llega el personaje (p. ej. \"la orilla\")."},"steps":{"type":"integer","description":"Aciertos necesarios para llegar a la meta (1–12; por omisión 5, ajustado al contenido disponible).","minimum":1,"maximum":12},"attempts":{"type":"integer","description":"Fallos permitidos antes de perder (1–8; por omisión 3).","minimum":1,"maximum":8},"challenges":{"type":"array","items":{"type":"object","properties":{"prompt":{"type":"string","maxLength":600,"description":"Enunciado del reto."},"options":{"type":"array","items":{"type":"string","maxLength":300,"description":"Elemento de texto."},"description":"Opciones (2–6); la correcta se indica con correct_index.","minItems":2,"maxItems":6},"correct_index":{"type":"integer","description":"Índice (base 0) de la opción correcta.","minimum":0,"maximum":5},"explanation":{"type":"string","maxLength":1000,"description":"Retroalimentación educativa: se muestra al acertar y, sobre todo, al fallar."}},"required":["prompt","options","correct_index"],"additionalProperties":false,"description":"Un reto de opción múltiple del camino."},"description":"Retos del camino (2–20). Deben bastar para llegar a la meta aun fallando.","minItems":2,"maxItems":20},"evidence":{"type":"string","maxLength":1000,"description":"Cita opcional del OA que respalda los retos (este juego no puntúa con evidence)."},"level":{"type":"string","enum":["comprension","inferencia","aplicacion"],"description":"Nivel cognitivo: comprension, inferencia o aplicacion."}},"required":["type","theme","challenges"],"additionalProperties":false},"BlockPuzzle":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/puzzle.json","title":"Bloque «puzzle»","description":"Rompecabezas de imágenes en tres modalidades: classic (reconstruir con 6\/12\/24 piezas), reveal (la imagen empieza cubierta y cada acierto destapa una parte; al final se identifica qué muestra) y explore (primero se arma y después se contestan preguntas sobre ella). La imagen se recorta sin distorsión; admite vista previa, pistas y reinicio.","type":"object","properties":{"type":{"const":"puzzle","description":"Tipo de bloque (siempre \"puzzle\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"title":{"type":"string","maxLength":180,"description":"Título del juego."},"instructions":{"type":"string","maxLength":500,"description":"Instrucciones opcionales."},"mode":{"type":"string","enum":["classic","reveal","explore"],"description":"classic=reconstruir; reveal=descubrir la imagen respondiendo; explore=armar y luego responder sobre ella."},"image":{"type":"string","pattern":"^assets\/[A-Za-z0-9._\/-]+$","description":"Imagen del rompecabezas; se recorta en piezas sin distorsión. Ruta relativa dentro del paquete (assets\/…); el archivo debe ir incluido en el ZIP."},"alt":{"type":"string","maxLength":300,"description":"Texto alternativo de la imagen (accesibilidad)."},"tiles":{"type":"array","items":{"type":"string","maxLength":24,"description":"Elemento de texto."},"description":"Fichas de texto\/emoji EN SU ORDEN correcto (modo autónomo sin imagen).","minItems":4,"maxItems":24},"pieces":{"type":"integer","enum":[6,12,24],"description":"Número de piezas (6, 12 o 24); ajústalo a la edad y a la pantalla. Por omisión 12."},"preview":{"type":"boolean","description":"true (por omisión) muestra un botón de vista previa de la imagen armada."},"credit":{"type":"string","maxLength":300,"description":"Procedencia y atribución de la imagen (licencia compatible)."},"caption":{"type":"string","maxLength":500,"description":"Qué muestra la imagen (se revela al completarla)."},"questions":{"type":"array","items":{"type":"object","properties":{"prompt":{"type":"string","maxLength":600,"description":"Enunciado de la pregunta sobre la imagen."},"options":{"type":"array","items":{"type":"string","maxLength":300,"description":"Elemento de texto."},"description":"Opciones (2–6); la correcta se indica con correct_index.","minItems":2,"maxItems":6},"correct_index":{"type":"integer","description":"Índice (base 0) de la opción correcta.","minimum":0,"maximum":5},"explanation":{"type":"string","maxLength":1000,"description":"Retroalimentación educativa tras responder."}},"required":["prompt","options","correct_index"],"additionalProperties":false,"description":"Una pregunta sobre la imagen."},"description":"Preguntas (1–12). Obligatorias (≥2) en los modos reveal y explore.","minItems":1,"maxItems":12},"level":{"type":"string","enum":["comprension","inferencia","aplicacion"],"description":"Nivel cognitivo: comprension, inferencia o aplicacion."}},"required":["type","mode"],"additionalProperties":false},"BlockFlashcards":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/flashcards.json","title":"Bloque «flashcards»","description":"Tarjetas de estudio que se voltean (front → back).","type":"object","properties":{"type":{"const":"flashcards","description":"Tipo de bloque (siempre \"flashcards\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"title":{"type":"string","maxLength":180,"description":"Título del mazo."},"cards":{"type":"array","items":{"type":"object","properties":{"front":{"type":"string","maxLength":300,"description":"Cara frontal (pregunta o término)."},"back":{"type":"string","maxLength":1000,"description":"Cara trasera (respuesta); admite **negrita**."}},"required":["front","back"],"additionalProperties":false,"description":"Una tarjeta."},"description":"Tarjetas (2–60).","minItems":2,"maxItems":60}},"required":["type","cards"],"additionalProperties":false},"BlockTimeline":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/timeline.json","title":"Bloque «timeline»","description":"Línea de tiempo con eventos ordenados.","type":"object","properties":{"type":{"const":"timeline","description":"Tipo de bloque (siempre \"timeline\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"title":{"type":"string","maxLength":180,"description":"Título de la línea."},"events":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string","maxLength":60,"description":"Etiqueta corta (fecha o hito)."},"title":{"type":"string","maxLength":180,"description":"Título del evento."},"text":{"type":"string","maxLength":1500,"description":"Descripción del evento."},"evidence":{"type":"string","maxLength":1000,"description":"Cita opcional que respalda el evento."}},"required":["label","title","text"],"additionalProperties":false,"description":"Un evento."},"description":"Eventos (2–60).","minItems":2,"maxItems":60}},"required":["type","events"],"additionalProperties":false},"BlockBranch":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/branch.json","title":"Bloque «branch»","description":"Narrativa ramificada: un grafo de nodos con decisiones, alcanzable desde «start».","type":"object","properties":{"type":{"const":"branch","description":"Tipo de bloque (siempre \"branch\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"title":{"type":"string","maxLength":180,"description":"Título de la actividad."},"nodes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","maxLength":40,"description":"Identificador único del nodo."},"text":{"type":"string","maxLength":2000,"description":"Texto del nodo."},"is_end":{"type":"boolean","description":"true si el nodo es un final."},"choices":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string","maxLength":180,"description":"Texto de la opción."},"goto":{"type":"string","maxLength":40,"description":"id del nodo destino."}},"required":["label","goto"],"additionalProperties":false,"description":"Una opción."},"description":"Opciones del nodo.","minItems":1,"maxItems":6}},"required":["id","text"],"additionalProperties":false,"description":"Un nodo."},"description":"Nodos del grafo (2–80).","minItems":2,"maxItems":80}},"required":["type","nodes"],"additionalProperties":false},"BlockReflection":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/reflection.json","title":"Bloque «reflection»","description":"Pregunta abierta de reflexión; el alumno escribe su respuesta.","type":"object","properties":{"type":{"const":"reflection","description":"Tipo de bloque (siempre \"reflection\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"prompt":{"type":"string","maxLength":1000,"description":"Pregunta abierta."},"hint":{"type":"string","maxLength":500,"description":"Pista opcional para orientar la respuesta."},"level":{"type":"string","enum":["comprension","inferencia","aplicacion"],"description":"Nivel cognitivo: comprension, inferencia o aplicacion."}},"required":["type","prompt"],"additionalProperties":false},"BlockStepReveal":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/step_reveal.json","title":"Bloque «step_reveal»","description":"Explicación en pasos que se revelan uno a uno («Mostrar siguiente»); cada paso admite texto, código, fórmula, imagen y recuadro.","type":"object","properties":{"type":{"const":"step_reveal","description":"Tipo de bloque (siempre \"step_reveal\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"title":{"type":"string","maxLength":180,"description":"Título de la secuencia de pasos."},"steps":{"type":"array","items":{"type":"object","properties":{"title":{"type":"string","maxLength":180,"description":"Título del paso."},"text":{"type":"string","maxLength":2000,"description":"Texto del paso (obligatorio); admite **negrita** y *cursiva*."},"code":{"type":"string","maxLength":2000,"description":"Bloque de código opcional."},"formula":{"oneOf":[{"type":"string"},{"type":"object"},{"type":"array"}],"description":"Fórmula opcional como árbol inerte (mismo formato que el bloque formula); el renderer la traduce a MathML."},"image":{"type":"string","pattern":"^assets\/[A-Za-z0-9._\/-]+$","description":"Imagen opcional del paso. Ruta relativa dentro del paquete (assets\/…); el archivo debe ir incluido en el ZIP."},"callout":{"type":"string","maxLength":500,"description":"Recuadro opcional para destacar algo del paso."}},"required":["text"],"additionalProperties":false,"description":"Un paso."},"description":"Pasos que se revelan en orden (2–30).","minItems":2,"maxItems":30}},"required":["type","steps"],"additionalProperties":false},"BlockCheckpoint":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/checkpoint.json","title":"Bloque «checkpoint»","description":"Compuerta de avance: agrega las puntuaciones locales de los bloques que cubre y, según el umbral, declara «superado» o «requiere refuerzo», con enlaces opcionales para continuar.","type":"object","properties":{"type":{"const":"checkpoint","description":"Tipo de bloque (siempre \"checkpoint\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"title":{"type":"string","maxLength":180,"description":"Título de la compuerta."},"passing_score":{"type":"number","minimum":0,"maximum":1,"description":"Umbral de aprobación como fracción de 0 a 1 (p. ej. 0.7 = 70 % de acierto agregado)."},"covers":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Id de un bloque puntuado del objeto."},"description":"Ids de los bloques que agrega (por omisión, todos los puntuados del objeto).","minItems":1,"maxItems":60},"on_pass":{"oneOf":[{"type":"string","pattern":"^[a-z0-9][a-z0-9-]{0,179}$","description":"Slug de otro objeto (p. ej. \"repaso-fracciones\")."},{"type":"string","format":"uri","pattern":"^https?:\/\/","maxLength":500,"description":"URL externa http(s)."}],"description":"Destino: slug de otro objeto o URL http(s)."},"on_fail":{"oneOf":[{"type":"string","pattern":"^[a-z0-9][a-z0-9-]{0,179}$","description":"Slug de otro objeto (p. ej. \"repaso-fracciones\")."},{"type":"string","format":"uri","pattern":"^https?:\/\/","maxLength":500,"description":"URL externa http(s)."}],"description":"Destino: slug de otro objeto o URL http(s)."},"pass_message":{"type":"string","maxLength":500,"description":"Mensaje cuando se supera la compuerta."},"fail_message":{"type":"string","maxLength":500,"description":"Mensaje cuando aún no se alcanza el umbral."}},"required":["type","passing_score"],"additionalProperties":false},"BlockSourceCompare":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/source_compare.json","title":"Bloque «source_compare»","description":"Comparar fuentes: muestra 2–4 paneles (texto, imagen, tabla o enlace) lado a lado para contrastarlos.","type":"object","properties":{"type":{"const":"source_compare","description":"Tipo de bloque (siempre \"source_compare\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"title":{"type":"string","maxLength":180,"description":"Título de la comparación."},"prompt":{"type":"string","maxLength":600,"description":"Instrucción opcional."},"sources":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","enum":["text","image","table","link"],"description":"Tipo de fuente."},"label":{"type":"string","maxLength":80,"description":"Etiqueta o título de la fuente (en «link» es el texto del enlace)."},"text":{"type":"string","maxLength":5000,"description":"Contenido (kind:text)."},"src":{"type":"string","pattern":"^assets\/[A-Za-z0-9._\/-]+$","description":"Imagen (kind:image). Ruta relativa dentro del paquete (assets\/…); el archivo debe ir incluido en el ZIP."},"alt":{"type":"string","maxLength":300,"description":"Texto alternativo de la imagen (kind:image, obligatorio)."},"columns":{"type":"array","items":{"type":"string","maxLength":120,"description":"Elemento de texto."},"description":"Columnas (kind:table).","minItems":1,"maxItems":12},"rows":{"type":"array","items":{"type":"array","items":{"type":"string","maxLength":300,"description":"Celda."},"description":"Fila.","minItems":1,"maxItems":12},"description":"Filas (kind:table).","minItems":1,"maxItems":100},"url":{"type":"string","format":"uri","pattern":"^https?:\/\/","maxLength":500,"description":"Destino http(s) (kind:link)."}},"required":["kind"],"additionalProperties":false,"description":"Una fuente; según «kind» requiere: text→text · image→src+alt · table→columns+rows · link→url."},"description":"Fuentes a comparar (2–4).","minItems":2,"maxItems":4},"caption":{"type":"string","maxLength":500,"description":"Pie opcional de la comparación."}},"required":["type","sources"],"additionalProperties":false},"BlockArgumentBuilder":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/argument_builder.json","title":"Bloque «argument_builder»","description":"Andamio argumentativo: guía al alumno a construir tesis, evidencia, razonamiento y conclusión (y, si procede, contraargumento y respuesta) en áreas de texto.","type":"object","properties":{"type":{"const":"argument_builder","description":"Tipo de bloque (siempre \"argument_builder\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"title":{"type":"string","maxLength":180,"description":"Título de la actividad."},"prompt":{"type":"string","maxLength":600,"description":"Consigna: qué debe argumentar el alumno."},"counterargument":{"type":"boolean","description":"true = añade los pasos «Contraargumento» y «Tu respuesta»."},"hints":{"type":"object","properties":{"tesis":{"type":"string","maxLength":300,"description":"Pista para la tesis."},"evidencia":{"type":"string","maxLength":300,"description":"Pista para la evidencia."},"razonamiento":{"type":"string","maxLength":300,"description":"Pista para el razonamiento."},"conclusion":{"type":"string","maxLength":300,"description":"Pista para la conclusión."},"contraargumento":{"type":"string","maxLength":300,"description":"Pista para el contraargumento (si counterargument)."},"respuesta":{"type":"string","maxLength":300,"description":"Pista para la respuesta al contraargumento (si counterargument)."}},"required":[],"additionalProperties":false,"description":"Pistas opcionales, una por paso del andamio."}},"required":["type"],"additionalProperties":false},"BlockBeforeAfter":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/before_after.json","title":"Bloque «before_after»","description":"Comparador antes\/después: dos imágenes superpuestas con un divisor que el alumno desliza (ratón, táctil o teclado).","type":"object","properties":{"type":{"const":"before_after","description":"Tipo de bloque (siempre \"before_after\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"title":{"type":"string","maxLength":180,"description":"Título."},"before_src":{"type":"string","pattern":"^assets\/[A-Za-z0-9._\/-]+$","description":"Imagen del «antes». Ruta relativa dentro del paquete (assets\/…); el archivo debe ir incluido en el ZIP."},"after_src":{"type":"string","pattern":"^assets\/[A-Za-z0-9._\/-]+$","description":"Imagen del «después». Ruta relativa dentro del paquete (assets\/…); el archivo debe ir incluido en el ZIP."},"before_label":{"type":"string","maxLength":80,"description":"Etiqueta del «antes» (por omisión «Antes»)."},"after_label":{"type":"string","maxLength":80,"description":"Etiqueta del «después» (por omisión «Después»)."},"alt":{"type":"string","maxLength":300,"description":"Texto alternativo compartido por ambas imágenes (accesibilidad)."},"caption":{"type":"string","maxLength":500,"description":"Pie opcional."}},"required":["type","before_src","after_src","alt"],"additionalProperties":false},"BlockScratchpad":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/scratchpad.json","title":"Bloque «scratchpad»","description":"Bloc de notas libre para cálculos, borradores o apuntes; se autoguarda en el dispositivo y, si el alumno tiene sesión, puede subirlo a su cuenta.","type":"object","properties":{"type":{"const":"scratchpad","description":"Tipo de bloque (siempre \"scratchpad\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"title":{"type":"string","maxLength":180,"description":"Título del bloc."},"placeholder":{"type":"string","maxLength":500,"description":"Texto de ejemplo dentro del área."},"rows":{"type":"integer","description":"Altura del área en filas (2–40).","minimum":2,"maximum":40},"save":{"type":"boolean","description":"true (por omisión) = muestra el botón «Guardar» para subirlo a la cuenta; false = sólo en el dispositivo."}},"required":["type"],"additionalProperties":false},"BlockVisualExplanation":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/visual_explanation.json","title":"Bloque «visual_explanation»","description":"Motor de explicaciones visuales: una secuencia DECLARATIVA de escenas que construyen la idea delante del alumno, revelándola poco a poco (no todo a la vez). Se reproduce a pantalla completa, con modo manual (por defecto) o automático, y narración opcional. NO es un cuarto tipo de objeto: es un bloque más, utilizable dentro de un OA o de un refuerzo.","type":"object","properties":{"type":{"const":"visual_explanation","description":"Tipo de bloque (siempre \"visual_explanation\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"title":{"type":"string","maxLength":160,"description":"Título breve de la explicación (encabezado \/ etiqueta del botón)."},"canvas":{"type":"string","enum":["wide","square","tall"],"description":"Proporción del lienzo."},"scenes":{"type":"array","items":{"type":"object","properties":{"caption":{"type":"string","maxLength":160,"description":"Idea central de la escena, en muy pocas palabras."},"narration":{"type":"string","maxLength":280,"description":"Frase que se muestra como subtítulo y se puede narrar por voz."},"layout":{"type":"string","enum":["center","row","column","grid","groups","compare","split","equation"],"description":"Cómo compone el motor los elementos (no pides píxeles)."},"ask":{"type":"string","maxLength":200,"description":"Pausa pedagógica: pregunta antes de continuar."},"items":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","enum":["text","number","emoji","shape","image","label","group"],"description":"Tipo de elemento semántico."},"id":{"type":"string","maxLength":40,"description":"Identificador para que una acción lo apunte (target)."},"value":{"description":"Texto (text\/label) o número (number)."},"glyph":{"type":"string","maxLength":8,"description":"Uno o pocos caracteres para emoji."},"shape":{"type":"string","enum":["rect","circle","triangle","bar"],"description":"Figura para kind=shape."},"color":{"type":"string","enum":["accent","warm","ok","ink","line"],"description":"Color semántico (nunca un hex)."},"count":{"type":"integer","description":"Cuántas copias (emoji\/shape), 1–24.","minimum":1,"maximum":24},"size":{"type":"integer","description":"Escala tipográfica 1–6.","minimum":1,"maximum":6},"emphasis":{"type":"string","enum":["strong","muted"],"description":"Énfasis del texto."},"src":{"type":"string","pattern":"^assets\/[A-Za-z0-9._\/-]+$","description":"Imagen para kind=image (el archivo va en el ZIP). Ruta relativa dentro del paquete (assets\/…); el archivo debe ir incluido en el ZIP."},"alt":{"type":"string","maxLength":200,"description":"Texto alternativo de la imagen."},"label":{"type":"string","maxLength":40,"description":"Etiqueta del grupo."},"of":{"type":"array","items":{"type":"object","description":"Elemento hijo del grupo (mismo formato que un item; sin grupos anidados)."},"description":"Elementos del grupo (1–12).","minItems":1,"maxItems":12}},"required":["kind"],"additionalProperties":false,"description":"Un elemento de la escena."},"description":"Elementos de la escena (0–24).","minItems":0,"maxItems":24},"actions":{"type":"array","items":{"type":"object","properties":{"do":{"type":"string","enum":["show","hide","highlight","move","pulse","wait"],"description":"Acción a ejecutar en la línea de tiempo de la escena."},"target":{"type":"string","maxLength":40,"description":"id del elemento sobre el que actúa (obligatorio para show\/hide\/highlight\/move\/pulse)."},"delay_ms":{"type":"integer","description":"Retraso en milisegundos desde que inicia la escena (0–8000).","minimum":0,"maximum":8000},"text":{"type":"string","maxLength":160,"description":"Texto opcional de la acción."}},"required":["do"],"additionalProperties":false,"description":"Una acción de la escena."},"description":"Acciones (0–12).","minItems":0,"maxItems":12}},"required":[],"additionalProperties":false,"description":"Una escena = una idea nueva."},"description":"Escenas en orden (2–14; con una sola el bloque se ve estático, sin controles).","minItems":1,"maxItems":14}},"required":["type","scenes"],"additionalProperties":false},"BlockReading":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/reading.json","title":"Bloque «reading»","description":"Una LECTURA dentro del propio OA, con el mismo motor del lector de libros y lecturas cortas de Lumbre: formato (párrafos, encabezados, énfasis, listas, imágenes), ajustes de tipografía\/apariencia, autoavance voluntario (arranca pausado, velocidad ajustable, se detiene al final sin saltar a otro contenido), pantalla completa real o vista ampliada y recuperación de posición. Dos modalidades con la MISMA experiencia: contenido PROPIO (text editado en el OA) o VINCULADO (una lectura existente o un fragmento delimitado de un libro, conservando autoría y referencia). No puntúa por sí solo: llegar al final con autoavance NO aprueba el OA ni demuestra comprensión; acompáñalo de una actividad posterior (mcq, reflection, argument_builder…).","type":"object","properties":{"type":{"const":"reading","description":"Tipo de bloque (siempre \"reading\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"title":{"type":"string","maxLength":180,"description":"Título de la lectura (se muestra en la cabecera del bloque)."},"purpose":{"type":"string","maxLength":300,"description":"Propósito breve de la lectura: para qué la lee el alumno (una frase)."},"minutes":{"type":"integer","description":"Tiempo estimado de lectura en minutos (1–180). Si se omite, Lumbre lo estima por el número de palabras.","minimum":1,"maximum":180},"content":{"type":"string","maxLength":24000,"description":"Texto PROPIO de la lectura (modalidad propia). Markdown-lite del lector: párrafos separados por línea en blanco, **negrita**, *cursiva*, ## encabezado, - listas, > cita y ![alt](assets\/…) imágenes. Referencia estable por párrafo: cada bloque recibe un id automático."},"source":{"type":"object","properties":{"book":{"oneOf":[{"type":"string","pattern":"^[a-z0-9][a-z0-9-]{0,189}$","description":"Slug del recurso."},{"type":"integer","minimum":1,"description":"Id del recurso."}],"description":"Lectura o libro vinculado (slug o id)."},"chapter":{"oneOf":[{"type":"string","pattern":"^[a-z0-9][a-z0-9-]{0,189}$"},{"type":"integer","minimum":0},{"type":"null"}],"description":"Capítulo por slug o por posición (0 = primero); null\/omitido = recurso completo o su primer capítulo."},"from_block":{"oneOf":[{"type":"string","pattern":"^[A-Za-z0-9_-]{4,64}$"},{"type":"null"}],"description":"Identificador estable del bloque de inicio del fragmento (book_blocks.id)."},"to_block":{"oneOf":[{"type":"string","pattern":"^[A-Za-z0-9_-]{4,64}$"},{"type":"null"}],"description":"Identificador estable del bloque final del fragmento (incluido)."},"version":{"oneOf":[{"type":"string","maxLength":64},{"type":"null"}],"description":"Versión identificable del recurso enlazada; si cambia, Lumbre avisa al autor antes de adoptar cambios que invaliden las actividades."}},"required":["book"],"additionalProperties":false,"description":"Modalidad VINCULADA: referencia a una lectura\/libro existente. La visibilidad se respeta al pintar (un OA público no expone un recurso privado)."},"attribution":{"type":"string","maxLength":300,"description":"Autoría y fuente del recurso (p. ej. «Ana R. — Dominio público \/ Proyecto Gutenberg»). OBLIGATORIA cuando hay source."},"settings":{"type":"object","properties":{"theme":{"type":"string","enum":["paper","light","sepia","dark","oled","midnight","forest"],"description":"Apariencia inicial del lector."},"text_align":{"type":"string","enum":["left","center","justify"],"description":"Alineación del texto."},"font_family":{"type":"string","maxLength":40,"description":"Familia tipográfica (p. ej. Georgia)."},"font_size":{"type":"integer","description":"Tamaño de letra inicial (px).","minimum":12,"maximum":40},"content_width":{"type":"integer","description":"Ancho de la columna de texto (px).","minimum":300,"maximum":1200},"line_height":{"type":"number","description":"Interlineado (1–3)."},"paragraph_spacing":{"type":"number","description":"Espaciado entre párrafos (em, 0–4)."},"brightness":{"type":"number","description":"Brillo del texto (0.5–1.5)."},"autoscroll_speed":{"type":"integer","description":"Velocidad inicial del autoavance (palabras\/min, 60–600).","minimum":60,"maximum":600}},"required":[],"additionalProperties":false,"description":"Ajustes iniciales del lector para esta lectura; el alumno puede cambiarlos y se guardan como preferencia general."},"remember_position":{"type":"string","enum":["auto","yes","no"],"description":"Recordar posición: auto (decide por extensión: las breves no persisten, las extensas sí), yes (persiste siempre) o no (sólo la sesión)."}},"required":["type"],"additionalProperties":false},"BlockComic":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/comic.json","title":"Bloque «comic»","description":"Secuencia de viñetas ordenadas con diálogos y texto alternativo. Ideal para narrar procesos históricos, científicos o sociales con personajes y contexto.","type":"object","properties":{"type":{"const":"comic","description":"Tipo de bloque (siempre \"comic\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"title":{"type":"string","maxLength":180,"description":"Título del cómic."},"panels":{"type":"array","items":{"type":"object","properties":{"src":{"type":"string","pattern":"^assets\/[A-Za-z0-9._\/-]+$","description":"Imagen de la viñeta. Ruta relativa dentro del paquete (assets\/…); el archivo debe ir incluido en el ZIP."},"alt":{"type":"string","maxLength":300,"description":"Texto alternativo de la viñeta (accesibilidad)."},"caption":{"type":"string","maxLength":300,"description":"Pie o narración de la viñeta."},"dialogue":{"type":"string","maxLength":200,"description":"Diálogo del personaje en la viñeta."}},"required":["src","alt"],"additionalProperties":false,"description":"Una viñeta del cómic."},"description":"Viñetas en orden (2–12).","minItems":2,"maxItems":12},"transcript":{"type":"string","maxLength":2000,"description":"Transcripción accesible del cómic (diálogos y narración en texto plano)."},"caption":{"type":"string","maxLength":500,"description":"Pie general del cómic."}},"required":["type","panels"],"additionalProperties":false},"BlockDiagram":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/diagram.json","title":"Bloque «diagram»","description":"Diagrama de procesos, relaciones, estructuras o secuencias. Puede ser una imagen\/SVG estático (src) o un grafo declarativo (nodes+edges) que el runtime dibuja.","type":"object","properties":{"type":{"const":"diagram","description":"Tipo de bloque (siempre \"diagram\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"title":{"type":"string","maxLength":180,"description":"Título del diagrama."},"src":{"type":"string","pattern":"^assets\/[A-Za-z0-9._\/-]+$","description":"Imagen o SVG del diagrama (alternativa a nodes+edges). Ruta relativa dentro del paquete (assets\/…); el archivo debe ir incluido en el ZIP."},"alt":{"type":"string","maxLength":300,"description":"Texto alternativo del diagrama (accesibilidad)."},"nodes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","maxLength":40,"description":"Identificador del nodo."},"label":{"type":"string","maxLength":80,"description":"Etiqueta del nodo."},"group":{"type":"string","maxLength":40,"description":"Grupo o categoría del nodo (opcional)."}},"required":["id","label"],"additionalProperties":false,"description":"Un nodo del diagrama."},"description":"Nodos del diagrama (2–30).","minItems":2,"maxItems":30},"edges":{"type":"array","items":{"type":"object","properties":{"from":{"type":"string","maxLength":40,"description":"Id del nodo origen."},"to":{"type":"string","maxLength":40,"description":"Id del nodo destino."},"label":{"type":"string","maxLength":80,"description":"Etiqueta de la relación."}},"required":["from","to"],"additionalProperties":false,"description":"Una arista del diagrama."},"description":"Aristas del diagrama (0–50).","minItems":0,"maxItems":50},"layout":{"type":"string","enum":["hierarchical","radial","flow","free"],"description":"Algoritmo de disposición automática."},"caption":{"type":"string","maxLength":500,"description":"Pie del diagrama."},"transcript":{"type":"string","maxLength":1000,"description":"Transcripción accesible (describe nodos y relaciones en texto plano)."}},"required":["type","alt"],"additionalProperties":false},"BlockPoster":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/poster.json","title":"Bloque «poster»","description":"Lámina o póster visual con conceptos, relaciones y ejemplos. Resume visualmente un tema con secciones identificables.","type":"object","properties":{"type":{"const":"poster","description":"Tipo de bloque (siempre \"poster\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"title":{"type":"string","maxLength":180,"description":"Título de la lámina."},"src":{"type":"string","pattern":"^assets\/[A-Za-z0-9._\/-]+$","description":"Imagen de la lámina. Ruta relativa dentro del paquete (assets\/…); el archivo debe ir incluido en el ZIP."},"alt":{"type":"string","maxLength":300,"description":"Texto alternativo (accesibilidad)."},"sections":{"type":"array","items":{"type":"object","properties":{"title":{"type":"string","maxLength":120,"description":"Título de la sección."},"text":{"type":"string","maxLength":500,"description":"Contenido de la sección."}},"required":["title","text"],"additionalProperties":false,"description":"Una sección de la lámina."},"description":"Secciones conceptuales (0–12).","minItems":0,"maxItems":12},"transcript":{"type":"string","maxLength":2000,"description":"Transcripción accesible (describe el contenido visual en texto plano)."},"caption":{"type":"string","maxLength":500,"description":"Pie de la lámina."}},"required":["type","src","alt"],"additionalProperties":false},"BlockVisualNotes":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/visual_notes.json","title":"Bloque «visual_notes»","description":"Síntesis visual estilo cuaderno: flechas, anotaciones, conceptos destacados. Conserve siempre una versión textual accesible.","type":"object","properties":{"type":{"const":"visual_notes","description":"Tipo de bloque (siempre \"visual_notes\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"title":{"type":"string","maxLength":180,"description":"Título de los apuntes."},"src":{"type":"string","pattern":"^assets\/[A-Za-z0-9._\/-]+$","description":"Imagen de los apuntes. Ruta relativa dentro del paquete (assets\/…); el archivo debe ir incluido en el ZIP."},"alt":{"type":"string","maxLength":300,"description":"Texto alternativo (accesibilidad)."},"annotations":{"type":"array","items":{"type":"object","properties":{"text":{"type":"string","maxLength":200,"description":"Texto de la anotación."},"target":{"type":"string","maxLength":40,"description":"Id o referencia al elemento anotado (opcional)."},"x":"float(0–1) Coordenada horizontal normalizada sobre la imagen (opcional).","y":"float(0–1) Coordenada vertical normalizada sobre la imagen (opcional).","color":"accent|warm|ok|ink|line — Color del marcador (opcional)."},"required":["text"],"additionalProperties":false,"description":"Una anotación."},"description":"Anotaciones sobre los apuntes (0–20).","minItems":0,"maxItems":20},"transcript":{"type":"string","maxLength":2000,"description":"Transcripción accesible (describe los apuntes y anotaciones en texto plano)."},"caption":{"type":"string","maxLength":500,"description":"Pie de los apuntes."}},"required":["type","src","alt"],"additionalProperties":false},"BlockInteractiveLab":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","$id":"https:\/\/lumbre.lat\/schemas\/block\/interactive_lab.json","title":"Bloque «interactive_lab»","description":"Laboratorio declarativo donde el alumno modifica variables, observa resultados y responde una pregunta educativa. El modelo matemático usa un vocabulario cerrado de operaciones (sin eval\/Function\/JS recibido).","type":"object","properties":{"type":{"const":"interactive_lab","description":"Tipo de bloque (siempre \"interactive_lab\")."},"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,39}$","description":"Identificador opcional del bloque (casi siempre se omite; sólo lo usan los nodos de un branch)."},"title":{"type":"string","maxLength":180,"description":"Título del laboratorio."},"config":{"type":"object","properties":{"variables":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","maxLength":40,"description":"Identificador de la variable."},"label":{"type":"string","maxLength":80,"description":"Etiqueta legible de la variable."},"min":{"type":"number","description":"Valor mínimo."},"max":{"type":"number","description":"Valor máximo."},"default":{"type":"number","description":"Valor por defecto."},"step":{"type":"number","description":"Incremento del control."},"unit":{"type":"string","maxLength":24,"description":"Unidad de la variable."}},"required":["id","label","min","max","default"],"additionalProperties":false,"description":"Una variable del laboratorio."},"description":"Variables del laboratorio (1–8).","minItems":1,"maxItems":8},"model":{"type":"object","properties":{"operations":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","maxLength":40,"description":"Identificador del resultado calculado."},"expr":{"type":"string","maxLength":200,"description":"Expresión con variables y operadores permitidos (+ - * \/ ^, funciones: sin cos tan sqrt abs log ln exp floor round, constantes: pi e)."}},"required":["id","expr"],"additionalProperties":false,"description":"Una operación del modelo."},"description":"Operaciones del modelo (1–20).","minItems":1,"maxItems":20}},"required":["operations"],"additionalProperties":false,"description":"Modelo matemático del laboratorio."},"display":{"type":"object","properties":{"type":{"type":"string","enum":["graph","gauge","table"],"description":"Tipo de visualización."},"x_axis":{"type":"string","maxLength":40,"description":"Variable del eje X (para graph)."},"y_axis":{"type":"string","maxLength":40,"description":"Variable o resultado del eje Y (para graph)."}},"required":["type"],"additionalProperties":false,"description":"Visualización del laboratorio."}},"required":["variables","model","display"],"additionalProperties":false,"description":"Configuración del laboratorio."},"instructions":{"type":"string","maxLength":500,"description":"Instrucciones para el alumno."},"feedback":{"type":"string","maxLength":500,"description":"Retroalimentación, supuestos y límites del modelo."},"alt_text":{"type":"string","maxLength":300,"description":"Descripción textual alternativa (accesibilidad)."}},"required":["type","config","alt_text"],"additionalProperties":false},"Block":{"description":"Un bloque del documento. Vocabulario cerrado: sólo los tipos listados en oneOf.","oneOf":[{"$ref":"#\/components\/schemas\/BlockHeading"},{"$ref":"#\/components\/schemas\/BlockText"},{"$ref":"#\/components\/schemas\/BlockList"},{"$ref":"#\/components\/schemas\/BlockCallout"},{"$ref":"#\/components\/schemas\/BlockQuote"},{"$ref":"#\/components\/schemas\/BlockCode"},{"$ref":"#\/components\/schemas\/BlockImage"},{"$ref":"#\/components\/schemas\/BlockFigure"},{"$ref":"#\/components\/schemas\/BlockAudio"},{"$ref":"#\/components\/schemas\/BlockVideo"},{"$ref":"#\/components\/schemas\/BlockEmbed"},{"$ref":"#\/components\/schemas\/BlockLink"},{"$ref":"#\/components\/schemas\/BlockTable"},{"$ref":"#\/components\/schemas\/BlockChart"},{"$ref":"#\/components\/schemas\/BlockMindmap"},{"$ref":"#\/components\/schemas\/BlockGraph"},{"$ref":"#\/components\/schemas\/BlockFormula"},{"$ref":"#\/components\/schemas\/BlockMcq"},{"$ref":"#\/components\/schemas\/BlockTrueFalse"},{"$ref":"#\/components\/schemas\/BlockFillBlank"},{"$ref":"#\/components\/schemas\/BlockMatch"},{"$ref":"#\/components\/schemas\/BlockSequence"},{"$ref":"#\/components\/schemas\/BlockNumericAnswer"},{"$ref":"#\/components\/schemas\/BlockErrorDetective"},{"$ref":"#\/components\/schemas\/BlockConfidence"},{"$ref":"#\/components\/schemas\/BlockPrediction"},{"$ref":"#\/components\/schemas\/BlockTextHighlight"},{"$ref":"#\/components\/schemas\/BlockWordsearch"},{"$ref":"#\/components\/schemas\/BlockMemory"},{"$ref":"#\/components\/schemas\/BlockSort"},{"$ref":"#\/components\/schemas\/BlockHangman"},{"$ref":"#\/components\/schemas\/BlockAdventure"},{"$ref":"#\/components\/schemas\/BlockPuzzle"},{"$ref":"#\/components\/schemas\/BlockFlashcards"},{"$ref":"#\/components\/schemas\/BlockTimeline"},{"$ref":"#\/components\/schemas\/BlockBranch"},{"$ref":"#\/components\/schemas\/BlockReflection"},{"$ref":"#\/components\/schemas\/BlockStepReveal"},{"$ref":"#\/components\/schemas\/BlockCheckpoint"},{"$ref":"#\/components\/schemas\/BlockSourceCompare"},{"$ref":"#\/components\/schemas\/BlockArgumentBuilder"},{"$ref":"#\/components\/schemas\/BlockBeforeAfter"},{"$ref":"#\/components\/schemas\/BlockScratchpad"},{"$ref":"#\/components\/schemas\/BlockVisualExplanation"},{"$ref":"#\/components\/schemas\/BlockReading"},{"$ref":"#\/components\/schemas\/BlockComic"},{"$ref":"#\/components\/schemas\/BlockDiagram"},{"$ref":"#\/components\/schemas\/BlockPoster"},{"$ref":"#\/components\/schemas\/BlockVisualNotes"},{"$ref":"#\/components\/schemas\/BlockInteractiveLab"}],"discriminator":{"propertyName":"type"}},"Meta":{"type":"object","description":"Metadatos del objeto.","required":["title","author","objective"],"properties":{"title":{"type":"string","minLength":3,"maxLength":180},"author":{"type":"string","maxLength":160},"objective":{"type":"string","minLength":10,"maxLength":500},"description":{"type":"string","maxLength":500},"subject":{"type":"string","maxLength":80,"example":"Ciencias naturales"},"min_age":{"type":"integer","minimum":3,"maximum":18},"max_age":{"type":"integer","minimum":3,"maximum":99},"language":{"type":"string","example":"es"},"tags":{"type":"array","items":{"type":"string"},"maxItems":10},"alignment":{"type":"object","description":"Alineación curricular opcional.","additionalProperties":true}}},"LearningObjectDocument":{"type":"object","description":"Documento lumbre-lo\/1.0.","required":["format","meta","blocks"],"properties":{"format":{"type":"string","enum":["lumbre-lo\/1.0"]},"meta":{"$ref":"#\/components\/schemas\/Meta"},"blocks":{"type":"array","items":{"$ref":"#\/components\/schemas\/Block"},"minItems":1}}},"Error":{"type":"object","description":"Error máquina-legible. Códigos: INVALID_INPUT, VALIDATION_ERROR, NOT_FOUND, PERMISSION_DENIED, RATE_LIMITED, UNKNOWN_BLOCK, SERVER_ERROR.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"VALIDATION_ERROR"},"message":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}}}}}}}},"CreateObjectRequest":{"type":"object","required":["object"],"properties":{"object":{"$ref":"#\/components\/schemas\/LearningObjectDocument"},"visibility":{"type":"string","enum":["PUBLIC","UNLISTED"],"default":"PUBLIC"},"slug":{"type":"string","description":"Opcional; por defecto se deriva del título."},"draft":{"type":"boolean","description":"true → guarda como borrador (state DRAFT) y devuelve preview_url en vez de publicar.","default":false},"assets":{"type":"object","description":"Mapa ruta→base64 (\"assets\/diagrama.png\": \"…\").","additionalProperties":{"type":"string"}},"cover":{"type":"string","description":"PORTADA de la ficha (no un bloque del contenido): base64 de una imagen (admite prefijo data:image\/…;base64,) o una ruta \"assets\/…\" incluida en \"assets\". jpg\/png\/webp\/gif, ≤15 MB. Si se omite y el objeto ya tenía portada, SE CONSERVA; solo se dibuja una de identidad si no tiene ninguna."},"route":{"type":"string","description":"Opcional: slug o id de la ruta donde colocar el objeto."},"stage":{"type":"string","description":"Opcional: nombre (se crea si falta) o id de etapa."},"note":{"type":"string","maxLength":500}}},"CreateObjectResponse":{"type":"object","properties":{"ok":{"type":"boolean"},"object_id":{"type":"integer"},"slug":{"type":"string"},"url":{"type":"string"},"preview_url":{"type":"string","description":"Sólo con draft:true (visible para el dueño\/personal)."},"version":{"type":"integer"},"state":{"type":"string","example":"PUBLISHED"},"assets":{"type":"integer"},"minutes":{"type":"integer"},"visibility":{"type":"string"},"cover":{"type":"object","description":"Portada efectivamente instalada tras publicar.","properties":{"url":{"type":"string","example":"\/media\/object\/123"},"source":{"type":"string","enum":["bundle","asset","kept","generated"],"description":"bundle\/asset = la que mandaste; kept = la que ya tenía la ficha; generated = dibujo de identidad."},"how_to_change":{"type":"string"}}},"route":{"type":"object","nullable":true}}},"PatchObjectRequest":{"type":"object","properties":{"ops":{"type":"array","description":"Operaciones en orden: add_video, add_block, replace_block, remove_block, set_meta, add_image, set_cover. Omítelas solo si mandas \"cover\".","items":{"type":"object","description":"{\"op\":\"add_video\",\"query\":\"…\"} · {\"op\":\"set_cover\",\"src\":\"assets\/…\"} · …","additionalProperties":true}},"assets":{"type":"object","description":"SOLO archivos nuevos (add_image o portada): ruta→base64.","additionalProperties":{"type":"string"}},"cover":{"type":"string","description":"Nueva PORTADA: base64 de la imagen o ruta \"assets\/…\" de un recurso del objeto (publicado o enviado aquí). Equivale a la op set_cover."},"dry_run":{"type":"boolean","description":"true → aplica y valida sin publicar; devuelve outline y cover_will_be.","default":false}}},"PatchObjectResponse":{"type":"object","properties":{"ok":{"type":"boolean"},"object_id":{"type":"integer"},"slug":{"type":"string"},"url":{"type":"string"},"version":{"type":"integer"},"assets_carried":{"type":"integer","description":"Recursos reempaquetados + nuevos instalados."},"applied":{"type":"array","items":{"type":"string"}},"notes":{"type":"array","items":{"type":"string"},"description":"Avisos no bloqueantes; aquí cae el cover_error si la portada no pudo instalarse."},"outline":{"type":"array","items":{"type":"object"}},"cover":{"type":"object","description":"Portada resultante tras el patch.","properties":{"url":{"type":"string"},"source":{"type":"string","enum":["bundle","asset","kept","generated"],"description":"\"\" si el dry_run no instala nada."},"has_cover":{"type":"boolean"},"error":{"type":"string","description":"Sólo si la imagen designada no estaba entre los recursos del objeto."}}}}},"ValidateRequest":{"type":"object","required":["object"],"properties":{"object":{"$ref":"#\/components\/schemas\/LearningObjectDocument"},"assets":{"type":"object","description":"Opcional: mapa ruta→base64 para comprobar también la presencia de recursos.","additionalProperties":{"type":"string"}},"cover":{"type":"string","description":"Opcional: portada en base64 o ruta \"assets\/…\"; se valida aquí para que el error salga en el ciclo validar→corregir→publicar (campo errors[].field = \"cover\")."}}},"ValidateResponse":{"type":"object","properties":{"ok":{"type":"boolean"},"valid":{"type":"boolean"},"errors":{"type":"array","items":{"type":"object","properties":{"block":{"type":["integer","null"]},"field":{"type":["string","null"]},"message":{"type":"string"}}}},"warnings":{"type":"array","items":{"type":"object"}},"blocks":{"type":"integer","description":"Número de bloques del documento."}}},"ObjectList":{"type":"object","properties":{"ok":{"type":"boolean"},"count":{"type":"integer"},"objects":{"type":"array","items":{"type":"object","properties":{"slug":{"type":"string"},"title":{"type":"string"},"state":{"type":"string"},"visibility":{"type":"string"},"url":{"type":"string"}}}}}},"ObjectDetail":{"type":"object","properties":{"ok":{"type":"boolean"},"object_id":{"type":"integer"},"slug":{"type":"string"},"version":{"type":"integer"},"state":{"type":"string"},"visibility":{"type":"string"},"object":{"$ref":"#\/components\/schemas\/LearningObjectDocument"},"assets":{"type":"array","items":{"type":"object"}},"cover":{"type":"object","description":"Portada actual: {url, source, has_cover}.","properties":{"url":{"type":"string"},"source":{"type":"string"},"has_cover":{"type":"boolean"}}},"cover_b64":{"type":"string","description":"Solo con ?assets=1: la portada actual en base64 (JPG normalizado), para reenviarla o editarla."}}},"CreateBookRequest":{"type":"object","required":["title"],"description":"Un libro de lectura. Da el contenido en UNA de estas formas: \"text\" (plano\/markdown), \"chapters\" ([{title, text|blocks}]) o \"file\" ({name, content_base64}). kind: book (libro largo), short (lectura corta), comic (PDF de lectura directa).","properties":{"title":{"type":"string","minLength":1,"maxLength":200},"author":{"type":"string","maxLength":160,"description":"Opcional; por defecto «Autor no indicado»."},"description":{"type":"string","maxLength":4000},"kind":{"type":"string","enum":["book","short","comic"],"default":"book"},"content_type":{"type":"string","enum":["book","manual","guide","document","short_read","internal"],"description":"Alternativa explícita a kind."},"text":{"type":"string","description":"Texto plano o markdown; los capítulos se parten por encabezados y líneas en blanco."},"chapters":{"type":"array","items":{"type":"object","description":"Capítulo: {title, text | blocks:[{type,text}]}","additionalProperties":true}},"file":{"type":"object","description":"Documento original {name, content_base64} (txt\/pdf\/docx\/epub; content_base64 admite prefijo data-URI).","properties":{"name":{"type":"string"},"content_base64":{"type":"string"}}},"direct_pdf":{"type":"boolean","description":"true: conserva el PDF como archivo de lectura directa (cómic\/escaneado) sin extraer texto."},"cover":{"type":"string","description":"PORTADA del libro en base64 (jpg\/png\/webp\/gif, admite data-URI). Se normaliza a JPG y se sirve en \/media\/book\/{id}."},"slug":{"type":"string","maxLength":190,"description":"Opcional; idempotencia (re-POST con el mismo slug EDITA, no duplica)."},"visibility":{"type":"string","enum":["PRIVATE","UNLISTED","PUBLIC"],"default":"PRIVATE"},"copyright_confirm":{"type":"string","description":"\"1\" obligatorio para visibility=PUBLIC."},"category":{"type":"string","description":"Slug o nombre de la temática."},"tags":{"type":"array","items":{"type":"string"},"description":"Hasta 8 etiquetas (también acepta string csv)."},"min_age":{"type":"integer"},"max_age":{"type":"integer"},"difficulty":{"type":"string","enum":["initial","intermediate","advanced"]},"language":{"type":"string","maxLength":12,"example":"es"},"embed_allowed":{"type":"boolean"},"source_url":{"type":"string","maxLength":500},"rights_note":{"type":"string","maxLength":1000}}},"BookResult":{"type":"object","description":"Resultado de crear\/editar un libro.","properties":{"ok":{"type":"boolean"},"id":{"type":"integer"},"slug":{"type":"string"},"url":{"type":"string"},"kind":{"type":"string","enum":["book","short","comic"]},"content_type":{"type":"string"},"read_mode":{"type":"string","enum":["text","pdf"]},"status":{"type":"string","example":"READY"},"visibility":{"type":"string"},"words":{"type":"integer"},"chapters":{"type":"integer"},"minutes":{"type":"integer"},"page_count":{"type":"integer"},"quality":{"type":"string","enum":["green","yellow","red"]},"cover":{"type":"string","description":"URL de la portada (\"\" si no tiene)."},"has_cover":{"type":"boolean"},"note":{"type":"string"},"warnings":{"type":"array","items":{"type":"string"},"description":"Avisos no bloqueantes para el agente (p. ej. \"kind\" invalido clasificado como book, portada ilegible omitida). El libro SI se guardo."}}},"BookList":{"type":"object","properties":{"ok":{"type":"boolean"},"count":{"type":"integer"},"books":{"type":"array","items":{"type":"object","properties":{"slug":{"type":"string"},"title":{"type":"string"},"author":{"type":"string"},"kind":{"type":"string"},"content_type":{"type":"string"},"read_mode":{"type":"string"},"status":{"type":"string"},"visibility":{"type":"string"},"words":{"type":"integer"},"chapters":{"type":"integer"},"minutes":{"type":"integer"},"url":{"type":"string"},"read":{"type":"string"}}}}}},"BookDetail":{"type":"object","properties":{"ok":{"type":"boolean"},"id":{"type":"integer"},"slug":{"type":"string"},"url":{"type":"string"},"title":{"type":"string"},"author":{"type":"string"},"description":{"type":"string"},"kind":{"type":"string"},"content_type":{"type":"string"},"read_mode":{"type":"string"},"status":{"type":"string"},"visibility":{"type":"string"},"words":{"type":"integer"},"chapters":{"type":"integer"},"minutes":{"type":"integer"},"category":{"type":"string","nullable":true},"tags":{"type":"array","items":{"type":"string"}},"cover":{"type":"string"},"has_cover":{"type":"boolean"},"file":{"type":"object","nullable":true,"description":"Info del documento original si el libro se importó desde un archivo."},"chapters_content":{"type":"array","description":"Sólo con ?content=1: capítulos con sus bloques {title, position, blocks:[{type,text}]}."},"cover_b64":{"type":"string","description":"Sólo con ?cover=1: la portada actual en base64."}}},"ValidateBookResponse":{"type":"object","properties":{"ok":{"type":"boolean"},"valid":{"type":"boolean"},"pdf_direct":{"type":"boolean"},"quality":{"type":"string","enum":["green","yellow","red"]},"quality_label":{"type":"string"},"words":{"type":"integer"},"chapters":{"type":"integer"},"notes":{"type":"array","items":{"type":"string"}},"preview":{"type":"string"},"errors":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}},"BlockTypeList":{"type":"object","properties":{"ok":{"type":"boolean"},"catalog_version":{"type":"integer"},"count":{"type":"integer"},"block_types":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"label":{"type":"string"},"version":{"type":"integer"},"description":{"type":"string"},"scored":{"type":"boolean"},"schema_url":{"type":"string"}}}}}},"BlockTypeDetail":{"type":"object","properties":{"ok":{"type":"boolean"},"type":{"type":"string"},"label":{"type":"string"},"version":{"type":"integer"},"scored":{"type":"boolean"},"description":{"type":"string"},"when_use":{"type":"string"},"when_not":{"type":"string"},"spec":{"type":"string"},"schema":{"type":"object"},"example":{"type":"object"},"response":{"type":"object","nullable":true}}},"Taxonomies":{"type":"object","properties":{"ok":{"type":"boolean"},"format":{"type":"string"},"subjects":{"type":"array","items":{"type":"string"}},"levels":{"type":"array","items":{"type":"string"}},"education_levels":{"type":"array","items":{"type":"string"}},"difficulties":{"type":"array","items":{"type":"string"}},"route_visibilities":{"type":"array","items":{"type":"string"}},"route_types":{"type":"array","items":{"type":"string"}},"object_visibility":{"type":"array","items":{"type":"string"}}}},"StructureList":{"type":"object","properties":{"ok":{"type":"boolean"},"count":{"type":"integer"},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"slug":{"type":"string"},"name":{"type":"string"},"url":{"type":"string"}}}}}},"LoResultRequest":{"type":"object","description":"Lo envía el runtime del visor con la sesión del estudiante (no clave de agente).","properties":{"object_id":{"type":"integer"},"block":{"type":"string"},"is_correct":{"type":"boolean"},"score":{"type":"number"},"response":{"type":"object","additionalProperties":true},"time_ms":{"type":"integer"},"attempts":{"type":"integer"}}},"LabDocument":{"type":"object","description":"Definición lumbre-lab\/1.0: documento JSON declarativo, sin JS propio. Catálogo de componentes en GET \/api\/v1\/labs\/schema.","required":["format","meta","activities"],"properties":{"format":{"type":"string","enum":["lumbre-lab\/1.0"]},"meta":{"type":"object","required":["title","level"],"properties":{"uid":{"type":"string","description":"UUID canónico; si falta se genera. Es la identidad global entre instalaciones."},"slug":{"type":"string","maxLength":190},"title":{"type":"string","minLength":3,"maxLength":180},"description":{"type":"string","maxLength":500},"level":{"type":"string","enum":["primaria","secundaria","preparatoria"]},"subject":{"type":"string","maxLength":80},"grades":{"type":"array","items":{"type":"integer"}},"min_age":{"type":"integer","minimum":3,"maximum":18},"max_age":{"type":"integer","minimum":3,"maximum":99},"difficulty":{"type":"string","enum":["basico","intermedio","avanzado"]},"version":{"type":"string","example":"1.0.0","description":"semver MAJOR.MINOR.PATCH."},"author":{"type":"string","maxLength":160},"relations":{"type":"array","description":"Vínculos con OA u otros labs (se registran en content_relations al ingerir).","items":{"type":"object","properties":{"type":{"type":"string","enum":["oa","lab"]},"uid":{"type":"string"},"relation":{"type":"string","enum":["uses_lab","explains","practices","teaches","extends","requires","related","next"]},"activity":{"type":"string","description":"id de actividad para deep link ?activity=."}}}}}},"activities":{"type":"array","minItems":1,"items":{"type":"object","required":["id","title","components"],"properties":{"id":{"type":"string","description":"ESTABLE (slug-like): el progreso del alumno se guarda por esta referencia y sobrevive a las actualizaciones."},"title":{"type":"string","maxLength":180},"instructions":{"type":"string","maxLength":1000},"difficulty":{"type":"string","enum":["basico","intermedio","avanzado"]},"guide":{"type":"array","description":"Bloques explicativos: reutilizan el vocabulario OA (heading, text, mcq, …).","items":{"type":"object","additionalProperties":true}},"components":{"type":"array","minItems":1,"items":{"type":"object","required":["component"],"properties":{"component":{"type":"string","description":"Nombre del catálogo cerrado (GET \/api\/v1\/labs\/schema)."},"params":{"type":"object","description":"Parámetros validados por ComponentCatalog.","additionalProperties":true}}}},"feedback":{"type":"string","maxLength":1000},"hints":{"type":"array","items":{"type":"string"},"maxItems":6},"minutes":{"type":"integer","minimum":1,"maximum":240}}}}}},"CreateLabRequest":{"type":"object","required":["lab"],"properties":{"lab":{"$ref":"#\/components\/schemas\/LabDocument"},"visibility":{"type":"string","enum":["PUBLIC","UNLISTED","CIRCLE","PRIVATE"],"default":"PUBLIC"},"draft":{"type":"boolean","description":"true → guarda como borrador (state draft) sin publicar.","default":false},"assets":{"type":"object","description":"Mapa ruta→base64 (\"assets\/diagrama.png\": \"…\"), sólo bajo assets\/.","additionalProperties":{"type":"string"}}}},"CreateLabResponse":{"type":"object","properties":{"ok":{"type":"boolean"},"lab_id":{"type":"integer"},"uid":{"type":"string"},"slug":{"type":"string"},"version":{"type":"string"},"skipped":{"type":"boolean","description":"true si el checksum no cambió: no se creó versión nueva."},"created":{"type":"boolean"},"url":{"type":"string"}}},"LabList":{"type":"object","properties":{"ok":{"type":"boolean"},"total":{"type":"integer"},"labs":{"type":"array","items":{"type":"object","properties":{"uid":{"type":"string"},"slug":{"type":"string"},"title":{"type":"string"},"level":{"type":"string"},"subject":{"type":"string"},"version":{"type":"string"},"url":{"type":"string"},"api":{"type":"string"}}}}}},"LabResultRequest":{"type":"object","description":"Lo envía el runtime del laboratorio con la sesión del estudiante (no clave de agente).","required":["lab_id","activity_ref"],"properties":{"lab_id":{"type":"integer"},"activity_ref":{"type":"string","maxLength":64,"description":"id estable de la actividad."},"params":{"type":"object","additionalProperties":true},"score":{"type":"number"},"hints_used":{"type":"integer"},"attempts":{"type":"integer"},"seconds":{"type":"integer"}}}}}}