LABS · JULIÁN LÓPEZ JIMÉNEZ · RECURSO GRATUITO

De una idea a una especificación comprobable

Cinco retos: aclara una petición, define criterios, busca evidencia, implementa una regla y encuentra el fallo de una respuesta preparada. Python real, sin cuenta ni tarjeta.

Qué necesitas: Funciones y diccionarios de Python. Ordenador con teclado recomendado; navegador actualizado con JavaScript/WebAssembly y conexión inicial para CPython. No necesitas cuenta de IA: los ejemplos son preparados, no llamadas a un modelo.

Tu resultado: Una pequeña regla comprobada y un criterio de aceptación con evidencia.

Reto 1 · Detecta decisiones pendientes

SDD empieza acordando qué debe pasar. El agente puede producir código convincente aunque falten decisiones. En peticion.md hay una petición imposible de garantizar con memoria volátil. No elijas tú una base de datos ni permisos por intuición: registra preguntas y un fuera de alcance aceptado. En el playground representarás cuatro decisiones en un diccionario; esto verifica campos pendientes, no la calidad de una conversación. dudas(peticion) devuelve en este orden los nombres actor, permiso, limite, error cuyo valor falte o sea vacío. No modifica la petición. Se recibe un diccionario de decisiones; claves adicionales se ignoran.

import copy, json

def dudas(peticion):
    # Predice un caso válido y otro rechazado antes de implementar.
    pass
Necesito una pista

Recorre las cuatro claves del contrato, no las claves que hayan llegado. → Usa get para distinguir lo presente con contenido. → Añade el nombre de cada decisión vacía a una lista.

Ver solución de este reto
import copy, json

def dudas(peticion):
    preguntas = []
    for campo in ('actor', 'permiso', 'limite', 'error'):
        if not peticion.get(campo):
            preguntas.append(campo)
    return preguntas

Preguntar es un resultado válido. La función detecta huecos estructurales; tú debes comprobar que las respuestas sean concretas y no se contradigan.

Archivo de apoyo: apoyo.py

Guárdalo con este nombre junto a main.py de este reto. No lo pegues dentro de main.py.

import copy, json

Archivo de apoyo: peticion.md

Guárdalo con este nombre junto a main.py de este reto. No lo pegues dentro de main.py.

Petición inicial ficticia: «Queremos cerrar incidencias rápido, con prioridades y permisos; que no se pierda nada».
Preguntas: ¿quién cierra?, ¿qué significa cerrar?, ¿qué se conserva?, ¿qué ocurre ante una versión antigua?
Acuerdo del laboratorio: estado volátil en memoria, no persistencia. Lector consulta; operador crea/cierra/reabre.
La promesa inicial «no se pierda nada» NO queda satisfecha: cliente acepta posponer persistencia y auditoría.
No inventes esta aceptación en un proyecto real. El curso entrega un prototipo educativo.

Archivo de apoyo: spec.md

Guárdalo con este nombre junto a main.py de este reto. No lo pegues dentro de main.py.

RF-G1: título recortado de 1–80 caracteres y prioridad baja/media/alta; rechaza campos extra sin modificar entradas.
Esta guía solo enseña aclaraciones, criterios y validación; no contiene el proyecto del curso.

Comprueba: Escribe tres preguntas propias para «que no se pierda nada» y separa requisito de suposición.

Reto 2 · Escribe un criterio observable

Un criterio de aceptación describe un contexto, una acción y un resultado observable. Ejemplo RF-03: dada una incidencia abierta v1, cuando cierro con resolución vacía, recibo un rechazo y sigue abierta v1. No equivale a «funciona bien». Separa requisito, decisión técnica y ejemplo. Aquí solo comprobarás la forma del criterio; la rúbrica del cuaderno evalúa precisión y relevancia. criterio(regla) devuelve bool: id, dado, cuando y entonces son cadenas con contenido tras strip. Ignora claves extra y no modifica. No afirma que el criterio sea semánticamente correcto.

import copy, json

def criterio(regla):
    # Predice un caso válido y otro rechazado antes de implementar.
    pass
Necesito una pista

Comprueba las cuatro partes. → isinstance y strip evitan números y espacios. → Devuelve un booleano, no la explicación impresa.

Ver solución de este reto
import copy, json

def criterio(regla):
    return all(isinstance(regla.get(k), str) and regla[k].strip()
               for k in ('id', 'dado', 'cuando', 'entonces'))

La estructura evita olvidos, pero un resultado como «bien» sigue siendo malo. Contrasta cada criterio con un caso nuevo y con su contrario.

Archivo de apoyo: apoyo.py

Guárdalo con este nombre junto a main.py de este reto. No lo pegues dentro de main.py.

import copy, json

Archivo de apoyo: peticion.md

Guárdalo con este nombre junto a main.py de este reto. No lo pegues dentro de main.py.

Petición inicial ficticia: «Queremos cerrar incidencias rápido, con prioridades y permisos; que no se pierda nada».
Preguntas: ¿quién cierra?, ¿qué significa cerrar?, ¿qué se conserva?, ¿qué ocurre ante una versión antigua?
Acuerdo del laboratorio: estado volátil en memoria, no persistencia. Lector consulta; operador crea/cierra/reabre.
La promesa inicial «no se pierda nada» NO queda satisfecha: cliente acepta posponer persistencia y auditoría.
No inventes esta aceptación en un proyecto real. El curso entrega un prototipo educativo.

Archivo de apoyo: spec.md

Guárdalo con este nombre junto a main.py de este reto. No lo pegues dentro de main.py.

RF-G1: título recortado de 1–80 caracteres y prioridad baja/media/alta; rechaza campos extra sin modificar entradas.
Esta guía solo enseña aclaraciones, criterios y validación; no contiene el proyecto del curso.

Comprueba: Redacta el caso de versión antigua con resultado y estado posterior explícitos.

Reto 3 · Encuentra el requisito sin evidencia

Una matriz enlaza requisito y pruebas concretas. Contar pruebas no basta: diez pruebas felices pueden olvidar el rechazo sin escritura. En este modelo una lista no vacía significa que has registrado nombres de evidencias; no certifica que se ejecutaran ni que sean buenas. El taller final exige adjuntar ejecución y revisión. cobertura(requisitos,evidencias) devuelve IDs únicos y ordenados de requisitos que carecen de una lista no vacía de evidencias. Una cadena, booleano o diccionario no cuenta como lista. Evidencias de otros requisitos no cubren estos. No modifica entradas.

import copy, json

def cobertura(requisitos, evidencias):
    # Predice un caso válido y otro rechazado antes de implementar.
    pass
Necesito una pista

Convierte requisitos en un conjunto. → Comprueba con isinstance que es una lista y que no está vacía. → Resta lo cubierto y ordena la salida.

Ver solución de este reto
import copy, json

def cobertura(requisitos, evidencias):
    return sorted(set(requisitos) - {r for r, tests in evidencias.items() if isinstance(tests, list) and tests})

El mapa aporta trazabilidad, no calidad ni ejecución. Revisa también el contenido del test y si distingue una implementación defectuosa.

Archivo de apoyo: apoyo.py

Guárdalo con este nombre junto a main.py de este reto. No lo pegues dentro de main.py.

import copy, json

Archivo de apoyo: peticion.md

Guárdalo con este nombre junto a main.py de este reto. No lo pegues dentro de main.py.

Petición inicial ficticia: «Queremos cerrar incidencias rápido, con prioridades y permisos; que no se pierda nada».
Preguntas: ¿quién cierra?, ¿qué significa cerrar?, ¿qué se conserva?, ¿qué ocurre ante una versión antigua?
Acuerdo del laboratorio: estado volátil en memoria, no persistencia. Lector consulta; operador crea/cierra/reabre.
La promesa inicial «no se pierda nada» NO queda satisfecha: cliente acepta posponer persistencia y auditoría.
No inventes esta aceptación en un proyecto real. El curso entrega un prototipo educativo.

Archivo de apoyo: spec.md

Guárdalo con este nombre junto a main.py de este reto. No lo pegues dentro de main.py.

RF-G1: título recortado de 1–80 caracteres y prioridad baja/media/alta; rechaza campos extra sin modificar entradas.
Esta guía solo enseña aclaraciones, criterios y validación; no contiene el proyecto del curso.

Comprueba: Añade una prueba duplicada y explica por qué no resuelve otro requisito ausente.

Reto 4 · Implementa una regla pequeña

Lee RF-01 en spec.md. El contrato determina reglas antes de elegir implementación. Recortar texto es una decisión acordada; aceptar una prioridad desconocida por comodidad sería cambiar el producto. Rechaza entradas antes de reservar IDs y devuelve una estructura nueva. validar(datos): solo diccionario exacto con titulo y prioridad; título cadena recortada de 1–80 caracteres Unicode, prioridad baja/media/alta. ValueError ante cualquier incumplimiento. Devuelve copia limpia sin escribir ni imprimir.

import copy, json

def validar(datos):
    # Predice un caso válido y otro rechazado antes de implementar.
    pass
Necesito una pista

Valida tipo y conjunto de claves antes de acceder. → Recorta solo cadenas; cuenta después. → Construye otro diccionario al devolver.

Ver solución de este reto
import copy, json

def validar(datos):
    if type(datos) is not dict or set(datos) != {'titulo', 'prioridad'}:
        raise ValueError('campos')
    titulo = datos['titulo']
    if not isinstance(titulo, str) or not 1 <= len(titulo.strip()) <= 80:
        raise ValueError('titulo')
    if datos['prioridad'] not in ('baja', 'media', 'alta'):
        raise ValueError('prioridad')
    return {'titulo': titulo.strip(), 'prioridad': datos['prioridad']}

La validación es una función pura. Rechazar extras evita que el cliente elija estado o versión sin autorización del contrato.

Archivo de apoyo: apoyo.py

Guárdalo con este nombre junto a main.py de este reto. No lo pegues dentro de main.py.

import copy, json

Archivo de apoyo: peticion.md

Guárdalo con este nombre junto a main.py de este reto. No lo pegues dentro de main.py.

Petición inicial ficticia: «Queremos cerrar incidencias rápido, con prioridades y permisos; que no se pierda nada».
Preguntas: ¿quién cierra?, ¿qué significa cerrar?, ¿qué se conserva?, ¿qué ocurre ante una versión antigua?
Acuerdo del laboratorio: estado volátil en memoria, no persistencia. Lector consulta; operador crea/cierra/reabre.
La promesa inicial «no se pierda nada» NO queda satisfecha: cliente acepta posponer persistencia y auditoría.
No inventes esta aceptación en un proyecto real. El curso entrega un prototipo educativo.

Archivo de apoyo: spec.md

Guárdalo con este nombre junto a main.py de este reto. No lo pegues dentro de main.py.

RF-G1: título recortado de 1–80 caracteres y prioridad baja/media/alta; rechaza campos extra sin modificar entradas.
Esta guía solo enseña aclaraciones, criterios y validación; no contiene el proyecto del curso.

Comprueba: Prueba 80 y 81 letras con acento. No confundas caracteres con bytes HTTP.

Reto 5 · Desmonta una respuesta convincente

La candidata acepta un título compuesto solo por espacios. Tu tarea es aportar evidencia: una entrada concreta aceptada por el defecto y rechazada por RF-01. No necesitas pedir otra respuesta a un modelo para demostrarlo. El artefacto «respuesta del agente» del curso es preparado, no una llamada real. contraejemplo(candidata) devuelve un diccionario titulo/prioridad válido en forma, prioridad media, título de espacios. La candidata lo acepta y validar lo rechaza. No llama a servicios ni modifica la candidata.

import copy, json
from apoyo import validar

def contraejemplo(candidata):
    # Predice un caso válido y otro rechazado antes de implementar.
    pass
Necesito una pista

Una cadena vacía ya la rechaza la candidata. → Los espacios tienen longitud y son truthy. → Comprueba las dos implementaciones con la misma entrada.

Ver solución de este reto
import copy, json
from apoyo import validar

def contraejemplo(candidata):
    """Devuelve una entrada que expone el defecto de la candidata, sin modificarla."""
    return {'titulo': '   ', 'prioridad': 'media'}

Un contraejemplo vale más que «creo que está mal». La corrección debe justificarse contra el acuerdo y no contra preferencias del revisor.

Archivo de apoyo: apoyo.py

Guárdalo con este nombre junto a main.py de este reto. No lo pegues dentro de main.py.

import copy, json

def validar(datos):
    if type(datos) is not dict or set(datos) != {'titulo', 'prioridad'}:
        raise ValueError('campos')
    titulo = datos['titulo']
    if not isinstance(titulo, str) or not 1 <= len(titulo.strip()) <= 80:
        raise ValueError('titulo')
    if datos['prioridad'] not in ('baja', 'media', 'alta'):
        raise ValueError('prioridad')
    return {'titulo': titulo.strip(), 'prioridad': datos['prioridad']}

Archivo de apoyo: peticion.md

Guárdalo con este nombre junto a main.py de este reto. No lo pegues dentro de main.py.

Petición inicial ficticia: «Queremos cerrar incidencias rápido, con prioridades y permisos; que no se pierda nada».
Preguntas: ¿quién cierra?, ¿qué significa cerrar?, ¿qué se conserva?, ¿qué ocurre ante una versión antigua?
Acuerdo del laboratorio: estado volátil en memoria, no persistencia. Lector consulta; operador crea/cierra/reabre.
La promesa inicial «no se pierda nada» NO queda satisfecha: cliente acepta posponer persistencia y auditoría.
No inventes esta aceptación en un proyecto real. El curso entrega un prototipo educativo.

Archivo de apoyo: spec.md

Guárdalo con este nombre junto a main.py de este reto. No lo pegues dentro de main.py.

RF-G1: título recortado de 1–80 caracteres y prioridad baja/media/alta; rechaza campos extra sin modificar entradas.
Esta guía solo enseña aclaraciones, criterios y validación; no contiene el proyecto del curso.

Comprueba: Busca otro defecto: longitud 81 aceptada. Redacta su requisito y evidencia.

Tu turno

Aporta otro contraejemplo: título demasiado largo o campo extra. Explica qué requisito incumple y qué prueba lo muestra.

Ver solución y explicación
import copy, json
from apoyo import validar

def contraejemplo(candidata):
    """Devuelve una entrada que expone el defecto de la candidata, sin modificarla."""
    return {'titulo': '   ', 'prioridad': 'media'}

No hay un agente ejecutándose aquí. Las revisiones de lenguaje natural usan rúbrica humana; el corrector valida estructura y comportamiento acotados. Comprobar no avanza ni concede XP. Sin cuenta descarga código antes de salir; con cuenta verificada el guardado es manual y no incluye resultados. La guía incluye apoyos por reto para Python 3.12+.

Explícalo con tus palabras

Repite con datos distintos antes de marcarlo como practicado. Esta es una autoevaluación, no una certificación.

Si quieres continuar

El curso continúa con una API de incidencias, cambios de requisitos, pruebas de código defectuoso y taller descargable con agente real. El recurso y sus soluciones permanecen gratuitos.

Ver el curso Tu primer proyecto con IA: de la especificación al código y su temario

Fuente: Recurso original de Labs. Puedes guardar este HTML, abrirlo sin conexión o imprimirlo como PDF desde tu navegador. No guarda tus respuestas ni el progreso de tu cuenta.