Este repo explora cómo construir una app fullstack — API en Python (FastAPI) y panel en React — dejando las decisiones en especificaciones y usando el agente de Cursor para implementar contra esas specs, no al revés.
El dominio (un panel de administración de departamentos en venta) es solo el caso. El punto es el método: spec-driven design mínimo, sin un framework encima de Cursor.
Demo: Railway — admin@lebane.local / lebanelebane
Tres capas de documento, cada una con un contrato. Si algo no entra en esa capa, no va ahí.
- Producto — para quién es, qué se considera hecho, qué queda fuera.
- Arquitectura — stack, capas, contrato HTTP, persistencia. Si cambia el código, primero cambia este archivo.
- Features — un recorte implementable. No está hecha porque compile: hay que tildar checkpoints y que “How to test” pase.
El orden de trabajo es una feature a la vez, backend + frontend en el mismo incremento cuando la feature lo pide. Si una idea choca con product.md, gana la spec.
backend/ # Python, uv, FastAPI
frontend/ # Vite, React, TypeScript
specs/ # producto, arquitectura y features
Cada feature en specs/features/ tiene status (created → in_progress → done), Goal, In/Out, checkpoints, How to test y Review. done solo si los checkpoints están cerrados y el comando de prueba funciona.
Intención del producto: usuarios, alcance, reglas, contrato HTTP, backlog. No describe el detalle de implementación de cada pantalla.
Decisiones técnicas estables: capas del backend, organización del frontend, stack, tests, convenciones. Si el código necesita otra forma, primero se actualiza este archivo.
Las features se numeran y se apuntan entre sí. Se implementan de a una.
| Archivo | Qué cubre |
|---|---|
01-infrastructure.md |
Compose local: API, Postgres, MinIO, esqueletos de backend y frontend |
02-departments-api.md |
CRUD de departamentos, filtros, paginación, DDD en capas |
03-images.md |
Fotos en MinIO/S3, máximo 5 en el alta, miniatura y totales en el listado |
04-inquiries.md |
Consultas de interesados en el agregado: detalle y conteo en el listado |
05-address.md |
Autocompletado de dirección con Nominatim; persiste texto + coordenadas |
06-seed.md |
Semilla de ≥ 500 departamentos, fotos y consultas |
07-panel-list.md |
Tabla paginada, filtros, estados de carga / vacío / error |
08-panel-create.md |
Formulario de alta, tope de 5 fotos, dirección autocompletada |
09-panel-detail.md |
Ficha en ruta propia, galería, edición in-place |
10-quality.md |
Tests, README de entrega, listón de calidad |
11-create-inquiry.md |
Registrar una consulta desde el panel (POST anidado) |
12-auth.md |
Sesión JWT, admin único y agentes, rutas protegidas |
13-create-agent.md |
El admin lista operadores y crea agentes desde el panel |
14-panel-polish.md |
Pulido pendiente: filtros más claros, búsqueda, mapa en la ficha (created) |
.cursor/rules/spec-driven.mdc está marcada con alwaysApply: true. No genera specs: obliga a leerlas en un orden fijo antes de codear.
- Leer
specs/product.md - Leer
specs/architecture.md - Trabajar una feature de
specs/features/ - Pasarla a
in_progressy actualizar la tabla enproduct.md - Implementar contra la arquitectura (backend en capas, frontend por
features/, tests junto al código) - Tildar checkpoints en el mismo cambio; anotar decisiones estables en Review
donesolo si los checkpoints cierran y “How to test” pasa- Un commit Conventional Commit por cambio coherente; no mezclar dos features
Si el pedido choca con una decisión de product.md, se sigue la spec y se dice.
cp .env.example .env
docker compose up --buildSi Postgres quedó de una cadena Alembic vieja: docker compose down -v && docker compose up --build.
| Servicio | URL |
|---|---|
| API | http://localhost:8000 |
| OpenAPI | http://localhost:8000/docs |
| Health | http://localhost:8000/health |
| Postgres | localhost:5432 |
| MinIO | http://localhost:9000 · consola :9001 |
Panel:
cp frontend/.env.example frontend/.env
cd frontend && npm install && npm run devhttp://localhost:5173 → API en :8000. Sin sesión abre /ingresar.
Seed (≥ 500 departamentos). No corre al arrancar:
docker compose exec api python -m app.seed
docker compose exec api python -m app.seed --force # borra y vuelve a sembrarcd backend && uv sync --group dev && uv run pytest
cd backend && uv run ruff check .
cd frontend && npm testEl detalle de stack, capas y contrato HTTP vive en specs/architecture.md, no en este README.
