Guía maestra para desarrollar Kislev (sistema de gestión residencial Django) usando Claude Code con arquitectura multi-agente. Úsala siempre que vayas a implementar nuevas features, refactorizar módulos existentes, o coordinar trabajo entre agentes especializados en el proyecto.
Resources
6Install
npx skillscat add yagamidsa/kislev Install via the SkillsCat registry.
Kislev — Claude Code Multi-Agent SKILL
Estado actual del proyecto
kislevsmart/ ← app principal
models.py ← Visitante, VisitanteVehicular, Sala, Reserva,
ParqueaderoCarro, ParqueaderoMoto,
Cuota, Pago, AuditLog,
Novedad, ArchivoNovedad, ComentarioNovedad, LikeNovedad ← NUEVOS
views.py ← ~2200 líneas (pendiente dividir en módulos)
urls.py
utils.py ← role_required (reexporta de accounts), log_audit
accounts/ ← auth, ConjuntoResidencial, Torre, Usuario
models.py
views.py ← incluye CustomPasswordChangeView + RecuperarPasswordView
backends.py
utils.py ← role_required (fuente canónica)Stack: Django 5.1 + Python 3.13 · PostgreSQL 18 (puerto 5433) · Railway (deploy) · Redis (cache/sesiones) · AWS SES (email boto3) · Whitenoise · django-ratelimit
Dominio: kislev.net.co · Moneda: COP (pesos colombianos) · TZ: America/Bogota
PC actual: Lenovo / usuario Windows: Lenovo (antes era AlipioD — rutas antiguas inválidas)
⚠️ REGLA CRÍTICA — RESPONSIVE SIEMPRE: Los usuarios (porteros, propietarios) usan principalmente celular. Toda vista nueva o modificada DEBE tener @media(max-width:600px) con breakpoints para topbar, container, cards, tablas y formularios.
Arquitectura multi-agente — cómo dividir el trabajo
Cuando una tarea es grande, lanza sub-agentes con claude --dangerously-skip-permissions -p "<prompt>".
Cada agente recibe solo el contexto que necesita para minimizar tokens.
Plantillas de agentes disponibles
🔵 AGENTE: Modelo de datos
Eres un agente Django especializado en modelos y migraciones para Kislev.
Contexto del proyecto:
- App: kislevsmart + accounts
- DB: PostgreSQL 18 en Railway (local: puerto 5433)
- Modelos existentes: ConjuntoResidencial, Torre, Usuario(AbstractBaseUser),
Visitante, VisitanteVehicular, Sala(con FK conjunto), Reserva,
ParqueaderoCarro, ParqueaderoMoto, Cuota, Pago, AuditLog,
Novedad, ArchivoNovedad, ComentarioNovedad, LikeNovedad
TAREA: [DESCRIBE AQUÍ]
Reglas:
- Solo toca models.py y migrations/
- Genera siempre la migración con makemigrations --name descriptivo
- No rompas unique_together existentes
- Usa JSONField nativo de Django (no postgres)
- Documenta cada campo nuevo con help_text🟢 AGENTE: Vistas y API
Eres un agente Django especializado en views/APIs para Kislev.
Contexto:
- views.py tiene ~2200 líneas — NO lo reescribas completo, solo añade/edita funciones
- Auth: login_required + verificación de user_type ('administrador','propietario','porteria')
- Email: usar EmailMultiAlternatives con fail_silently=True (SES local no configurado)
- Redis: django.core.cache (default cache)
- Patrón existente: JsonResponse para APIs, render() para vistas
- AuditLog: usar log_audit(request, accion, detalle) de kislevsmart.utils
TAREA: [DESCRIBE AQUÍ]
Reglas:
- Añade imports solo si son nuevos
- Registra la URL nueva en urls.py
- Devuelve {'status':'ok'|'error', 'message':...} en todas las APIs JSON
- Sanitiza inputs con sanitize_text() que ya existe
- Llama log_audit() en acciones críticas (crear, validar, pagar)🟡 AGENTE: Frontend / Templates
Eres un agente de frontend para Kislev (Django templates, CSS inline).
Contexto:
- Templates en kislevsmart/templates/ y accounts/templates/
- Estáticos en kislevsmart/static/ y accounts/static/
- UI en español, mercado colombiano
- Estilo: fondo dark gradient (135deg, #1a1a2e, #16213e, #0f3460), acento #e100ff/#7f00ff
- Existen: dashboard, portería QR, salas, notificaciones, parking,
historial visitantes (propietario), finanzas (admin + propietario),
novedades (lista, detalle, crear, metricas)
TAREA: [DESCRIBE AQUÍ]
Reglas:
- Formularios con csrf_token siempre
- OBLIGATORIO responsive: incluir @media(max-width:600px) en TODA vista nueva
→ topbar flex-wrap:wrap, container padding reducido, cards sin hover transform,
tablas con overflow-x:auto y min-width:500px, formularios full width
- Porteros y propietarios usan celular → mobile-first es prioridad #1
- Mensajes de éxito/error con el sistema de messages de Django
- No usar frameworks externos (Bootstrap, Tailwind) — CSS inline/style block🔴 AGENTE: Infraestructura / DevOps
Eres un agente de infraestructura para Kislev en Railway.
Contexto:
- Deploy: Railway (bloquea puertos SMTP estándar)
- Email: AWS SES API via boto3 (NO usar SMTP)
- Variables de entorno en Railway: DATABASE_URL, AWS_ACCESS_KEY_ID,
AWS_SECRET_ACCESS_KEY, AWS_SES_REGION, DJANGO_SECRET_KEY, REDIS_URL, FERNET_KEY
- Estáticos: Whitenoise + CompressedManifestStaticFilesStorage
- Rate limiting: RATELIMIT_ENABLED=not DEBUG (requiere Redis en prod)
TAREA: [DESCRIBE AQUÍ]
Reglas:
- Nunca hardcodees credenciales
- Usa os.environ.get() con default seguro
- Si cambias settings.py, verifica que no rompa DEBUG=False
- Para Redis: django-redis con CACHE_BACKEND urlBACKLOG — Estado actualizado al 2026-04-03
✅ COMPLETADO sesiones anteriores
| Item | Descripción |
|---|---|
| C1 | Fernet hardcodeada → variable de entorno FERNET_KEY |
| C2 | Password en sesión → signing.dumps() token firmado |
| C3 | DJANGO_SECRET_KEY sin default inseguro |
| C4 | DEBUG=False por defecto |
| D1 | usuario_id bug → FK a ConjuntoResidencial con db_column='usuario_id' |
| D2 | Reservas con select_for_update (race condition) |
| D3 | email_creador = request.user.email |
| R1 | Procfile con release + gunicorn configurado para Railway |
| R2 | conn_max_age=60 + conn_health_checks=True |
| R3 | Cache Redis (django_redis) en prod, LocMemCache en dev |
| R4 | ALLOWED_HOSTS + CSRF_TRUSTED_ORIGINS con kislev.net.co |
| R5 | Logging solo a consola (Railway filesystem efímero) |
| A1 | Rate limiting login: 5/min por IP (django-ratelimit) |
| A2 | QR en BytesIO sin tocar disco |
| A3 | role_required unificado — kislevsmart/utils.py reexporta de accounts |
| A4 | select_related en parqueaderos (N+1) |
| T3 | ConjuntoResidencial hardcodeado → .first() con validación |
| T4 | print() → logger.debug() en modelos |
| T5 | .env.example creado y documentado |
| F2 | Historial visitantes por apartamento (vista propietario) |
| F7 | AuditLog — modelo + helper log_audit() + llamadas en views críticas |
| F1 | Módulo financiero: Cuota + Pago + EstadoCuenta (admin + propietario) |
| F8 | Dashboard financiero — KPIs recaudo, gráfico Chart.js, barra progreso |
| F5 | Reporte PDF mensual con WeasyPrint (visitantes, reservas, pagos) |
| F9 | Tests pytest-django — 20/20 (auth, visitantes, reservas, finanzas) |
| "Recuérdame" | Checkbox en login con session.set_expiry(30 días) |
✅ COMPLETADO sesión 2026-04-03 (migración PC nuevo + features)
| Item | Descripción |
|---|---|
| T2 | Django 3.2 → 5.1 + Python 3.7 → 3.13 (completado al migrar al nuevo PC Lenovo) |
| PC-MIG | Migración completa a PC nuevo: nuevo venv, PostgreSQL 18 (puerto 5433), kislev_user grants |
| SEED | Base de datos poblada: 2 conjuntos (Oliva Madrid + Puerto Hayuelos 1), 20 users, salas por conjunto, parqueaderos |
| AUTH-PWD | Módulo cambiar contraseña (logueado): CustomPasswordChangeView con update_session_auth_hash |
| AUTH-REC | Módulo recuperar contraseña (por cédula, sin email): flujo 2 pasos en sesión. URL: accounts:recuperar_password |
| SALA-FK | Sala.conjunto FK a ConjuntoResidencial — salas son por conjunto (no compartidas) |
| DROPDOWN | Fix click dropdown en visor_admin/propietario/porteria: pointer-events:none/auto + padding en <a> en vez de <li> |
| SUBTITLE | Panel muestra nombre del conjunto: {{ user.conjunto.nombre }} (Rol) en los 3 paneles |
| NOV-MOD | Modelos: Novedad, ArchivoNovedad, ComentarioNovedad, LikeNovedad (unique_together novedad+usuario) |
| NOV-VIEWS | Vistas novedades: lista, detalle, crear, eliminar, agregar_comentario, toggle_like (JSON), metricas_novedades |
| NOV-EMAIL | Email HTML al publicar novedad con EmailMultiAlternatives + fail_silently=True |
| NOV-UI | Templates: lista.html (cards grid), detalle.html (hero img + linkify + like animado), crear.html (preview imagen + multi-file), metricas.html (Chart.js bar+doughnut) |
| NOV-LIKE | Botón ❤️ con animación heartPop + burst CSS, toggle via fetch POST |
| NOV-RESP | Todos los templates de novedades responsive con @media(max-width:600px) |
| MENU-NOV | Menú admin/propietario/porteria actualizado con links a Novedades |
🔴 PENDIENTE — Crítico / Alta prioridad
[T1] Unificar Visitante + VisitanteVehicular
Complejidad: Alta — requiere migración de datos.
Unificar en un solo modelo con campo tipo ('peatonal', 'vehicular') y campos vehiculares opcionales. Dashboard actualmente no incluye visitantes vehiculares en estadísticas.
🚀 BLOQUE 6 — Features pendientes
| # | Feature | Estado | Complejidad |
|---|---|---|---|
| F3 | Solicitar SES producción AWS | ⏳ Trámite externo | Baja (gestión) |
| F4 | Notificaciones push PWA | ⏳ Pendiente | Media |
| F6 | API REST con DRF para app móvil | ⏳ Pendiente | Alta |
| F10 | Predicción morosidad con Claude API | ⏳ Pendiente | Alta |
Notas técnicas importantes
Módulo Novedades
Modelos (kislevsmart/models.py):
class Novedad(models.Model):
conjunto = ForeignKey(ConjuntoResidencial, related_name='novedades')
autor = ForeignKey(AUTH_USER_MODEL, related_name='novedades')
titulo = CharField(max_length=200)
imagen = ImageField(upload_to='novedades/imagenes/', null=True, blank=True)
contenido = TextField()
activa = BooleanField(default=True) # soft delete
created_at = DateTimeField(auto_now_add=True)
class ArchivoNovedad(models.Model):
novedad = ForeignKey(Novedad, related_name='archivos')
archivo = FileField(upload_to='novedades/archivos/')
nombre_original = CharField(max_length=255)
extension = CharField(max_length=20) # 'pdf', 'excel', 'txt', 'otro'
class ComentarioNovedad(models.Model):
novedad = ForeignKey(Novedad, related_name='comentarios')
usuario = ForeignKey(AUTH_USER_MODEL)
texto = TextField()
created_at = DateTimeField(auto_now_add=True)
class LikeNovedad(models.Model):
novedad = ForeignKey(Novedad, related_name='likes')
usuario = ForeignKey(AUTH_USER_MODEL, related_name='likes_novedad')
created_at = DateTimeField(auto_now_add=True)
class Meta:
unique_together = [['novedad', 'usuario']]URLs (kislevsmart/urls.py):
/novedades/ → lista_novedades
/novedades/<pk>/ → detalle_novedad
/novedades/<pk>/comentar/ → agregar_comentario
/novedades/crear/ → crear_novedad
/novedades/<pk>/eliminar/ → eliminar_novedad
/novedades/<pk>/like/ → toggle_like (POST JSON)
/novedades/metricas/ → metricas_novedadestoggle_like retorna {'liked': bool, 'total': int}.
Linkify JS — función en detalle.html y comentarios convierte https://... a <a> clickeable.
Módulo Auth — Cambiar/Recuperar Contraseña
Cambiar contraseña (logueado):
- View:
CustomPasswordChangeViewenaccounts/views.py - Usa
LoginRequiredMixin+update_session_auth_hashpara no cerrar sesión - URL:
accounts:cambiar_password - Link en:
kislevsmart/templates/dashboard.htmlsidebar
Recuperar contraseña (sin email, por cédula):
- View:
RecuperarPasswordViewenaccounts/views.py - Flujo 2 pasos: paso 1 verifica cédula (guarda user_id en sesión), paso 2 establece nueva contraseña
- URL:
accounts:recuperar_password - No requiere email — ideal para porteros que no tienen correo configurado
Modelos financieros
Cuota: cuotas de administración por conjunto. Campos: nombre, monto (COP), periodicidad, fecha_vencimiento.Pago: pago de un propietario a una cuota. unique_together=(cuota, propietario).- Vistas admin:
finanzas_admin,crear_cuota,registrar_pago - Vista propietario:
estado_cuenta
AuditLog
- Helper
log_audit(request, accion, detalle)enkislevsmart/utils.py - Acciones: visitante_creado, qr_validado, qr_invalido, reserva_creada, reserva_fallida, login, logout
Visitante FK a Conjunto
Visitante.conjuntoyVisitanteVehicular.conjunto→ ForeignKey a ConjuntoResidencial- DB column sigue siendo
usuario_id(sin migración de datos, solo cambio de constraint) - Filtrar siempre por
conjunto_id=request.user.conjunto_id
Rate limiting
RATELIMIT_ENABLED = not DEBUGen settings.pySILENCED_SYSTEM_CHECKSactivo en DEBUG para suprimir error de LocMemCache- En producción funciona con Redis (REDIS_URL)
Guía de migración a nuevo PC
Código en GitHub: https://github.com/yagamidsa/Kislev.git
Stack actual: Python 3.13 + Django 5.1 + PostgreSQL 18
Paso 1 — Instalar Python 3.13
- Descargar desde: https://www.python.org/downloads/
- Marcar "Add Python to PATH" durante la instalación
- Verificar:
python --version→Python 3.13.x
Paso 2 — Instalar WeasyPrint (requiere GTK)
- Descargar GTK3 runtime desde:
https://github.com/tschoonj/GTK-for-Windows-Runtime-Environment-Installer/releases - Instalar el
.exemás reciente con todas las opciones por defecto - Reiniciar el PC después de instalar GTK
Paso 3 — Instalar PostgreSQL
- Descargar PostgreSQL desde https://www.postgresql.org/download/windows/
- Durante la instalación anotar la contraseña del superusuario
- Puerto por defecto: 5432 (si ya hay otra versión puede quedar en 5433)
Paso 4 — Crear base de datos
En pgAdmin o psql como superusuario:
CREATE DATABASE kislev;
CREATE USER kislev_user WITH PASSWORD 'kislev123';
GRANT ALL PRIVILEGES ON DATABASE kislev TO kislev_user;
-- Conectarse a la DB kislev y ejecutar:
GRANT ALL ON SCHEMA public TO kislev_user;
ALTER DATABASE kislev OWNER TO kislev_user;
ALTER USER kislev_user CREATEDB;OJO: Si hay múltiples versiones de PostgreSQL, verificar el puerto correcto y usar ese en DATABASE_URL.
Paso 5 — Clonar el proyecto
git clone https://github.com/yagamidsa/Kislev.git
cd KislevPaso 6 — Crear entorno virtual e instalar dependencias
python -m venv entornoV
entornoV\Scripts\activate
pip install --upgrade pip
pip install -r requirements.txtSi hay errores de SSL:
pip install --trusted-host pypi.org --trusted-host files.pythonhosted.org -r requirements.txt
Paso 7 — Crear archivo .env
DJANGO_SECRET_KEY=genera-uno-con-el-comando-de-abajo
DEBUG=True
FERNET_KEY=genera-uno-con-el-comando-de-abajo
DATABASE_URL=postgres://kislev_user:kislev123@localhost:5432/kislev
# AWS SES — dejar vacío en local si no se envían emails reales
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_SES_REGION=us-east-1
DEFAULT_FROM_EMAIL=noreply@kislev.net.coAjustar puerto en DATABASE_URL según la instalación (5432 o 5433).
Generar SECRET_KEY:
python -c "from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())"Generar FERNET_KEY:
python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"Paso 8 — Aplicar migraciones
python manage.py migrate
python manage.py runserverAbrir http://127.0.0.1:8000 — debe cargar el login.
Paso 9 — Poblar datos de prueba
Usar el script seed o el shell de Django para crear:
- 2 conjuntos: Oliva Madrid, Puerto Hayuelos 1
- 10 usuarios por conjunto (propietario + portero + admin)
- 6 salas por conjunto (Gym, Piscina, Sala de Juegos, Salón Eventos 1, Salón Eventos 2, BBQ)
- Parqueaderos: Oliva Madrid 30 carros + 10 motos / Puerto Hayuelos 50 carros + 10 motos
Las cédulas y contraseñas de usuarios de prueba están en credenciales.txt (no commitear — está en .gitignore).
Paso 10 — Verificar tests
python -m pytest tests/ -vDebe dar 20/20 pasando.
Paso 11 — Instalar Claude Code
npm install -g @anthropic/claude-codeSi no tienes Node.js: https://nodejs.org (versión LTS). Luego en la carpeta del proyecto: claude
Checklist rápido nuevo PC
- Python 3.13 instalado y en PATH
- GTK3 runtime instalado (para WeasyPrint)
- PostgreSQL instalado y corriendo
- Base de datos
kislev+ usuariokislev_usercreados con todos los GRANTs - Repositorio clonado
-
entornoVcreado yrequirements.txtinstalado -
.envcreado con SECRET_KEY, FERNET_KEY y DATABASE_URL correcto -
python manage.py migratesin errores -
python -m pytest tests/→ 20/20 -
python manage.py runserver→ abre en el browser
Orden de ejecución recomendado — próximas sesiones
SESIÓN PRÓXIMA — Features restantes
→ F4 : Notificaciones push PWA (discutir opt-in por apartamento)
→ F6 : API REST DRF
→ F10: Predicción morosidad Claude API
SESIÓN T1 — Unificar modelos Visitante (sesión dedicada, alto riesgo)
1. Nuevo modelo unificado con campo tipo
2. Migración de datos (RunPython)
3. Actualizar views y templatesFlujo de trabajo con Claude Code
Comando de inicio rápido
# Desde la raíz del proyecto
claude --dangerously-skip-permissionsPatrón para tareas grandes (orquestador + sub-agentes)
# Sub-agente 1: modelos
claude -p "$(cat .claude/prompts/agente-modelos.txt) TAREA: ..."
# Sub-agente 2: vistas (después de que 1 termine)
claude -p "$(cat .claude/prompts/agente-vistas.txt) TAREA: ..."
# Sub-agente 3: frontend
claude -p "$(cat .claude/prompts/agente-frontend.txt) TAREA: ..."CLAUDE.md — pegar en la raíz del proyecto
# Kislev — Contexto para Claude Code
## Proyecto
Sistema de gestión residencial Django para conjuntos en Colombia.
Deploy en Railway. Email via AWS SES (boto3, NO smtp).
Stack: Django 5.1 + Python 3.13 + PostgreSQL 18.
## Comandos útiles
- `python manage.py runserver`
- `python manage.py makemigrations && python manage.py migrate`
## REGLA #1 — RESPONSIVE OBLIGATORIO
Los usuarios usan principalmente celular (porteros, propietarios).
TODA vista nueva DEBE tener @media(max-width:600px) con:
- topbar: flex-wrap:wrap, padding reducido, font-size menor
- container: padding:14px 10px
- tablas: overflow-x:auto + min-width:500px
- formularios: width:100%, columnas apiladas
- cards: sin hover transform, padding reducido
## Convenciones
- Views: siempre @login_required + @role_required([...])
- APIs JSON: retornar {'status': 'ok'|'error', 'message': str}
- Inputs: sanitizar con sanitize_text() de kislevsmart/utils.py
- Email: EmailMultiAlternatives con fail_silently=True (SES no activo en dev)
- Fechas: siempre timezone-aware (America/Bogota)
- Moneda: COP, formatear con f"${valor:,.0f}"
- QR: siempre en memoria (BytesIO), nunca guardar en disco
- Cache: usar django.core.cache (apunta a Redis en Railway)
- AuditLog: llamar log_audit(request, accion, detalle) en acciones críticas
## NO hacer
- No guardar passwords en sesión → usar signing.dumps()
- No usar SMTP → Railway lo bloquea, usar AWS SES API
- No modificar migrations manualmente
- No hardcodear nombres de conjuntos en código nuevo
- No usar print() → usar logger.debug/info/error
- No guardar archivos en disco → usar BytesIO en memoria
- No hardcodear SECRET_KEY, FERNET_KEY ni credenciales
- No usar LocMemCache en producción → usar Redis
- No crear vistas sin @media(max-width:600px)
## Bugs conocidos pendientes
- T1: Visitante + VisitanteVehicular duplicados — pendiente unificar