{"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:\/\/filosofia.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\/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:\/\/filosofia.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 y valida las palabras trazadas.","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}},"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},"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},"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\/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"}],"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"}}}}}}