diff --git a/docs/propuesta-lizette/01-requisitos-backend.md b/docs/propuesta-lizette/01-requisitos-backend.md
new file mode 100644
index 0000000..aa68e20
--- /dev/null
+++ b/docs/propuesta-lizette/01-requisitos-backend.md
@@ -0,0 +1,83 @@
+# Requisitos del backend
+
+Este documento recoge los requisitos del **backend** de Plano3D: el servidor (API
+REST, cuentas, historial y base de datos) y el motor que convierte el plano técnico
+2D en una pieza 3D. La interfaz y el visor 3D son del frontend y no se incluyen aquí.
+
+Cada requisito tiene un identificador para poder citarlo desde el resto de la
+documentación, los *issues* y las pruebas. La prioridad indica su importancia para
+la primera versión: **Alta** (imprescindible), **Media** (deseable) o **Baja**
+(opcional).
+
+## 1. Requisitos funcionales
+
+### Cuentas y proyectos
+
+| ID | Nombre | Descripción | Prioridad |
+| --- | --- | --- | --- |
+| RF-01 | Registro de usuario | El usuario crea una cuenta con correo electrónico y contraseña. | Alta |
+| RF-02 | Inicio de sesión | El usuario se autentica y recibe un token que envía en el resto de peticiones. | Alta |
+| RF-03 | Gestión de proyectos | El usuario crea, lista, consulta y elimina sus proyectos. Solo ve los suyos. | Alta |
+| RF-04 | Subida del plano | La API recibe un plano (DXF; DWG y PDF vectorial en la ampliación) asociado a un proyecto. | Alta |
+| RF-05 | Validación del archivo | Antes de procesar se comprueban la extensión, el tamaño máximo y que el archivo se puede abrir. Si no, se rechaza explicando el motivo. | Alta |
+
+### Conversión (motor)
+
+| ID | Nombre | Descripción | Prioridad |
+| --- | --- | --- | --- |
+| RF-06 | Lectura del plano | Se extraen las entidades geométricas (LINE, ARC, CIRCLE, LWPOLYLINE con *bulge*, POLYLINE), se separan las líneas visibles de las ocultas y se ignoran el marco, el cajetín, los textos y las cotas. | Alta |
+| RF-07 | Unidades y escala | Se pasa todo a mm según `$INSUNITS` y se aplica la escala del cajetín, o la que indique el usuario. Si falta, se avisa. | Alta |
+| RF-08 | Vistas | Se agrupan las líneas en vistas y se identifican alzado, planta y perfil. | Alta |
+| RF-09 | Sistema de proyección | Se detecta si el plano sigue el sistema europeo (ISO-E) o el americano (ISO-A), y se avisa si los mezcla. | Alta |
+| RF-10 | Reconstrucción | Se genera el sólido cuyas tres proyecciones reproducen el plano: caras planas, chaflanes, agujeros pasantes y roscas (como agujero y nota). | Alta |
+| RF-11 | Geometría ampliada | Agujeros ciegos, avellanados y cajeados, y piezas de revolución. | Media |
+| RF-12 | Plan B: pieza plana | Si el plano tiene una sola vista, se extruye el contorno exterior con el espesor indicado y se restan los agujeros. | Alta |
+| RF-13 | Verificación del plano | Se proyectan las aristas del sólido y se comparan con el plano, y se comprueban las cotas. Cada diferencia genera un aviso. | Alta |
+| RF-14 | Avisos | Cada problema se devuelve con un código estable, un mensaje comprensible, una gravedad (info, aviso o bloqueante) y la vista afectada. | Alta |
+| RF-15 | Alternativas | Si varias piezas reproducen el plano, se indica cuántas hay y el usuario puede elegir una. | Media |
+| RF-16 | Exportación | La pieza se exporta a STEP (para CAD) y STL (para la vista previa del frontend). | Alta |
+
+### Historial y archivos
+
+| ID | Nombre | Descripción | Prioridad |
+| --- | --- | --- | --- |
+| RF-17 | Conversión en segundo plano | La API responde enseguida con el identificador de la conversión, y el trabajo se hace en segundo plano. | Alta |
+| RF-18 | Estado de la conversión | Cada conversión tiene un estado (pendiente, procesando, completada o error) que el frontend puede consultar. | Alta |
+| RF-19 | Historial | Se guardan en MySQL todas las conversiones de cada proyecto, con sus parámetros, el resultado, los avisos y los archivos generados. | Alta |
+| RF-20 | Repetir una conversión | El usuario puede volver a convertir un plano con otros parámetros (escala, alternativa, espesor) sin subirlo de nuevo. | Media |
+| RF-21 | Descarga de archivos | El usuario descarga el plano original y los STEP/STL de sus proyectos. | Alta |
+
+## 2. Requisitos no funcionales
+
+| ID | Categoría | Descripción |
+| --- | --- | --- |
+| RNF-01 | Seguridad | Las contraseñas se guardan con bcrypt, nunca en texto plano. |
+| RNF-02 | Seguridad | Todos los endpoints, salvo el registro y el inicio de sesión, piden un token JWT válido y con caducidad. |
+| RNF-03 | Seguridad | Un usuario no puede ver ni descargar proyectos ni archivos de otro usuario. |
+| RNF-04 | Seguridad | Las credenciales (base de datos, clave JWT) van en variables de entorno (archivo `.env`) y no se suben al repositorio. |
+| RNF-05 | Fiabilidad | Nunca se entrega una pieza que no reproduce el plano sin avisarlo. Lo que no se soporta se detecta y se avisa. |
+| RNF-06 | Robustez | Un plano erróneo no tumba el servidor ni afecta a otras peticiones: la conversión queda en estado de error, con el motivo. |
+| RNF-07 | Robustez | El tamaño máximo de archivo admitido es de 20 MB. |
+| RNF-08 | Rendimiento | Una pieza del catálogo de pruebas se convierte en menos de 10 segundos. |
+| RNF-09 | Mantenibilidad | El servidor se organiza en capas (rutas, servicios, repositorios y modelos) y el motor en etapas. Todo sigue PEP 8. |
+| RNF-10 | Calidad | Hay pruebas automáticas (`pytest`) del motor, con el catálogo de piezas, y de la API. Pasan antes de cada *pull request*. |
+| RNF-11 | Portabilidad | El backend y la base de datos se levantan con un solo comando (`docker compose up`), y las pruebas se ejecutan en el mismo entorno Docker en cualquier equipo. |
+| RNF-12 | Documentación | La API se documenta automáticamente con OpenAPI/Swagger, y es el contrato con el frontend. |
+| RNF-13 | Trazabilidad | Las operaciones y los errores se registran en un log. |
+
+*Los valores numéricos (20 MB, 10 segundos) son objetivos iniciales y se ajustarán con las pruebas.*
+
+## 3. Restricciones y supuestos
+
+- Piezas mecánicas. No se admiten planos de arquitectura.
+- La reconstrucción completa necesita tres vistas ortogonales alineadas. Con una sola vista se aplica el plan B (RF-12).
+- No se reconstruyen superficies libres ni *splines*: se detectan y se avisa.
+- La lectura de DWG depende de un conversor externo instalado en la imagen Docker.
+
+## 4. Ampliación futura
+
+| ID | Nombre | Descripción | Prioridad |
+| --- | --- | --- | --- |
+| RF-22 | PDF vectorial | Se extraen las líneas de un PDF vectorial y se procesan como un DXF. | Media |
+| RF-23 | DWG | Los DWG se convierten a DXF con un conversor externo. | Media |
+| RF-24 | PDF escaneado y OCR | Se detectan líneas y cotas en planos escaneados. | Baja |
diff --git a/docs/propuesta-lizette/02-tecnologias-backend.md b/docs/propuesta-lizette/02-tecnologias-backend.md
new file mode 100644
index 0000000..a08f33c
--- /dev/null
+++ b/docs/propuesta-lizette/02-tecnologias-backend.md
@@ -0,0 +1,80 @@
+# Tecnologías del backend
+
+Este documento explica qué tecnologías usa el backend (servidor y motor) y por qué
+se han elegido frente a otras alternativas.
+
+## 1. Lenguaje
+
+El backend se desarrolla en **Python 3.12**. CadQuery solo admite de 3.12 a 3.13,
+como fija el `pyproject.toml`.
+
+La razón principal es que Python tiene bibliotecas maduras y gratuitas para leer
+archivos CAD y generar sólidos 3D. En Java o C#, lo equivalente es mucho más
+limitado o necesita licencias comerciales. Además, permite crear APIs rápidamente.
+
+## 2. Bibliotecas
+
+### 2.1. Motor: geometría y modelado
+
+| Tecnología | Para qué se usa | Por qué esta y no otra |
+| --- | --- | --- |
+| **ezdxf** | Leer las entidades, capas, tipos de línea, textos y cabecera de los DXF. | Es la biblioteca de referencia en Python para DXF: admite todas las versiones, está bien documentada y no necesita AutoCAD. |
+| **shapely** | Geometría 2D de las vistas: unir segmentos, formar contornos, calcular áreas y saber si un contorno está dentro de otro. | Resuelve en una línea operaciones que a mano serían largas y darían problemas de tolerancia. |
+| **numpy** | Cálculos con coordenadas y tolerancias. | Estándar en Python, y ya lo usa CadQuery. |
+| **CadQuery** (OpenCASCADE) | Construir los sólidos (celdas, extrusiones, booleanas), proyectar aristas y exportar a STEP y STL. | Usa OpenCASCADE, el mismo núcleo que FreeCAD, con una API en Python más sencilla. pythonOCC ofrece lo mismo con un código más complejo, y FreeCAD es pesado para usarlo dentro de un servidor. |
+| **OR-Tools (CP-SAT)** | Elegir qué celdas forman la pieza para que sus proyecciones coincidan con el plano. | El prototipo prueba todas las combinaciones, y eso no funciona a partir de unas 18 celdas. CP-SAT resuelve el mismo problema como restricciones. |
+
+### 2.2. Servidor: API, seguridad y base de datos
+
+| Tecnología | Para qué se usa | Por qué esta y no otra |
+| --- | --- | --- |
+| **FastAPI** | Crear la API REST con la que se comunica el frontend. | Más moderna y rápida que Flask, valida los datos sola y genera la documentación Swagger sin trabajo extra. Django sería excesivo para una API. |
+| **Uvicorn** | Servidor que ejecuta FastAPI. | Es el recomendado por FastAPI. |
+| **Pydantic** | Definir y validar los datos que entran y salen de la API. | Viene integrado con FastAPI. |
+| **SQLAlchemy** | Acceder a MySQL mediante un ORM. | Es el ORM más usado en Python y evita escribir SQL a mano en cada consulta. |
+| **PyMySQL** | Conectar SQLAlchemy con MySQL. | Está escrito en Python puro y se instala sin problemas en Docker. |
+| **Alembic** | Gestionar las migraciones de la base de datos. | Es la herramienta oficial de migraciones de SQLAlchemy. |
+| **passlib (bcrypt)** | Calcular el hash de las contraseñas. | bcrypt es lento por diseño, lo que dificulta los ataques de fuerza bruta. |
+| **PyJWT** | Generar y verificar los tokens de sesión. | Permite una autenticación sin estado, adecuada para una API REST. |
+
+### 2.3. Pruebas
+
+| Tecnología | Para qué se usa | Por qué esta y no otra |
+| --- | --- | --- |
+| **pytest** | Pruebas del motor (catálogo de piezas) y de la API. | Es el estándar en Python, con una sintaxis más sencilla que *unittest*. |
+| **httpx** (`TestClient` de FastAPI) | Llamar a los endpoints desde las pruebas sin levantar el servidor. | Es lo que usa FastAPI para probar. |
+
+## 3. Base de datos
+
+Se usa **MySQL 8** para guardar los usuarios (con la contraseña cifrada), los
+proyectos, el historial de conversiones con sus avisos y las rutas de los archivos
+generados.
+
+Los archivos en sí (DXF, STEP, STL) no se guardan en la base de datos, sino en un
+volumen de Docker, porque pueden ser grandes.
+
+Se ha elegido MySQL porque el equipo ya lo conoce del ciclo y cubre de sobra las
+necesidades del proyecto. PostgreSQL sería una alternativa igual de válida.
+
+## 4. Herramientas de desarrollo
+
+| Herramienta | Uso en el proyecto |
+| --- | --- |
+| **uv** | Gestionar las dependencias con `pyproject.toml` y `uv.lock`, tanto en local como dentro de la imagen Docker, para tener las mismas versiones en todas partes. |
+| **Docker** | Ejecutar el backend con todas sus dependencias. CadQuery es especialmente delicado de instalar, y el contenedor evita problemas entre equipos. |
+| **Docker Compose** | Levantar juntos el backend y MySQL, y lanzar las pruebas contra una base de datos de pruebas, con un solo comando. |
+| **Git y Gitea del instituto** | Control de versiones, ramas por tarea, *pull requests* revisados, *issues* y tablero, según `CONTRIBUTING.md`. |
+| **Visual Studio Code** | Editor y depurador, con integración de Python, Git y Docker. |
+| **Swagger UI** | Probar los endpoints desde el navegador. Lo genera FastAPI en `/docs`. |
+| **FreeCAD / Fusion 360** | Abrir los STEP generados y comprobar que la pieza es correcta. |
+
+## 5. Ampliación futura
+
+| Tecnología | Para qué se usará |
+| --- | --- |
+| **PyMuPDF** | Extraer los trazos vectoriales de los planos en PDF. |
+| **OpenCV** y **Tesseract OCR** | Detectar líneas y leer cotas en planos escaneados. |
+
+*Riesgo identificado: reconocer cotas con OCR en planos reales es poco fiable
+(símbolos especiales, textos girados, solapamientos). Por eso queda como
+ampliación y no como parte del producto mínimo.*
diff --git a/docs/propuesta-lizette/03-arquitectura-backend.md b/docs/propuesta-lizette/03-arquitectura-backend.md
new file mode 100644
index 0000000..21cc10f
--- /dev/null
+++ b/docs/propuesta-lizette/03-arquitectura-backend.md
@@ -0,0 +1,239 @@
+# Arquitectura del backend
+
+Este documento describe cómo se organiza el backend: los componentes que lo forman,
+el flujo de una conversión, la API que usa el frontend, el modelo de datos, la
+estructura del código y el entorno Docker.
+
+## 1. Visión general
+
+Plano3D sigue una arquitectura cliente-servidor. La aplicación de escritorio
+(frontend, de Joel) es el cliente y se comunica con el backend mediante una API REST
+que intercambia JSON. El backend se ejecuta en contenedores Docker: uno para la
+aplicación Python (servidor y motor) y otro para MySQL, más un volumen donde se
+guardan los archivos.
+
+```mermaid
+flowchart LR
+ APP[Frontend
aplicación de escritorio] -- HTTP + JSON
token JWT --> API
+ subgraph Contenedor backend
+ API[API REST
FastAPI] --> SEG[Seguridad
JWT, bcrypt]
+ API --> SRV[Servicios]
+ SRV --> REP[Repositorios
SQLAlchemy]
+ SRV --> MOT[Motor
motor.api.procesar]
+ end
+ REP --> DB[(MySQL)]
+ SRV --> VOL[/Volumen de archivos
DXF, STEP, STL/]
+```
+
+| Componente | Responsabilidad |
+| --- | --- |
+| API REST (FastAPI + Uvicorn) | Recibe las peticiones del frontend, valida los datos y devuelve las respuestas. |
+| Seguridad | Registro, inicio de sesión, hash de contraseñas y verificación de los tokens JWT. |
+| Servicios | Lógica de negocio: proyectos, lanzamiento de conversiones en segundo plano, guardado de resultados. |
+| Motor (`motor/`) | Convierte el plano en pieza: lectura, vistas, reconstrucción, verificación y exportación. Se usa solo a través de `motor/api.py`. |
+| Repositorios (SQLAlchemy) | Todas las consultas a la base de datos. |
+| MySQL | Usuarios, proyectos, conversiones, avisos y metadatos de los archivos. |
+| Volumen de archivos | Guarda los DXF, STEP y STL. En la base de datos solo se guarda su ruta. |
+
+### Dos contratos
+
+- **API REST** (servidor ↔ frontend). Es lo único que Joel necesita conocer del
+ backend. Se documenta sola en `/docs` (Swagger) y cualquier cambio se acuerda
+ antes.
+- **`motor/api.py`** (servidor ↔ motor). Es interno del backend: el servidor llama
+ a `procesar()` y `exportar()`, y guarda el `Resultado` en la base de datos.
+ Mientras el motor real no esté terminado, `procesar()` devuelve el motor simulado
+ (CUBE01), de modo que el servidor y el frontend pueden avanzar en paralelo.
+
+## 2. Flujo de una conversión
+
+La conversión puede tardar varios segundos, así que no se hace dentro de la propia
+petición. La API responde enseguida con el identificador de la conversión y el
+trabajo sigue en segundo plano. El frontend consulta el estado hasta que termina y
+entonces descarga los archivos.
+
+1. **Subida.** El frontend envía el plano y los parámetros (escala, espesor para
+ el plan B, alternativa).
+2. **Validación.** Se comprueba la extensión, que no supere 20 MB y que se pueda
+ abrir. Si falla, se responde 400 con el motivo.
+3. **Registro.** Se guarda el plano en el volumen, se crea la conversión en estado
+ PENDIENTE y se devuelve su identificador (202).
+4. **Procesado** (en segundo plano, estado PROCESANDO). El servicio llama a
+ `motor.api.procesar()`, que ejecuta las etapas del motor:
+ 1. lectura del plano, unidades y escala;
+ 2. separación e identificación de alzado, planta y perfil (con una sola vista,
+ plan B: extrusión);
+ 3. sistema de proyección ISO-E / ISO-A;
+ 4. reconstrucción: celdas y búsqueda con CP-SAT;
+ 5. verificación: proyectar las aristas y compararlas con el plano.
+5. **Exportación.** Si hay sólido, se generan el STEP y el STL con
+ `motor.api.exportar()` y se guardan en el volumen.
+6. **Registro final.** Se guardan en MySQL el estado del resultado (OK,
+ CON_AVISOS, IMPOSIBLE), los avisos, la escala aplicada, el número de
+ alternativas y los archivos. La conversión pasa a COMPLETADA, o a ERROR si el
+ resultado es IMPOSIBLE o ha fallado algo inesperado.
+7. **Descarga.** El frontend consulta el estado, muestra los avisos y descarga los
+ archivos.
+
+## 3. API REST
+
+Todos los endpoints, salvo el registro y el inicio de sesión, piden la cabecera
+`Authorization: Bearer `.
+
+| Método | Ruta | Descripción |
+| --- | --- | --- |
+| POST | `/auth/registro` | Crear una cuenta. |
+| POST | `/auth/login` | Iniciar sesión y obtener el token JWT. |
+| GET | `/proyectos` | Listar los proyectos del usuario. |
+| POST | `/proyectos` | Crear un proyecto subiendo un plano. |
+| GET | `/proyectos/{id}` | Consultar un proyecto y su historial de conversiones. |
+| DELETE | `/proyectos/{id}` | Eliminar un proyecto y sus archivos. |
+| POST | `/proyectos/{id}/conversiones` | Lanzar una conversión con sus parámetros. |
+| GET | `/conversiones/{id}` | Consultar el estado, el resultado y los avisos de una conversión. |
+| GET | `/archivos/{id}` | Descargar un archivo (plano, STEP o STL). |
+
+Ejemplo de respuesta de `GET /conversiones/{id}`:
+
+```json
+{
+ "id": 12,
+ "estado": "COMPLETADA",
+ "resultado": "CON_AVISOS",
+ "escala": 2.0,
+ "alternativas": 1,
+ "avisos": [
+ {"codigo": "SIN_COTAS", "gravedad": "aviso",
+ "mensaje": "El plano no tiene cotas: las medidas se deducen de la geometría y la escala.",
+ "vista": null},
+ {"codigo": "PROYECCION_MIXTA", "gravedad": "aviso",
+ "mensaje": "Sistemas de proyección mezclados: ...", "vista": "perfil"}
+ ],
+ "archivos": [{"id": 31, "tipo": "STEP"}, {"id": 32, "tipo": "STL"}]
+}
+```
+
+### Códigos de respuesta
+
+| Código | Significado |
+| --- | --- |
+| 200 / 201 | Operación correcta / recurso creado. |
+| 202 | Conversión aceptada y en proceso. |
+| 400 | Archivo o datos no válidos (se indica el motivo). |
+| 401 | Falta el token o no es válido. |
+| 404 | El recurso no existe o es de otro usuario. |
+| 413 | El archivo supera el tamaño máximo. |
+
+## 4. Modelo de datos
+
+```mermaid
+erDiagram
+ USUARIO ||--o{ PROYECTO : tiene
+ PROYECTO ||--o{ CONVERSION : tiene
+ PROYECTO ||--o{ ARCHIVO : guarda
+ CONVERSION ||--o{ AVISO : genera
+ CONVERSION |o--o{ ARCHIVO : produce
+```
+
+| Tabla | Campos principales |
+| --- | --- |
+| **USUARIO** | id, correo (único), hash de la contraseña, fecha de alta |
+| **PROYECTO** | id, usuario_id, nombre, fecha de creación |
+| **CONVERSION** | id, proyecto_id, parámetros (escala, espesor, alternativa), estado (PENDIENTE, PROCESANDO, COMPLETADA, ERROR), resultado (OK, CON_AVISOS, IMPOSIBLE), escala aplicada, alternativas, duración, fechas |
+| **AVISO** | id, conversion_id, código, gravedad, mensaje, vista |
+| **ARCHIVO** | id, proyecto_id, conversion_id (nulo para el plano original), tipo (DXF, DWG, PDF, STEP, STL), ruta, tamaño, hash SHA-256 |
+
+## 5. Estructura del código
+
+Se añade la carpeta `servidor/` al repositorio. El motor sigue en `motor/` y el
+frontend en `app/`.
+
+```
+plano3d/
+├── servidor/
+│ ├── main.py # arranque de FastAPI
+│ ├── api/routers/ # auth, proyectos, conversiones, archivos
+│ ├── schemas/ # modelos Pydantic (entrada/salida)
+│ ├── services/ # proyectos, conversiones (llama a motor.api)
+│ ├── repositories/ # consultas a la base de datos
+│ ├── models/ # tablas SQLAlchemy
+│ └── core/ # configuración y seguridad (JWT, bcrypt)
+├── motor/
+│ ├── api.py # contrato servidor ↔ motor
+│ ├── simulado.py # motor simulado (CUBE01)
+│ ├── procesado.py # orquesta las etapas
+│ ├── lectura.py # DXF a segmentos, textos y cotas
+│ ├── escala.py # unidades y escala
+│ ├── vistas.py # alzado, planta y perfil
+│ ├── proyeccion.py # ISO-E / ISO-A
+│ ├── reconstruccion.py # celdas y CP-SAT
+│ ├── pieza_plana.py # plan B: extrusión
+│ ├── verificacion.py # comparar el sólido con el plano
+│ └── avisos.py # catálogo de códigos de aviso
+├── alembic/ # migraciones de la base de datos
+├── pruebas/ # pytest: motor, catálogo y API
+├── Dockerfile
+├── docker-compose.yml
+└── .env.example # variables necesarias, sin valores reales
+```
+
+Las rutas no tienen lógica de negocio ni acceden a la base de datos. Las
+excepciones del motor se capturan en el servicio de conversiones: la conversión
+queda en ERROR con el motivo y el servidor sigue funcionando.
+
+## 6. Catálogo de avisos
+
+Los códigos son estables: el frontend puede usarlos para mostrar ayuda o resaltar
+la vista afectada. Los dos primeros ya aparecen en el motor simulado. El resto es
+una propuesta que hay que acordar con Joel.
+
+| Código | Gravedad | Cuándo |
+| --- | --- | --- |
+| `SIN_COTAS` | Aviso | El plano no tiene cotas |
+| `PROYECCION_MIXTA` | Aviso | Planta y perfil siguen sistemas de proyección distintos |
+| `ESCALA_DETECTADA` | Info | Se ha leído la escala del cajetín |
+| `SIN_ESCALA` | Aviso | No hay escala ni en el plano ni en los parámetros |
+| `SIN_UNIDADES` | Info | El DXF no define unidades; se suponen mm |
+| `ARCHIVO_NO_VALIDO` | Bloqueante | No se puede abrir el archivo |
+| `VISTAS_NO_IDENTIFICADAS` | Bloqueante | No se encuentran alzado, planta y perfil alineados |
+| `SIN_PERFIL_CERRADO` | Bloqueante | Plan B: no hay ningún contorno cerrado |
+| `ENTIDAD_NO_SOPORTADA` | Aviso | Hay *splines* u otras entidades que se ignoran |
+| `ROSCA` | Info | Se ha modelado una rosca como agujero |
+| `PLANO_AMBIGUO` | Aviso | Varias piezas reproducen el plano |
+| `NO_REPRODUCE` | Aviso | La proyección del sólido no coincide del todo con el plano |
+| `COTA_INCOHERENTE` | Aviso | Una cota no coincide con la geometría |
+
+## 7. Entorno Docker
+
+`docker-compose.yml` define tres servicios:
+
+| Servicio | Qué hace |
+| --- | --- |
+| `mysql` | MySQL 8 con un volumen para sus datos. |
+| `backend` | La imagen del backend ejecutando Uvicorn en el puerto 8000. La documentación de la API queda en `http://localhost:8000/docs`. |
+| `pruebas` | La misma imagen, ejecutando `pytest` contra una base de datos de pruebas que se crea y se borra en cada ejecución. |
+
+Además hay un volumen para los archivos generados. Las credenciales se leen de un
+archivo `.env` que no se sube al repositorio; en él se incluye `.env.example` con
+los nombres de las variables.
+
+La imagen parte de `python:3.12-slim`, instala las bibliotecas del sistema que
+necesita OpenCASCADE (`libgl1`, `libglib2.0-0`, `libxrender1`) y las dependencias
+con `uv sync --frozen`, usando el mismo `uv.lock` que en local. Así las pruebas
+dan el mismo resultado en el contenedor y en cualquier equipo.
+
+```bash
+docker compose up -d # levantar backend y MySQL
+docker compose run --rm pruebas # ejecutar todas las pruebas
+docker compose run --rm pruebas pytest pruebas/test_contrato.py # una sola
+```
+
+## 8. Pruebas
+
+- `pruebas/test_contrato.py` (ya existe): comprueba el contrato `motor/api.py`.
+- Una prueba por pieza del catálogo (`pruebas/catalogo/`): volumen esperado y
+ códigos de aviso esperados.
+- Pruebas unitarias de cada etapa del motor con planos pequeños generados en la
+ prueba.
+- Pruebas de la API con el `TestClient` de FastAPI: registro e inicio de sesión,
+ acceso sin token (401), acceso a proyectos de otro usuario (404), subida de un
+ archivo no válido (400), y una conversión completa de CUBE01 hasta la descarga.
diff --git a/docs/propuesta-lizette/04-prueba-pieza-plana.md b/docs/propuesta-lizette/04-prueba-pieza-plana.md
new file mode 100644
index 0000000..e5443bc
--- /dev/null
+++ b/docs/propuesta-lizette/04-prueba-pieza-plana.md
@@ -0,0 +1,94 @@
+# Prueba técnica: pieza plana por extrusión (plan B)
+
+La viabilidad de la reconstrucción desde tres vistas ya la demostró
+`prototipo/reconstruir.py` con CUBE01. Esta segunda prueba comprueba el **plan B**
+acordado en las decisiones: si la reconstrucción completa no llega a tiempo, o el
+plano tiene una sola vista, generar una pieza plana extruyendo su contorno con un
+espesor.
+
+También sirve para probar la lectura de contornos cerrados y agujeros con ezdxf y
+la exportación a STEP y STL con CadQuery, que se reutilizan en el motor.
+
+Los scripts están en `prototipo/lizette/`.
+
+## 1. Objetivo
+
+- Comprobar que ezdxf lee bien las polilíneas cerradas y los círculos.
+- Comprobar que CadQuery genera el sólido por extrusión y resta los agujeros.
+- Comprobar que el STEP y el STL generados se abren bien en un programa CAD.
+
+## 2. Alcance
+
+Es un script independiente: no usa la API ni la base de datos, ni `motor/api.py`. Admite polilíneas cerradas
+y círculos. El contorno de mayor área se toma como exterior y el resto se restan
+como agujeros.
+
+Quedan fuera, y se resolverán en `motor/pieza_plana.py`:
+
+- Los arcos dentro de las polilíneas (*bulge*).
+- Unir líneas y arcos sueltos en contornos cerrados.
+- Comprobar que los agujeros están dentro del contorno exterior.
+- Las unidades.
+
+## 3. Pieza de prueba
+
+Una placa rectangular de 100 × 60 mm con dos taladros de Ø20 mm, generada con
+`prototipo/lizette/pieza_prueba.py`.
+
+**[ Captura de `placa.dxf` abierto en LibreCAD o AutoCAD ]**
+
+*Figura 1. Plano 2D de entrada*
+
+## 4. Ejecución
+
+En local, con el entorno del proyecto (`uv sync`):
+
+```bash
+cd prototipo/lizette
+uv run python pieza_prueba.py
+uv run python conversion.py placa.dxf 10
+```
+
+O dentro del contenedor del backend, sin instalar nada en el equipo:
+
+```bash
+docker compose run --rm -w /app/prototipo/lizette pruebas python pieza_prueba.py
+docker compose run --rm -w /app/prototipo/lizette pruebas python conversion.py placa.dxf 10
+```
+
+`conversion.py` recibe el DXF y el espesor en mm, y genera `resultado.step` y
+`resultado.stl`.
+
+**[ Captura de la terminal con la salida del script ]**
+
+*Figura 2. Ejecución de la prueba*
+
+## 5. Resultado
+
+**[ Captura de `resultado.step` abierto en FreeCAD o Fusion 360 ]**
+
+*Figura 3. Placa de 10 mm de espesor con dos taladros*
+
+Volumen esperado: 100 × 60 × 10 − 2 × π × 10² × 10 ≈ **53 717 mm³**. Se puede
+comprobar en FreeCAD o con `solido.val().Volume()`.
+
+## 6. Conclusiones
+
+**[ Completar tras ejecutar: ¿se ha generado bien?, ¿coincide el volumen?, ¿cuánto ha tardado?, ¿algún problema? ]**
+
+Siguientes pasos para llevarlo al motor (`motor/pieza_plana.py`):
+
+1. Leer los arcos de las polilíneas (*bulge*) y unir líneas y arcos sueltos en
+ contornos (con shapely).
+2. Comprobar que cada agujero está dentro del contorno exterior y avisar si no.
+3. Convertir las unidades a mm.
+4. Devolver un `Resultado` del contrato, con el aviso `SIN_PERFIL_CERRADO` en lugar
+ de una excepción.
+5. Añadir la placa al catálogo (`pruebas/catalogo/PLACA01/`) con su prueba
+ automática.
+6. Probar la conversión completa desde la API: subir `placa.dxf` en Swagger
+ (`/docs`), consultar el estado y descargar el STEP.
+
+**[ Captura de Swagger UI (http://localhost:8000/docs) con los endpoints ]**
+
+*Figura 4. Endpoints de la API en Swagger*
diff --git a/docs/propuesta-lizette/README.md b/docs/propuesta-lizette/README.md
new file mode 100644
index 0000000..f511f72
--- /dev/null
+++ b/docs/propuesta-lizette/README.md
@@ -0,0 +1,40 @@
+# Propuesta de Lizette: backend (servidor y motor)
+
+Documentación de la parte del **backend**: el servidor (API REST, cuentas, historial
+y base de datos) y el motor de procesado que convierte el plano en pieza 3D.
+
+| Documento | Contenido |
+| --- | --- |
+| [01-requisitos-backend.md](01-requisitos-backend.md) | Requisitos funcionales y no funcionales del backend |
+| [02-tecnologias-backend.md](02-tecnologias-backend.md) | Tecnologías elegidas y por qué |
+| [03-arquitectura-backend.md](03-arquitectura-backend.md) | Componentes, API, modelo de datos, estructura del código y Docker |
+| [04-prueba-pieza-plana.md](04-prueba-pieza-plana.md) | Prueba técnica del plan B (pieza plana por extrusión) |
+
+## Planteamiento
+
+| Parte | Responsable | Qué hace |
+| --- | --- | --- |
+| Frontend (`app/`) | Joel | Aplicación de escritorio: interfaz, visor 3D, inicio de sesión. Habla con el servidor por la API REST. |
+| Servidor (`servidor/`) | Lizette | API REST con FastAPI: cuentas, proyectos, historial de conversiones, archivos. |
+| Motor (`motor/`) | Lizette | Reconstrucción de la pieza desde las tres vistas, verificación del plano y exportación STEP/STL. |
+| Base de datos | Lizette | MySQL: usuarios, proyectos, conversiones, avisos y archivos. |
+| Pruebas | Los dos | `pytest` dentro de Docker, contra un MySQL de pruebas. |
+
+## Cambios que hay que reflejar en el resto del repositorio
+
+Este planteamiento cambia decisiones que ahora mismo están escritas de otra forma en
+el repositorio. Hay que acordarlas con Joel y actualizarlas en un *pull request*
+propio, según `CONTRIBUTING.md`:
+
+| Dónde | Dice ahora | Debe decir |
+| --- | --- | --- |
+| `docs/decisiones.md` (14-10) | «Aplicación de escritorio, no web»; sin servidor | Aplicación de escritorio **con servidor**: el frontend se conecta a una API REST que guarda cuentas e historial |
+| `docs/decisiones.md` (22-10) | Joel: aplicación; compañera: motor | Joel: frontend; Lizette: backend (servidor, base de datos y motor) |
+| `docs/guia-tecnica.md` | «No hay servidor ni cuentas»; historial en SQLite | Servidor FastAPI con cuentas (JWT) e historial en MySQL; Docker para pruebas y despliegue |
+| `README.md` | Tabla de carpetas sin `servidor/` | Añadir `servidor/` y el arranque con `docker compose` |
+| `motor/api.py` | Contrato entre motor y aplicación | Contrato entre motor y **servidor**. El contrato del frontend pasa a ser la API REST (OpenAPI en `/docs`) |
+| `pyproject.toml` | PySide6 entre las dependencias comunes | PySide6 en un grupo `app`, para que la imagen Docker del servidor no lo instale |
+
+Se mantiene lo que ya estaba decidido sobre el producto: piezas mecánicas,
+reconstrucción desde tres vistas con verificación del plano, plan B de pieza plana,
+y avisar siempre de lo que no se soporta.
diff --git a/docs/uso-ia.md b/docs/uso-ia.md
index 29d17d4..9587b92 100644
--- a/docs/uso-ia.md
+++ b/docs/uso-ia.md
@@ -8,3 +8,4 @@ Registro de los usos relevantes de IA en el proyecto, para la declaración de la
| 2026-10-14 | Claude (Claude Code) | Joel | Prototipo de reconstrucción (`prototipo/reconstruir.py`) a partir de CUBE01.dxf | Resultado abierto y comprobado en Fusion 360 |
| 2026-10-14 | Claude (Claude Code) | Joel | Revisión de decisiones técnicas y redacción de la propuesta | Revisado por el equipo antes de entregarla |
| 2026-10-22 | Claude (Claude Code) | Joel | Estructura del repositorio, contrato `motor/api.py`, motor simulado y plantillas | Revisado por el equipo |
+| 2026-11-05 | Claude | Lizette | Redacción de los documentos del backend (requisitos, tecnologías, arquitectura y prueba de la pieza plana) con servidor, cuentas, historial y Docker | Pendiente de revisar por el equipo |
diff --git a/prototipo/lizette/conversion.py b/prototipo/lizette/conversion.py
new file mode 100644
index 0000000..9e165b8
--- /dev/null
+++ b/prototipo/lizette/conversion.py
@@ -0,0 +1,59 @@
+"""Prueba de concepto: DXF 2D -> modelo 3D (STEP y STL) por extrusion.
+
+Uso: python conversion.py placa.dxf 10 (DXF y espesor en mm)
+"""
+import sys
+
+import cadquery as cq
+import ezdxf
+
+
+def area(puntos):
+ """Area de un poligono con la formula del area de Gauss (shoelace)."""
+ s = 0.0
+ for (x1, y1), (x2, y2) in zip(puntos, puntos[1:] + puntos[:1]):
+ s += x1 * y2 - x2 * y1
+ return abs(s) / 2
+
+
+def leer_perfiles(ruta):
+ """Devuelve las polilineas cerradas y los circulos del DXF."""
+ doc = ezdxf.readfile(ruta)
+ msp = doc.modelspace()
+ poligonos = []
+ for e in msp.query("LWPOLYLINE"):
+ if e.closed:
+ poligonos.append([(p[0], p[1]) for p in e.get_points()])
+ circulos = [(c.dxf.center.x, c.dxf.center.y, c.dxf.radius)
+ for c in msp.query("CIRCLE")]
+ return poligonos, circulos
+
+
+def construir_solido(poligonos, circulos, altura):
+ """El perfil de mayor area es el exterior; el resto se restan como agujeros."""
+ if not poligonos:
+ raise ValueError("El DXF no contiene ningun perfil cerrado")
+ exterior = max(poligonos, key=area)
+ solido = cq.Workplane("XY").polyline(exterior).close().extrude(altura)
+ for p in poligonos:
+ if p is not exterior:
+ hueco = cq.Workplane("XY").polyline(p).close().extrude(altura)
+ solido = solido.cut(hueco)
+ for x, y, r in circulos:
+ taladro = cq.Workplane("XY").center(x, y).circle(r).extrude(altura)
+ solido = solido.cut(taladro)
+ return solido
+
+
+def main():
+ ruta, altura = sys.argv[1], float(sys.argv[2])
+ poligonos, circulos = leer_perfiles(ruta)
+ print(f"Perfiles cerrados: {len(poligonos)} · Circulos: {len(circulos)}")
+ solido = construir_solido(poligonos, circulos, altura)
+ cq.exporters.export(solido, "resultado.step")
+ cq.exporters.export(solido, "resultado.stl")
+ print("Generados resultado.step y resultado.stl")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/prototipo/lizette/pieza_prueba.py b/prototipo/lizette/pieza_prueba.py
new file mode 100644
index 0000000..bdc578a
--- /dev/null
+++ b/prototipo/lizette/pieza_prueba.py
@@ -0,0 +1,10 @@
+"""Genera un DXF de prueba: placa de 100 x 60 mm con dos taladros de Ø20."""
+import ezdxf
+
+doc = ezdxf.new("R2010", units=4) # 4 = milímetros
+msp = doc.modelspace()
+msp.add_lwpolyline([(0, 0), (100, 0), (100, 60), (0, 60)], close=True)
+msp.add_circle((25, 30), radius=10)
+msp.add_circle((75, 30), radius=10)
+doc.saveas("placa.dxf")
+print("Creado placa.dxf")