@saulwade/swl-ses 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +238 -0
- package/README.md +560 -0
- package/_userland/agentes/.gitkeep +0 -0
- package/_userland/habilidades/.gitkeep +0 -0
- package/agentes/.evolved.json +9 -0
- package/agentes/accesibilidad-wcag-swl.md +692 -0
- package/agentes/arquitecto-swl.md +238 -0
- package/agentes/auto-evolucion-swl.md +854 -0
- package/agentes/backend-api-swl.md +470 -0
- package/agentes/backend-csharp-swl.md +418 -0
- package/agentes/backend-go-swl.md +388 -0
- package/agentes/backend-java-swl.md +279 -0
- package/agentes/backend-node-swl.md +477 -0
- package/agentes/backend-python-swl.md +608 -0
- package/agentes/backend-rust-swl.md +362 -0
- package/agentes/backend-workers-swl.md +480 -0
- package/agentes/cloud-infra-swl.md +485 -0
- package/agentes/consolidador-swl.md +539 -0
- package/agentes/datos-swl.md +584 -0
- package/agentes/depurador-swl.md +349 -0
- package/agentes/devops-ci-swl.md +374 -0
- package/agentes/disenador-ui-swl.md +558 -0
- package/agentes/documentador-swl.md +343 -0
- package/agentes/evals/arquitecto-swl.evals.json +56 -0
- package/agentes/evals/auto-evolucion-swl.evals.json +68 -0
- package/agentes/evals/implementador-swl.evals.json +56 -0
- package/agentes/evals/orquestador-swl.evals.json +60 -0
- package/agentes/evals/perfilador-usuario-swl.evals.json +60 -0
- package/agentes/evals/red-team-swl.evals.json +59 -0
- package/agentes/evals/revisor-codigo-swl.evals.json +59 -0
- package/agentes/frontend-angular-swl.md +627 -0
- package/agentes/frontend-css-swl.md +720 -0
- package/agentes/frontend-react-swl.md +696 -0
- package/agentes/frontend-swl.md +500 -0
- package/agentes/frontend-tailwind-swl.md +830 -0
- package/agentes/implementador-swl.md +328 -0
- package/agentes/investigador-swl.md +430 -0
- package/agentes/investigador-ux-swl.md +500 -0
- package/agentes/llm-apps-swl.md +276 -0
- package/agentes/migrador-swl.md +417 -0
- package/agentes/mobile-android-swl.md +509 -0
- package/agentes/mobile-cross-swl.md +539 -0
- package/agentes/mobile-ios-swl.md +500 -0
- package/agentes/mobile-testing-swl.md +300 -0
- package/agentes/notificador-swl.md +916 -0
- package/agentes/observabilidad-swl.md +436 -0
- package/agentes/orquestador-swl.md +884 -0
- package/agentes/pagos-swl.md +283 -0
- package/agentes/perfilador-usuario-swl.md +306 -0
- package/agentes/planificador-swl.md +402 -0
- package/agentes/producto-prd-swl.md +587 -0
- package/agentes/red-team-swl.md +216 -0
- package/agentes/release-manager-swl.md +568 -0
- package/agentes/rendimiento-swl.md +714 -0
- package/agentes/resolutor-build-swl.md +243 -0
- package/agentes/revisor-angular-swl.md +276 -0
- package/agentes/revisor-codigo-swl.md +348 -0
- package/agentes/revisor-csharp-swl.md +262 -0
- package/agentes/revisor-go-swl.md +257 -0
- package/agentes/revisor-java-swl.md +255 -0
- package/agentes/revisor-kotlin-swl.md +271 -0
- package/agentes/revisor-nextjs-swl.md +279 -0
- package/agentes/revisor-php-swl.md +269 -0
- package/agentes/revisor-react-swl.md +276 -0
- package/agentes/revisor-rust-swl.md +344 -0
- package/agentes/revisor-seguridad-swl.md +390 -0
- package/agentes/revisor-swift-swl.md +266 -0
- package/agentes/revisor-typescript-swl.md +344 -0
- package/agentes/sre-swl.md +265 -0
- package/agentes/tdd-qa-swl.md +354 -0
- package/agentes/ux-disenador-swl.md +501 -0
- package/bin/lib/bot-comandos.js +1030 -0
- package/bin/lib/bot-discovery.js +182 -0
- package/bin/lib/bot-git.js +142 -0
- package/bin/swl-ses.js +325 -0
- package/bin/swl-telegram-bot.js +442 -0
- package/bin/swl-telegram-bot.plist +21 -0
- package/bin/swl-telegram-bot.service +14 -0
- package/comandos/swl/.evolved.json +23 -0
- package/comandos/swl/actualizar.md +174 -0
- package/comandos/swl/adoptar-proyecto.md +207 -0
- package/comandos/swl/aprender.md +701 -0
- package/comandos/swl/auditar-deps.md +134 -0
- package/comandos/swl/autoresearch.md +170 -0
- package/comandos/swl/ayuda.md +224 -0
- package/comandos/swl/brainstorm.md +50 -0
- package/comandos/swl/checkpoint.md +330 -0
- package/comandos/swl/compactar.md +283 -0
- package/comandos/swl/configurar-ci.md +227 -0
- package/comandos/swl/contexto.md +112 -0
- package/comandos/swl/contribuir.md +233 -0
- package/comandos/swl/crear-skill.md +292 -0
- package/comandos/swl/cron.md +196 -0
- package/comandos/swl/dashboard.md +146 -0
- package/comandos/swl/discutir-fase.md +230 -0
- package/comandos/swl/ejecutar-fase.md +135 -0
- package/comandos/swl/evaluar-skill.md +487 -0
- package/comandos/swl/evolucion-estado.md +142 -0
- package/comandos/swl/evolucionar.md +259 -0
- package/comandos/swl/exportar-vault.md +189 -0
- package/comandos/swl/gateway.md +158 -0
- package/comandos/swl/inbox.md +116 -0
- package/comandos/swl/instalar.md +220 -0
- package/comandos/swl/instintos.md +86 -0
- package/comandos/swl/mapear-codebase.md +312 -0
- package/comandos/swl/mcp-status.md +175 -0
- package/comandos/swl/metricas.md +270 -0
- package/comandos/swl/modelo.md +102 -0
- package/comandos/swl/notificaciones.md +396 -0
- package/comandos/swl/nuevo-proyecto.md +154 -0
- package/comandos/swl/planear-fase.md +221 -0
- package/comandos/swl/plugins.md +256 -0
- package/comandos/swl/reflect-skills.md +125 -0
- package/comandos/swl/release.md +217 -0
- package/comandos/swl/revisar-impacto.md +206 -0
- package/comandos/swl/revisar.md +330 -0
- package/comandos/swl/salud.md +363 -0
- package/comandos/swl/sesiones.md +200 -0
- package/comandos/swl/skill-search.md +113 -0
- package/comandos/swl/verificar.md +585 -0
- package/comandos/swl/wiki.md +620 -0
- package/contextos/dev.md +32 -0
- package/contextos/research.md +30 -0
- package/contextos/review.md +31 -0
- package/habilidades/accesibilidad-a11y/SKILL.md +201 -0
- package/habilidades/accesibilidad-a11y/evals/evals.json +56 -0
- package/habilidades/accesibilidad-a11y/recursos/ejemplos-y-checklist-completo.md +441 -0
- package/habilidades/agent-browser/SKILL.md +218 -0
- package/habilidades/agentes-como-servicio/SKILL.md +218 -0
- package/habilidades/ai-runtime-security/SKILL.md +273 -0
- package/habilidades/angular-avanzado/SKILL.md +164 -0
- package/habilidades/angular-avanzado/recursos/ejemplos-avanzados.md +219 -0
- package/habilidades/angular-moderno/SKILL.md +186 -0
- package/habilidades/angular-moderno/evals/evals.json +45 -0
- package/habilidades/angular-moderno/recursos/ejemplos-avanzados.md +106 -0
- package/habilidades/api-rest-diseno/SKILL.md +191 -0
- package/habilidades/api-rest-diseno/recursos/openapi-template.yaml +506 -0
- package/habilidades/api-rest-diseno/recursos/referencia-api.md +140 -0
- package/habilidades/aprendizaje-continuo/SKILL.md +151 -0
- package/habilidades/aprendizaje-continuo/evals/evals.json +53 -0
- package/habilidades/aprendizaje-continuo/recursos/referencia-instintos.md +290 -0
- package/habilidades/async-python/SKILL.md +149 -0
- package/habilidades/async-python/evals/evals.json +47 -0
- package/habilidades/async-python/recursos/patrones-y-ejemplos-completos.md +292 -0
- package/habilidades/auth-patrones/.evolved.json +9 -0
- package/habilidades/auth-patrones/SKILL.md +413 -0
- package/habilidades/auth-patrones/recursos/implementaciones-completas.md +229 -0
- package/habilidades/auto-evolucion-protocolo/SKILL.md +276 -0
- package/habilidades/auto-evolucion-protocolo/evals/evals.json +55 -0
- package/habilidades/auto-evolucion-protocolo/recursos/referencia-completa.md +145 -0
- package/habilidades/autoresearch/SKILL.md +268 -0
- package/habilidades/autoresearch/evals/evals.json +41 -0
- package/habilidades/autoresearch/recursos/checklist-template.md +191 -0
- package/habilidades/autoresearch/scripts/calcular-score.js +88 -0
- package/habilidades/azure-cloud/SKILL.md +308 -0
- package/habilidades/azure-cloud/recursos/aks.md +327 -0
- package/habilidades/backend-mcp-servidor/SKILL.md +270 -0
- package/habilidades/backend-production-resilience/SKILL.md +288 -0
- package/habilidades/brainstorming/SKILL.md +295 -0
- package/habilidades/brainstorming/recursos/componentes-html.md +247 -0
- package/habilidades/build-errors-cpp/SKILL.md +270 -0
- package/habilidades/build-errors-csharp/SKILL.md +265 -0
- package/habilidades/build-errors-go/SKILL.md +306 -0
- package/habilidades/build-errors-java/SKILL.md +278 -0
- package/habilidades/build-errors-kotlin/SKILL.md +303 -0
- package/habilidades/build-errors-nextjs/SKILL.md +312 -0
- package/habilidades/build-errors-php/SKILL.md +270 -0
- package/habilidades/build-errors-python/SKILL.md +292 -0
- package/habilidades/build-errors-rust/SKILL.md +284 -0
- package/habilidades/build-errors-swift/SKILL.md +272 -0
- package/habilidades/build-errors-typescript/SKILL.md +369 -0
- package/habilidades/checklist-calidad/SKILL.md +271 -0
- package/habilidades/checklist-calidad/recursos/quality-report-template.md +148 -0
- package/habilidades/checklist-seguridad/SKILL.md +285 -0
- package/habilidades/checkpoints-verificacion/SKILL.md +298 -0
- package/habilidades/checkpoints-verificacion/recursos/checkpoint-templates.md +360 -0
- package/habilidades/ci-cd-pipelines/SKILL.md +157 -0
- package/habilidades/ci-cd-pipelines/recursos/github-actions-template.yaml +403 -0
- package/habilidades/ci-cd-pipelines/recursos/pipelines-completos.md +487 -0
- package/habilidades/cloud-aws/SKILL.md +142 -0
- package/habilidades/cloud-aws/recursos/servicios-aws-referencia.md +321 -0
- package/habilidades/compactacion-contexto/SKILL.md +247 -0
- package/habilidades/contenedores-docker/SKILL.md +137 -0
- package/habilidades/contenedores-docker/recursos/dockerfile-template.dockerfile +160 -0
- package/habilidades/contenedores-docker/recursos/ejemplos-y-configuraciones.md +327 -0
- package/habilidades/context-builder/SKILL.md +170 -0
- package/habilidades/control-profundidad/SKILL.md +128 -0
- package/habilidades/csharp-experto/SKILL.md +322 -0
- package/habilidades/csharp-patrones/SKILL.md +316 -0
- package/habilidades/csharp-testing/SKILL.md +286 -0
- package/habilidades/css-moderno/SKILL.md +166 -0
- package/habilidades/css-moderno/evals/evals.json +43 -0
- package/habilidades/css-moderno/recursos/ejemplos-y-patrones-completos.md +337 -0
- package/habilidades/datos-etl/SKILL.md +129 -0
- package/habilidades/datos-etl/recursos/implementaciones-completas.md +322 -0
- package/habilidades/dbml-experto/SKILL.md +339 -0
- package/habilidades/dbml-experto/evals/evals.json +56 -0
- package/habilidades/dependencias-auditoria/SKILL.md +320 -0
- package/habilidades/deprecacion-migracion/SKILL.md +169 -0
- package/habilidades/deprecacion-migracion/recursos/implementaciones-completas.md +220 -0
- package/habilidades/design-tokens/SKILL.md +158 -0
- package/habilidades/design-tokens/recursos/tokens-y-configuracion.md +363 -0
- package/habilidades/devsecops-pipeline-security/SKILL.md +309 -0
- package/habilidades/diagrama-arquitectura/SKILL.md +165 -0
- package/habilidades/diagrama-arquitectura/assets/template.html +276 -0
- package/habilidades/discutir-fase/SKILL.md +188 -0
- package/habilidades/diseno-herramientas-agente/SKILL.md +199 -0
- package/habilidades/diseno-responsivo/SKILL.md +186 -0
- package/habilidades/diseno-responsivo/recursos/ejemplos-layouts.md +156 -0
- package/habilidades/django-experto/SKILL.md +205 -0
- package/habilidades/django-experto/recursos/async-django.md +390 -0
- package/habilidades/django-experto/recursos/drf-patrones.md +438 -0
- package/habilidades/django-experto/recursos/orm-avanzado.md +382 -0
- package/habilidades/django-experto/recursos/referencia-completa.md +188 -0
- package/habilidades/django-experto/recursos/testing-django.md +415 -0
- package/habilidades/doc-sync/SKILL.md +280 -0
- package/habilidades/drift-detection/SKILL.md +179 -0
- package/habilidades/ejecutar-fase/SKILL.md +468 -0
- package/habilidades/estilo-sin-ai-isms/SKILL.md +775 -0
- package/habilidades/estilo-sin-ai-isms/evals/evals.json +63 -0
- package/habilidades/estilo-sin-ai-isms/scripts/detectar_aiisms.py +500 -0
- package/habilidades/estructura-proyecto-claude/SKILL.md +215 -0
- package/habilidades/estructura-proyecto-claude/recursos/claude-md-template.md +261 -0
- package/habilidades/estructura-proyecto-claude/recursos/configuracion-y-extensiones.md +176 -0
- package/habilidades/estructura-proyecto-claude/recursos/frontmatter-y-hooks-referencia.md +289 -0
- package/habilidades/estructura-proyecto-claude/recursos/mcp-json-template.json +77 -0
- package/habilidades/estructura-proyecto-claude/recursos/variantes-por-stack.md +177 -0
- package/habilidades/evaluacion-agentes/SKILL.md +314 -0
- package/habilidades/event-driven/SKILL.md +153 -0
- package/habilidades/event-driven/recursos/implementaciones-completas.md +423 -0
- package/habilidades/extraccion-documentos/SKILL.md +221 -0
- package/habilidades/extractor-de-aprendizajes/.evolved.json +9 -0
- package/habilidades/extractor-de-aprendizajes/SKILL.md +311 -0
- package/habilidades/extractor-de-aprendizajes/evals/evals.json +55 -0
- package/habilidades/fastapi-experto/SKILL.md +221 -0
- package/habilidades/fastapi-experto/recursos/async-patterns.md +438 -0
- package/habilidades/fastapi-experto/recursos/dependency-injection.md +330 -0
- package/habilidades/fastapi-experto/recursos/referencia-completa.md +79 -0
- package/habilidades/fastapi-experto/recursos/testing-httpx.md +420 -0
- package/habilidades/filament-admin/SKILL.md +290 -0
- package/habilidades/frontend-avanzado/SKILL.md +257 -0
- package/habilidades/frontend-avanzado/recursos/apis-nativas-ejemplos.md +341 -0
- package/habilidades/gcp-cloud/SKILL.md +260 -0
- package/habilidades/gcp-cloud/recursos/gke.md +234 -0
- package/habilidades/gcp-cloud/recursos/terraform-gcp.md +307 -0
- package/habilidades/generacion-mermaid/SKILL.md +229 -0
- package/habilidades/git-worktrees-paralelo/SKILL.md +270 -0
- package/habilidades/go-experto/SKILL.md +305 -0
- package/habilidades/go-patrones/SKILL.md +299 -0
- package/habilidades/go-testing/SKILL.md +291 -0
- package/habilidades/graphql-experto/SKILL.md +323 -0
- package/habilidades/guardrail-semantico/SKILL.md +282 -0
- package/habilidades/harness-claude-code/SKILL.md +299 -0
- package/habilidades/iam-secretos/SKILL.md +265 -0
- package/habilidades/iam-secretos/recursos/implementaciones-completas.md +356 -0
- package/habilidades/infra-github-actions/SKILL.md +166 -0
- package/habilidades/instalar-sistema/.evolved.json +9 -0
- package/habilidades/instalar-sistema/SKILL.md +221 -0
- package/habilidades/java-experto/SKILL.md +290 -0
- package/habilidades/java-patrones/SKILL.md +275 -0
- package/habilidades/java-testing/SKILL.md +288 -0
- package/habilidades/kotlin-compose/SKILL.md +278 -0
- package/habilidades/kotlin-compose/recursos/animaciones-performance.md +93 -0
- package/habilidades/kotlin-experto/SKILL.md +318 -0
- package/habilidades/kotlin-testing/SKILL.md +267 -0
- package/habilidades/kotlin-testing/recursos/testing-avanzado.md +74 -0
- package/habilidades/kubernetes-orquestacion/SKILL.md +152 -0
- package/habilidades/kubernetes-orquestacion/recursos/manifiestos-completos.md +452 -0
- package/habilidades/langchain-langraph/SKILL.md +386 -0
- package/habilidades/langchain-langraph/recursos/evaluacion-rag.md +321 -0
- package/habilidades/langchain-langraph/recursos/rag-maturity-model.md +225 -0
- package/habilidades/langchain-langraph/recursos/vectorstores.md +306 -0
- package/habilidades/legacy-code-rescue/SKILL.md +267 -0
- package/habilidades/likec4-experto/SKILL.md +412 -0
- package/habilidades/likec4-experto/evals/evals.json +69 -0
- package/habilidades/manejo-errores/.evolved.json +9 -0
- package/habilidades/manejo-errores/SKILL.md +407 -0
- package/habilidades/manejo-errores/recursos/implementaciones-completas.md +248 -0
- package/habilidades/mapear-codebase/SKILL.md +275 -0
- package/habilidades/memoria-busqueda/SKILL.md +194 -0
- package/habilidades/memoria-busqueda/evals/evals.json +44 -0
- package/habilidades/meta-skills-estandar/SKILL.md +298 -0
- package/habilidades/meta-skills-estandar/recursos/anti-patrones-y-leyes.md +205 -0
- package/habilidades/meta-skills-estandar/recursos/frameworks-seguridad.md +107 -0
- package/habilidades/meta-skills-estandar/recursos/idiomas-framework.md +60 -0
- package/habilidades/meta-skills-estandar/recursos/skills-as-agents.md +163 -0
- package/habilidades/microservicios/SKILL.md +155 -0
- package/habilidades/microservicios/recursos/patrones-y-ejemplos-completos.md +325 -0
- package/habilidades/mobile-flutter/SKILL.md +199 -0
- package/habilidades/mobile-flutter/recursos/ejemplos-completos.md +319 -0
- package/habilidades/mobile-react-native/SKILL.md +176 -0
- package/habilidades/mobile-react-native/recursos/ejemplos-completos.md +216 -0
- package/habilidades/mongodb-experto/SKILL.md +302 -0
- package/habilidades/monitoring-alertas/SKILL.md +201 -0
- package/habilidades/monitoring-alertas/recursos/instrumentacion-y-alertas.md +301 -0
- package/habilidades/nestjs-experto/SKILL.md +307 -0
- package/habilidades/nestjs-experto/recursos/guards-interceptors.md +339 -0
- package/habilidades/nestjs-experto/recursos/modulos-di.md +287 -0
- package/habilidades/nestjs-experto/recursos/testing-nestjs.md +354 -0
- package/habilidades/nextjs-experto/SKILL.md +335 -0
- package/habilidades/nextjs-patrones/SKILL.md +303 -0
- package/habilidades/nextjs-testing/SKILL.md +331 -0
- package/habilidades/node-experto/.evolved.json +9 -0
- package/habilidades/node-experto/SKILL.md +266 -0
- package/habilidades/node-experto/recursos/patrones-completos.md +283 -0
- package/habilidades/notificaciones-multicanal/SKILL.md +159 -0
- package/habilidades/notificaciones-multicanal/recursos/config-template.json +115 -0
- package/habilidades/notificaciones-multicanal/recursos/configuracion-y-templates.md +303 -0
- package/habilidades/nuevo-proyecto/SKILL.md +204 -0
- package/habilidades/orquestacion-async/SKILL.md +303 -0
- package/habilidades/paid-media-tracking/SKILL.md +269 -0
- package/habilidades/paid-media-tracking/recursos/auditoria-tracking.md +220 -0
- package/habilidades/paid-media-tracking/recursos/google-ads-api.md +215 -0
- package/habilidades/patrones-python/SKILL.md +228 -0
- package/habilidades/patrones-python/evals/evals.json +56 -0
- package/habilidades/patrones-python/recursos/patrones-avanzados.md +469 -0
- package/habilidades/patrones-python/recursos/referencia-completa.md +202 -0
- package/habilidades/perfil-usuario/SKILL.md +200 -0
- package/habilidades/perfil-usuario/evals/evals.json +55 -0
- package/habilidades/performance-baseline/SKILL.md +297 -0
- package/habilidades/php-experto/SKILL.md +291 -0
- package/habilidades/php-patrones/SKILL.md +306 -0
- package/habilidades/php-testing/SKILL.md +280 -0
- package/habilidades/planear-fase/SKILL.md +269 -0
- package/habilidades/postgresql-experto/SKILL.md +151 -0
- package/habilidades/postgresql-experto/evals/evals.json +53 -0
- package/habilidades/postgresql-experto/recursos/referencia-completa.md +215 -0
- package/habilidades/prevencion-racionalizacion/SKILL.md +175 -0
- package/habilidades/prevencion-sobreingenieria/SKILL.md +323 -0
- package/habilidades/privacy-memoria/SKILL.md +141 -0
- package/habilidades/privacy-memoria/evals/evals.json +43 -0
- package/habilidades/prompt-engineering/SKILL.md +518 -0
- package/habilidades/prompt-engineering/recursos/patrones-avanzados.md +467 -0
- package/habilidades/rag-arquitectura/SKILL.md +338 -0
- package/habilidades/rails-experto/SKILL.md +237 -0
- package/habilidades/rails-experto/recursos/active-record.md +260 -0
- package/habilidades/rails-experto/recursos/hotwire-turbo.md +293 -0
- package/habilidades/rails-experto/recursos/testing-rspec.md +362 -0
- package/habilidades/react-experto/SKILL.md +209 -0
- package/habilidades/react-experto/evals/evals.json +55 -0
- package/habilidades/react-experto/recursos/patrones-y-ejemplos-completos.md +240 -0
- package/habilidades/react-optimizacion/SKILL.md +174 -0
- package/habilidades/react-optimizacion/recursos/patrones-avanzados.md +138 -0
- package/habilidades/redis-experto/SKILL.md +305 -0
- package/habilidades/release-semver/.evolved.json +9 -0
- package/habilidades/release-semver/SKILL.md +248 -0
- package/habilidades/release-semver/scripts/generar-changelog.sh +238 -0
- package/habilidades/rust-experto/SKILL.md +400 -0
- package/habilidades/rust-patrones/SKILL.md +296 -0
- package/habilidades/rust-testing/SKILL.md +311 -0
- package/habilidades/seguridad-skills-ia/SKILL.md +262 -0
- package/habilidades/sql-optimizacion/SKILL.md +200 -0
- package/habilidades/sql-optimizacion/evals/evals.json +54 -0
- package/habilidades/sql-optimizacion/recursos/patrones-sql-avanzados.md +131 -0
- package/habilidades/sre-patrones/SKILL.md +333 -0
- package/habilidades/sre-patrones/recursos/chaos-engineering.md +241 -0
- package/habilidades/sre-patrones/recursos/oncall-design.md +236 -0
- package/habilidades/stripe-pagos/SKILL.md +550 -0
- package/habilidades/stripe-pagos/recursos/errores-reintentos.md +390 -0
- package/habilidades/stripe-pagos/recursos/stripe-connect.md +290 -0
- package/habilidades/structured-outputs/SKILL.md +343 -0
- package/habilidades/swift-experto/SKILL.md +320 -0
- package/habilidades/swift-experto/recursos/keychain-y-wrappers.md +110 -0
- package/habilidades/swift-patrones/SKILL.md +313 -0
- package/habilidades/swift-patrones/recursos/tca-ejemplo-completo.md +113 -0
- package/habilidades/swift-testing/SKILL.md +254 -0
- package/habilidades/swift-testing/recursos/xcuitest-planes.md +143 -0
- package/habilidades/swl-dashboard/SKILL.md +370 -0
- package/habilidades/swl-markitdown/SKILL.md +285 -0
- package/habilidades/swl-markitdown/evals/evals.json +52 -0
- package/habilidades/swl-revisar-impacto/SKILL.md +233 -0
- package/habilidades/tailwind-experto/SKILL.md +240 -0
- package/habilidades/tailwind-experto/recursos/referencia-completa.md +184 -0
- package/habilidades/tdd-workflow/SKILL.md +293 -0
- package/habilidades/terraform-experto/SKILL.md +321 -0
- package/habilidades/testing-python/SKILL.md +340 -0
- package/habilidades/testing-python/recursos/ejemplos-completos.md +167 -0
- package/habilidades/threat-model-lite/SKILL.md +246 -0
- package/habilidades/tracing-processor/SKILL.md +212 -0
- package/habilidades/tracking-measurement/SKILL.md +239 -0
- package/habilidades/tracking-measurement/recursos/consent-mode.md +231 -0
- package/habilidades/tracking-measurement/recursos/gtm-datalayer.md +216 -0
- package/habilidades/tracking-measurement/recursos/meta-capi.md +262 -0
- package/habilidades/typescript-avanzado/SKILL.md +144 -0
- package/habilidades/typescript-avanzado/evals/evals.json +55 -0
- package/habilidades/typescript-avanzado/recursos/patrones-y-ejemplos-completos.md +298 -0
- package/habilidades/typescript-diagnosticos/SKILL.md +513 -0
- package/habilidades/ux-diseno/SKILL.md +116 -0
- package/habilidades/ux-diseno/evals/evals.json +43 -0
- package/habilidades/ux-diseno/recursos/patrones-ux-referencia.md +214 -0
- package/habilidades/validacion-ci-sistema/SKILL.md +136 -0
- package/habilidades/validacion-ci-sistema/recursos/validadores-completos.md +369 -0
- package/habilidades/validacion-ci-sistema/scripts/validar-sistema.sh +286 -0
- package/habilidades/verificacion-evidencia/SKILL.md +160 -0
- package/habilidades/verificar-trabajo/SKILL.md +303 -0
- package/habilidades/verificar-trabajo/recursos/plantilla-verificacion.md +60 -0
- package/habilidades/wiki-conocimiento/SKILL.md +276 -0
- package/habilidades/wireframes-flujos/SKILL.md +212 -0
- package/habilidades/wireframes-flujos/recursos/referencia-completa.md +192 -0
- package/habilidades/workflow-claude-code/SKILL.md +260 -0
- package/habilidades/workflow-claude-code/recursos/referencia-completa.md +109 -0
- package/hooks/_run-hook.sh +57 -0
- package/hooks/actualizar-perfil-usuario.js +364 -0
- package/hooks/agente-lifecycle.js +71 -0
- package/hooks/aiisms-detector.js +173 -0
- package/hooks/audit-trail.js +204 -0
- package/hooks/auto-background.js +97 -0
- package/hooks/auto-consolidacion.js +178 -0
- package/hooks/auto-evolucion.js +666 -0
- package/hooks/auto-restaurar-settings.js +360 -0
- package/hooks/calidad-pre-commit.js +929 -0
- package/hooks/calidad-typescript.js +511 -0
- package/hooks/captura-feedback-usuario.js +148 -0
- package/hooks/check-update.js +211 -0
- package/hooks/clasificador-mensajes.js +271 -0
- package/hooks/degradacion-instintos.js +272 -0
- package/hooks/escaneo-secretos.js +389 -0
- package/hooks/extraccion-aprendizajes.js +763 -0
- package/hooks/grafo-contexto.js +129 -0
- package/hooks/graph-update.js +67 -0
- package/hooks/guardrail-modelo.js +247 -0
- package/hooks/inbox-aviso.js +75 -0
- package/hooks/inyeccion-contexto.js +246 -0
- package/hooks/lib/abort-registry.js +214 -0
- package/hooks/lib/agent-backend.js +210 -0
- package/hooks/lib/agent-comms.js +263 -0
- package/hooks/lib/agent-issue-codes.js +284 -0
- package/hooks/lib/agent-matcher.js +189 -0
- package/hooks/lib/async-hook-registry.js +252 -0
- package/hooks/lib/atomic-write.js +130 -0
- package/hooks/lib/auto-consolidator.js +335 -0
- package/hooks/lib/canary-skills.js +187 -0
- package/hooks/lib/consolidation-lock.js +291 -0
- package/hooks/lib/context-builder.js +430 -0
- package/hooks/lib/context-compressor.js +657 -0
- package/hooks/lib/convergence-detector.js +105 -0
- package/hooks/lib/delegation-tracker.js +198 -0
- package/hooks/lib/detectar-package-manager.js +423 -0
- package/hooks/lib/edit-accumulator.js +171 -0
- package/hooks/lib/error-classifier.js +308 -0
- package/hooks/lib/event-bus.js +112 -0
- package/hooks/lib/evolution-tracker.js +442 -0
- package/hooks/lib/execution-state.js +316 -0
- package/hooks/lib/fingerprint-id.js +135 -0
- package/hooks/lib/gateway-notify.js +116 -0
- package/hooks/lib/graph-security.js +75 -0
- package/hooks/lib/guardrail-metrics.js +202 -0
- package/hooks/lib/hook-circuit-breaker.js +206 -0
- package/hooks/lib/loop-detector.js +267 -0
- package/hooks/lib/mcp-health.js +184 -0
- package/hooks/lib/mcp-pool.js +436 -0
- package/hooks/lib/memory-search.js +506 -0
- package/hooks/lib/merkle-audit.js +96 -0
- package/hooks/lib/model-router.js +222 -0
- package/hooks/lib/normalize-error.js +324 -0
- package/hooks/lib/normalize-input.js +65 -0
- package/hooks/lib/nudge-tracker.js +306 -0
- package/hooks/lib/otlp-exporter.js +365 -0
- package/hooks/lib/performance-marks.js +239 -0
- package/hooks/lib/privacy-filter.js +128 -0
- package/hooks/lib/prompt-injection-scanner.js +209 -0
- package/hooks/lib/provenance-tracker.js +183 -0
- package/hooks/lib/rate-limit-tracker.js +253 -0
- package/hooks/lib/reflect-classifier.js +164 -0
- package/hooks/lib/resource-quota.js +122 -0
- package/hooks/lib/retry-jitter.js +165 -0
- package/hooks/lib/risk-engine.js +368 -0
- package/hooks/lib/run-log.js +408 -0
- package/hooks/lib/session-fts.js +379 -0
- package/hooks/lib/session-store.js +293 -0
- package/hooks/lib/singleton-guard.js +159 -0
- package/hooks/lib/skill-auditor.js +588 -0
- package/hooks/lib/sync-status.js +228 -0
- package/hooks/lib/taint-tracker.js +107 -0
- package/hooks/lib/task-service.js +295 -0
- package/hooks/lib/tech-skills-map.js +146 -0
- package/hooks/lib/telegram-cliente.js +159 -0
- package/hooks/lib/telegram-config.js +170 -0
- package/hooks/lib/token-budget.js +156 -0
- package/hooks/lib/token-estimator.js +420 -0
- package/hooks/lib/toon-compressor.js +245 -0
- package/hooks/lib/usage-model.js +183 -0
- package/hooks/lib/variable-resolver.js +230 -0
- package/hooks/linea-estado.js +324 -0
- package/hooks/metricas-evolucion.js +209 -0
- package/hooks/monitor-contexto.js +325 -0
- package/hooks/notificacion-sesion-stop.js +198 -0
- package/hooks/notificacion-telegram-notification.js +4 -0
- package/hooks/notificacion-telegram-subagent.js +4 -0
- package/hooks/notificacion-telegram.js +267 -0
- package/hooks/preservar-estado-pre-compact.js +150 -0
- package/hooks/proteccion-rutas.js +366 -0
- package/hooks/registro-turnos.js +209 -0
- package/hooks/resumen-sesion.js +249 -0
- package/hooks/risk-scoring.js +323 -0
- package/hooks/rotar-audit-auto.js +122 -0
- package/hooks/sugerir-regenerar-inventario.js +170 -0
- package/hooks/telemetria-agentes.js +167 -0
- package/hooks/tracking-costos.js +688 -0
- package/instintos/global.yaml +8 -0
- package/instintos/perfil-usuario.yaml +53 -0
- package/instintos/prompt-appendices.yaml +57 -0
- package/instintos/proyecto.yaml +372 -0
- package/manifiestos/gateway-config.json +77 -0
- package/manifiestos/handoff-context.json +223 -0
- package/manifiestos/hook-profiles.json +44 -0
- package/manifiestos/hooks-config.json +360 -0
- package/manifiestos/modulos.json +1173 -0
- package/manifiestos/perfiles.json +404 -0
- package/package.json +86 -0
- package/plantillas/ESTADO.md +109 -0
- package/plantillas/HOJA-RUTA.md +143 -0
- package/plantillas/PROYECTO.md +122 -0
- package/plantillas/REQUISITOS.md +132 -0
- package/plantillas/auditor-veto-template.md +105 -0
- package/plantillas/github-workflows/README.md +47 -0
- package/plantillas/github-workflows/release-please.yml +44 -0
- package/plantillas/github-workflows/swl-ci.yml +107 -0
- package/plantillas/github-workflows/swl-security.yml +51 -0
- package/plantillas/mcp-mineru.json +13 -0
- package/plantillas/research/ARQUITECTURA.md +220 -0
- package/plantillas/research/FUNCIONALIDADES.md +175 -0
- package/plantillas/research/RESUMEN.md +165 -0
- package/plantillas/research/STACK.md +233 -0
- package/plantillas/research/TRAMPAS.md +299 -0
- package/plantillas/skill-evals-template.json +44 -0
- package/plugin.json +343 -0
- package/reglas/accesibilidad.md +269 -0
- package/reglas/api-diseno.md +400 -0
- package/reglas/arquitectura.md +352 -0
- package/reglas/brevedad-output.md +124 -0
- package/reglas/cloud-infra.md +247 -0
- package/reglas/docs.md +245 -0
- package/reglas/estilo-codigo.md +201 -0
- package/reglas/git-workflow.md +245 -0
- package/reglas/gobernanza.md +271 -0
- package/reglas/harness-claude-code.md +213 -0
- package/reglas/hooks.md +186 -0
- package/reglas/lenguajes/csharp/estilo-codigo.md +231 -0
- package/reglas/lenguajes/csharp/hooks.md +281 -0
- package/reglas/lenguajes/csharp/patrones.md +226 -0
- package/reglas/lenguajes/csharp/seguridad.md +258 -0
- package/reglas/lenguajes/csharp/testing.md +176 -0
- package/reglas/lenguajes/go/estilo-codigo.md +195 -0
- package/reglas/lenguajes/go/hooks.md +249 -0
- package/reglas/lenguajes/go/patrones.md +249 -0
- package/reglas/lenguajes/go/seguridad.md +225 -0
- package/reglas/lenguajes/go/testing.md +272 -0
- package/reglas/lenguajes/java/estilo-codigo.md +217 -0
- package/reglas/lenguajes/java/hooks.md +251 -0
- package/reglas/lenguajes/java/patrones.md +226 -0
- package/reglas/lenguajes/java/seguridad.md +233 -0
- package/reglas/lenguajes/java/testing.md +238 -0
- package/reglas/lenguajes/kotlin/estilo-codigo.md +208 -0
- package/reglas/lenguajes/kotlin/hooks.md +245 -0
- package/reglas/lenguajes/kotlin/patrones.md +201 -0
- package/reglas/lenguajes/kotlin/seguridad.md +202 -0
- package/reglas/lenguajes/kotlin/testing.md +236 -0
- package/reglas/lenguajes/nextjs/estilo-codigo.md +175 -0
- package/reglas/lenguajes/nextjs/hooks.md +186 -0
- package/reglas/lenguajes/nextjs/patrones.md +225 -0
- package/reglas/lenguajes/nextjs/seguridad.md +216 -0
- package/reglas/lenguajes/nextjs/testing.md +193 -0
- package/reglas/lenguajes/php/estilo-codigo.md +228 -0
- package/reglas/lenguajes/php/hooks.md +165 -0
- package/reglas/lenguajes/php/patrones.md +233 -0
- package/reglas/lenguajes/php/seguridad.md +186 -0
- package/reglas/lenguajes/php/testing.md +205 -0
- package/reglas/lenguajes/rust/estilo-codigo.md +207 -0
- package/reglas/lenguajes/rust/hooks.md +240 -0
- package/reglas/lenguajes/rust/patrones.md +250 -0
- package/reglas/lenguajes/rust/seguridad.md +221 -0
- package/reglas/lenguajes/rust/testing.md +194 -0
- package/reglas/lenguajes/swift/estilo-codigo.md +238 -0
- package/reglas/lenguajes/swift/hooks.md +257 -0
- package/reglas/lenguajes/swift/patrones.md +235 -0
- package/reglas/lenguajes/swift/seguridad.md +248 -0
- package/reglas/lenguajes/swift/testing.md +242 -0
- package/reglas/markitdown.md +60 -0
- package/reglas/memoria-consolidada.md +209 -0
- package/reglas/patrones.md +225 -0
- package/reglas/performance.md +195 -0
- package/reglas/pruebas.md +159 -0
- package/reglas/seguridad-agentes.md +351 -0
- package/reglas/seguridad.md +151 -0
- package/reglas/skills-estandar.md +373 -0
- package/reglas/testing.md +193 -0
- package/schemas/agent-contract.json +176 -0
- package/schemas/agent-frontmatter.schema.json +149 -0
- package/schemas/agent-message.schema.json +53 -0
- package/schemas/agent-output-implementacion.schema.json +85 -0
- package/schemas/agent-output-planificacion.schema.json +113 -0
- package/schemas/agent-output-review.schema.json +78 -0
- package/schemas/diary-entry.schema.json +80 -0
- package/schemas/hook-profiles.schema.json +39 -0
- package/schemas/hooks-config.schema.json +74 -0
- package/schemas/instinct.schema.json +115 -0
- package/schemas/modulos.schema.json +29 -0
- package/schemas/perfiles.schema.json +28 -0
- package/schemas/plugin.schema.json +64 -0
- package/schemas/skill-evals.schema.json +95 -0
- package/schemas/skill-frontmatter.schema.json +170 -0
- package/scripts/actualizar.js +145 -0
- package/scripts/audit-skills.sh +78 -0
- package/scripts/auditar-agentes-gaps.js +149 -0
- package/scripts/auditar-cobertura-frameworks.js +241 -0
- package/scripts/auditar-skills-gaps.js +206 -0
- package/scripts/bootstrap-instintos.js +259 -0
- package/scripts/check-update.js +109 -0
- package/scripts/comandos/agents.js +105 -0
- package/scripts/comandos/info.js +108 -0
- package/scripts/comandos/install-asistido.js +186 -0
- package/scripts/comandos/skills.js +211 -0
- package/scripts/configurar-branch-protection.js +418 -0
- package/scripts/daemon-swl.py +388 -0
- package/scripts/desinstalar.js +130 -0
- package/scripts/doctor.js +559 -0
- package/scripts/field-report.js +199 -0
- package/scripts/generar-inventario.js +317 -0
- package/scripts/inbox-tmux-inject.js +161 -0
- package/scripts/inferir-herramientas-permitidas.js +586 -0
- package/scripts/inicializar.js +133 -0
- package/scripts/instalador.js +1031 -0
- package/scripts/instalar-git-hook.js +122 -0
- package/scripts/lib/agp-frontmatter.js +222 -0
- package/scripts/lib/append-con-marcadores.js +199 -0
- package/scripts/lib/artefactos-python.js +43 -0
- package/scripts/lib/audit-query.js +221 -0
- package/scripts/lib/autostart-linux.js +347 -0
- package/scripts/lib/autostart-macos.js +360 -0
- package/scripts/lib/autostart-windows.js +307 -0
- package/scripts/lib/budget-enforcer.js +252 -0
- package/scripts/lib/claude-sessions.js +285 -0
- package/scripts/lib/configurar-ci.js +380 -0
- package/scripts/lib/console-span-exporter.js +92 -0
- package/scripts/lib/contadores-inventario.js +217 -0
- package/scripts/lib/dashboard-widgets.js +290 -0
- package/scripts/lib/detectar-runtime.js +279 -0
- package/scripts/lib/detectar-stack.js +187 -0
- package/scripts/lib/diary-entry.js +234 -0
- package/scripts/lib/drift-detector.js +545 -0
- package/scripts/lib/estado.js +124 -0
- package/scripts/lib/gestor-componentes.js +243 -0
- package/scripts/lib/gitignore-manifest.js +305 -0
- package/scripts/lib/graph-analyze.py +556 -0
- package/scripts/lib/graph-builder.py +485 -0
- package/scripts/lib/graph-cluster.py +259 -0
- package/scripts/lib/health-row.js +168 -0
- package/scripts/lib/hooks-settings.js +789 -0
- package/scripts/lib/manifiestos.js +138 -0
- package/scripts/lib/mc-client.js +137 -0
- package/scripts/lib/notificaciones-telegram.js +1107 -0
- package/scripts/lib/npm-version.js +261 -0
- package/scripts/lib/paquetes-conocidos.js +50 -0
- package/scripts/lib/preservar-usuario.js +586 -0
- package/scripts/lib/prompt-builder.js +264 -0
- package/scripts/lib/resolver-externo.js +332 -0
- package/scripts/lib/schedule-parser.js +305 -0
- package/scripts/lib/scoring-instintos.js +240 -0
- package/scripts/lib/seguridad.js +160 -0
- package/scripts/lib/selector-interactivo.js +152 -0
- package/scripts/lib/semantic-search.js +242 -0
- package/scripts/lib/skill-discovery.js +234 -0
- package/scripts/lib/skill-metrics.js +246 -0
- package/scripts/lib/skill-normalizer.js +112 -0
- package/scripts/lib/skills-hub.js +340 -0
- package/scripts/lib/span-schema.js +134 -0
- package/scripts/lib/tool-cost-analyzer.js +255 -0
- package/scripts/lib/tracing-processor-interface.js +286 -0
- package/scripts/lib/transformadores/base.js +80 -0
- package/scripts/lib/transformadores/claude.js +124 -0
- package/scripts/lib/transformadores/codex.js +115 -0
- package/scripts/lib/transformadores/copilot.js +106 -0
- package/scripts/lib/transformadores/gemini.js +74 -0
- package/scripts/lib/transformadores/index.js +35 -0
- package/scripts/lib/transformadores/opencode.js +75 -0
- package/scripts/lib/ui.js +259 -0
- package/scripts/limpiar-artefactos-python.js +131 -0
- package/scripts/mcp-orchestrator.py +386 -0
- package/scripts/mcp-pool-manager.py +352 -0
- package/scripts/mcp-telemetry.py +378 -0
- package/scripts/poblar-evolvable.js +226 -0
- package/scripts/publicar.js +287 -0
- package/scripts/reflect-skills.js +403 -0
- package/scripts/rotar-audit-logs.js +185 -0
- package/scripts/run-skill-evals.js +242 -0
- package/scripts/smoke-test.js +374 -0
- package/scripts/token-analysis.py +471 -0
- package/scripts/validar-manifest.js +195 -0
- package/scripts/validar-memoria.js +321 -0
- package/scripts/validar-tests-aislamiento.js +184 -0
- package/scripts/validar-tokens-test.js +208 -0
- package/scripts/validar.js +147 -0
- package/scripts/validate-markdown.py +339 -0
- package/scripts/validate-skills.py +385 -0
- package/scripts/vendor/claude-usage/README.md +116 -0
- package/scripts/vendor/claude-usage/cli.py +334 -0
- package/scripts/vendor/claude-usage/dashboard.py +795 -0
- package/scripts/vendor/claude-usage/scanner.py +467 -0
- package/scripts/vendor/markitdown/cli.py +194 -0
- package/scripts/verificar-evolucion.js +289 -0
- package/scripts/verificar-release.js +494 -0
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: terraform-experto
|
|
3
|
+
description: >
|
|
4
|
+
Terraform: module design, remote state con S3+DynamoDB, workspaces, import,
|
|
5
|
+
moved blocks, lifecycle rules, for_each y seguridad con checkov/tfsec. Cargar
|
|
6
|
+
cuando se escriba infraestructura como código o se diseñen modules Terraform.
|
|
7
|
+
version: "1.0.0"
|
|
8
|
+
herramientasPermitidas: [Read, Grep]
|
|
9
|
+
exclusiones:
|
|
10
|
+
- "No cargar para Kubernetes manifiestos YAML o Helm charts — para Kubernetes cargar `kubernetes-orquestacion`."
|
|
11
|
+
- "No cargar para Pulumi, CDK o CloudFormation — son herramientas IaC distintas con APIs y DSLs incompatibles con Terraform HCL."
|
|
12
|
+
- "No cargar para Docker y containerización — para Docker cargar `contenedores-docker`."
|
|
13
|
+
- "No cargar para recursos cloud específicos (políticas IAM de AWS, redes de Azure) sin contexto Terraform — para cloud sin IaC cargar `cloud-aws`, `azure-cloud` o `gcp-cloud`."
|
|
14
|
+
evolvable: true # default para skill estandar
|
|
15
|
+
---
|
|
16
|
+
# Terraform Experto — IaC Moderno
|
|
17
|
+
|
|
18
|
+
Terraform es el estándar de IaC multi-cloud. Un módulo bien diseñado es pequeño,
|
|
19
|
+
reutilizable y con interfaz minimal. El estado remoto con locking es obligatorio
|
|
20
|
+
en equipos. Este skill cubre el diseño de módulos, estrategias de estado, patrones
|
|
21
|
+
de seguridad y el flujo correcto de trabajo.
|
|
22
|
+
|
|
23
|
+
## Cuándo NO cargar
|
|
24
|
+
|
|
25
|
+
- La tarea es Kubernetes YAML o Helm charts — cargar `kubernetes-orquestacion`.
|
|
26
|
+
- La herramienta IaC es Pulumi, AWS CDK o CloudFormation — tienen DSLs incompatibles con HCL.
|
|
27
|
+
- El trabajo es Docker y containerización — cargar `contenedores-docker`.
|
|
28
|
+
- La pregunta es sobre recursos cloud (IAM, networking) fuera del contexto de Terraform — cargar `cloud-aws`, `azure-cloud` o `gcp-cloud`.
|
|
29
|
+
|
|
30
|
+
## Cuándo cargar este skill
|
|
31
|
+
|
|
32
|
+
Invoca `Skill("terraform-experto")` cuando:
|
|
33
|
+
|
|
34
|
+
- Se escriban resources, modules o data sources de Terraform
|
|
35
|
+
- Se diseñe la estructura de módulos para un proyecto nuevo
|
|
36
|
+
- Se configure remote state con locking
|
|
37
|
+
- Se importe infraestructura existente con `terraform import` o import blocks
|
|
38
|
+
- Se evaluen estrategias de workspaces vs multiple state files
|
|
39
|
+
- Se configure checkov o tfsec para seguridad
|
|
40
|
+
|
|
41
|
+
## Conceptos clave
|
|
42
|
+
|
|
43
|
+
### Modules — la unidad de reutilización
|
|
44
|
+
|
|
45
|
+
Un módulo es un directorio con archivos `.tf`. El módulo raíz es el que se aplica
|
|
46
|
+
directamente. Los módulos hijo encapsulan recursos relacionados con una interfaz
|
|
47
|
+
de `variables.tf` (inputs) y `outputs.tf` (outputs). Un buen módulo tiene más
|
|
48
|
+
implementación que interfaz.
|
|
49
|
+
|
|
50
|
+
### Remote State con Locking
|
|
51
|
+
|
|
52
|
+
El estado de Terraform contiene la información de todos los recursos gestionados.
|
|
53
|
+
En equipos, debe almacenarse en backend remoto (S3, GCS, Terraform Cloud) con
|
|
54
|
+
locking habilitado para evitar applies concurrentes. DynamoDB como lock en AWS,
|
|
55
|
+
GCS nativo en GCP.
|
|
56
|
+
|
|
57
|
+
### for_each vs count
|
|
58
|
+
|
|
59
|
+
`count` genera recursos por índice numérico — frágil porque cambiar el orden
|
|
60
|
+
recrea recursos. `for_each` genera recursos por clave de mapa o set — estable
|
|
61
|
+
porque la clave identifica el recurso. Preferir `for_each` siempre que sea posible.
|
|
62
|
+
|
|
63
|
+
## Reglas obligatorias
|
|
64
|
+
|
|
65
|
+
### Terraform plan SIEMPRE antes de apply — nunca apply directo
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
# MAL — apply sin revisar el plan
|
|
69
|
+
terraform apply
|
|
70
|
+
|
|
71
|
+
# BIEN — plan con output a archivo, luego apply del mismo plan
|
|
72
|
+
terraform plan -out=tfplan
|
|
73
|
+
# Revisar el plan: terraform show -json tfplan | jq '.resource_changes[]'
|
|
74
|
+
terraform apply tfplan
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Por qué**: Un `terraform apply` sin plan puede destruir recursos si el estado
|
|
78
|
+
está desactualizado o si hay cambios de configuración inesperados.
|
|
79
|
+
|
|
80
|
+
### NUNCA hardcodear valores — usar variables con validación
|
|
81
|
+
|
|
82
|
+
```hcl
|
|
83
|
+
# MAL — valores hardcodeados no reutilizables
|
|
84
|
+
resource "aws_instance" "web" {
|
|
85
|
+
instance_type = "t3.micro"
|
|
86
|
+
ami = "ami-0c55b159cbfafe1f0"
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
# BIEN — variables con validacion y descripcion
|
|
90
|
+
variable "instance_type" {
|
|
91
|
+
type = string
|
|
92
|
+
description = "Tipo de instancia EC2"
|
|
93
|
+
default = "t3.micro"
|
|
94
|
+
|
|
95
|
+
validation {
|
|
96
|
+
condition = contains(["t3.micro", "t3.small", "t3.medium"], var.instance_type)
|
|
97
|
+
error_message = "Solo se permiten instancias t3.micro, t3.small o t3.medium."
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Modules con versión pinneada — nunca rangos abiertos
|
|
103
|
+
|
|
104
|
+
```hcl
|
|
105
|
+
# MAL — version no pinneada, puede romper en el proximo apply
|
|
106
|
+
module "vpc" {
|
|
107
|
+
source = "terraform-aws-modules/vpc/aws"
|
|
108
|
+
version = "~> 5.0" # cualquier 5.x puede incluir breaking changes
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
# BIEN — version exacta, actualizar conscientemente
|
|
112
|
+
module "vpc" {
|
|
113
|
+
source = "terraform-aws-modules/vpc/aws"
|
|
114
|
+
version = "5.5.3"
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### prevent_destroy para recursos críticos de datos
|
|
119
|
+
|
|
120
|
+
```hcl
|
|
121
|
+
resource "aws_db_instance" "principal" {
|
|
122
|
+
# ...configuracion...
|
|
123
|
+
|
|
124
|
+
lifecycle {
|
|
125
|
+
prevent_destroy = true # falla el plan si algo intenta destruir esta BD
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Patrones recomendados
|
|
131
|
+
|
|
132
|
+
### Estructura de módulo bien organizado
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
modulo-vpc/
|
|
136
|
+
├── main.tf # Resources principales
|
|
137
|
+
├── variables.tf # Inputs con descripcion y validacion
|
|
138
|
+
├── outputs.tf # Outputs documentados
|
|
139
|
+
├── versions.tf # required_providers y terraform version
|
|
140
|
+
└── README.md # Generado con terraform-docs
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
```hcl
|
|
144
|
+
# versions.tf
|
|
145
|
+
terraform {
|
|
146
|
+
required_version = ">= 1.6.0"
|
|
147
|
+
|
|
148
|
+
required_providers {
|
|
149
|
+
aws = {
|
|
150
|
+
source = "hashicorp/aws"
|
|
151
|
+
version = "~> 5.0"
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Remote State con S3 + DynamoDB (AWS)
|
|
158
|
+
|
|
159
|
+
```hcl
|
|
160
|
+
# backend.tf
|
|
161
|
+
terraform {
|
|
162
|
+
backend "s3" {
|
|
163
|
+
bucket = "mi-empresa-terraform-state"
|
|
164
|
+
key = "produccion/vpc/terraform.tfstate"
|
|
165
|
+
region = "us-east-1"
|
|
166
|
+
encrypt = true # cifrado en reposo OBLIGATORIO
|
|
167
|
+
dynamodb_table = "terraform-state-locks" # locking
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### for_each para múltiples recursos similares
|
|
173
|
+
|
|
174
|
+
```hcl
|
|
175
|
+
variable "ambientes" {
|
|
176
|
+
type = map(object({
|
|
177
|
+
cidr_block = string
|
|
178
|
+
enable_nat = bool
|
|
179
|
+
}))
|
|
180
|
+
default = {
|
|
181
|
+
staging = { cidr_block = "10.1.0.0/16", enable_nat = false }
|
|
182
|
+
prod = { cidr_block = "10.0.0.0/16", enable_nat = true }
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
resource "aws_vpc" "ambientes" {
|
|
187
|
+
for_each = var.ambientes
|
|
188
|
+
cidr_block = each.value.cidr_block
|
|
189
|
+
|
|
190
|
+
tags = {
|
|
191
|
+
Name = "vpc-${each.key}"
|
|
192
|
+
Ambiente = each.key
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Import block (Terraform 1.5+) para infraestructura existente
|
|
198
|
+
|
|
199
|
+
```hcl
|
|
200
|
+
# import.tf — declarativo, incluible en el codigo
|
|
201
|
+
import {
|
|
202
|
+
to = aws_s3_bucket.logs
|
|
203
|
+
id = "mi-bucket-logs-existente"
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
resource "aws_s3_bucket" "logs" {
|
|
207
|
+
bucket = "mi-bucket-logs-existente"
|
|
208
|
+
# terraform genera el codigo inicial con: terraform plan -generate-config-out=generated.tf
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### moved block para refactorizar sin destruir recursos
|
|
213
|
+
|
|
214
|
+
```hcl
|
|
215
|
+
# Al renombrar un resource o moverlo a un modulo
|
|
216
|
+
moved {
|
|
217
|
+
from = aws_security_group.web
|
|
218
|
+
to = module.web_server.aws_security_group.this
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Dynamic blocks para configuración variable
|
|
223
|
+
|
|
224
|
+
```hcl
|
|
225
|
+
resource "aws_security_group" "web" {
|
|
226
|
+
name = "sg-web"
|
|
227
|
+
|
|
228
|
+
dynamic "ingress" {
|
|
229
|
+
for_each = var.puertos_permitidos
|
|
230
|
+
content {
|
|
231
|
+
from_port = ingress.value
|
|
232
|
+
to_port = ingress.value
|
|
233
|
+
protocol = "tcp"
|
|
234
|
+
cidr_blocks = ["0.0.0.0/0"]
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Locals para expresiones reutilizables
|
|
241
|
+
|
|
242
|
+
```hcl
|
|
243
|
+
locals {
|
|
244
|
+
tags_comunes = {
|
|
245
|
+
proyecto = var.nombre_proyecto
|
|
246
|
+
ambiente = var.ambiente
|
|
247
|
+
gestionado = "terraform"
|
|
248
|
+
equipo = var.equipo
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
prefijo = "${var.nombre_proyecto}-${var.ambiente}"
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
resource "aws_s3_bucket" "datos" {
|
|
255
|
+
bucket = "${local.prefijo}-datos"
|
|
256
|
+
tags = local.tags_comunes
|
|
257
|
+
}
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
## Anti-patrones conocidos
|
|
261
|
+
|
|
262
|
+
### Estado local en equipo — siempre usar backend remoto
|
|
263
|
+
|
|
264
|
+
```hcl
|
|
265
|
+
# MAL — estado en disco local (default si no se configura backend)
|
|
266
|
+
# El estado se pierde si el disco falla y no se puede trabajar en equipo
|
|
267
|
+
|
|
268
|
+
# BIEN — siempre configurar backend remoto antes del primer apply
|
|
269
|
+
terraform {
|
|
270
|
+
backend "s3" { ... }
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### Módulos monolíticos con 500+ líneas
|
|
275
|
+
|
|
276
|
+
Un módulo con 500 resources mezcla responsabilidades y es imposible de reutilizar.
|
|
277
|
+
Dividir por dominio: modulo-red, modulo-compute, modulo-bd. La interfaz de cada
|
|
278
|
+
módulo debe ser pequeña comparada con su implementación.
|
|
279
|
+
|
|
280
|
+
### ignore_changes para ocultar drift — investigar la causa en su lugar
|
|
281
|
+
|
|
282
|
+
```hcl
|
|
283
|
+
# SOLO usar ignore_changes cuando el recurso se modifica por un proceso externo
|
|
284
|
+
# documentado. Nunca para ocultar conflictos sin entender la causa.
|
|
285
|
+
lifecycle {
|
|
286
|
+
ignore_changes = [
|
|
287
|
+
tags["LastModified"], # etiqueta que AWS actualiza automaticamente
|
|
288
|
+
]
|
|
289
|
+
}
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
## Gotchas / Errores comunes no obvios
|
|
293
|
+
|
|
294
|
+
**`count` en lugar de `for_each` causa recreación de recursos si se elimina un elemento del medio de la lista**: `count = length(var.servidores)` crea recursos indexados `aws_instance.web[0]`, `[1]`, `[2]` — si se elimina `servidores[1]`, Terraform recrea `[1]` (con los valores de `[2]`) y destruye `[2]`. Causa: los recursos con `count` son identificados por índice numérico, no por valor. Fix: siempre usar `for_each = toset(var.servidores)` para colecciones — los recursos se identifican por la clave del mapa/set y la eliminación de un elemento no afecta a los demás.
|
|
295
|
+
|
|
296
|
+
**`terraform apply` en CI sin `-auto-approve` bloquea esperando input interactivo**: si el pipeline no pasa `-auto-approve`, Terraform espera `yes` del usuario — en CI no hay terminal interactivo y el job se congela hasta el timeout. Causa: comportamiento por defecto de Terraform para prevenir applies accidentales. Fix: en CI SIEMPRE usar `terraform apply -auto-approve tfplan` donde `tfplan` es el archivo de plan generado previamente con `terraform plan -out=tfplan` — nunca `terraform apply -auto-approve` sin plan previo.
|
|
297
|
+
|
|
298
|
+
**`terraform state` modificado manualmente (mv, rm) sin un `moved` block crea drift que otros membres del equipo no ven**: `terraform state mv` modifica el state file remoto pero no deja evidencia en el código HCL — otros miembros que ejecuten `terraform plan` ven diferencias inesperadas. Causa: el state file es el source of truth pero no está versionado junto con el código. Fix: para renombrar o mover recursos, usar siempre `moved {}` blocks en el HCL (Terraform 1.1+) que se commitean al repositorio — son la forma declarativa y rastreable de refactorizar recursos.
|
|
299
|
+
|
|
300
|
+
**`for_each` con un `map` que contiene valores de otro resource que no existe aún causa error de "value not known until apply"**: `for_each = var.ips_elasticas` donde `var.ips_elasticas` es un output de otro resource que se crea en el mismo apply lanza error porque el set de `for_each` debe ser conocido antes del apply. Causa: Terraform necesita conocer las claves del `for_each` en la fase de plan para saber cuántos recursos crear. Fix: usar valores estáticos como claves de `for_each` (strings conocidos) y referenciar el resource dinámico como atributo del objeto, no como clave.
|
|
301
|
+
|
|
302
|
+
## Checklist de verificación
|
|
303
|
+
|
|
304
|
+
- [ ] Plan revisado antes de cada apply
|
|
305
|
+
- [ ] Backend remoto con locking configurado desde el inicio
|
|
306
|
+
- [ ] Módulos con versión exacta pinneada
|
|
307
|
+
- [ ] Variables con `description` y `validation` donde aplica
|
|
308
|
+
- [ ] `prevent_destroy` en recursos críticos (BD, buckets con datos)
|
|
309
|
+
- [ ] `for_each` en lugar de `count` para recursos en colecciones
|
|
310
|
+
- [ ] Tags obligatorios definidos en `locals` y aplicados a todos los recursos
|
|
311
|
+
- [ ] checkov o tfsec ejecutado en CI sin errores HIGH/CRITICAL
|
|
312
|
+
|
|
313
|
+
## Referencias
|
|
314
|
+
|
|
315
|
+
- [Terraform Documentation](https://developer.hashicorp.com/terraform/docs)
|
|
316
|
+
- [terraform-aws-modules](https://registry.terraform.io/namespaces/terraform-aws-modules)
|
|
317
|
+
- [checkov](https://www.checkov.io)
|
|
318
|
+
- [tfsec](https://aquasecurity.github.io/tfsec)
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
*Skill creado con swl:crear-skill el 2026-03-31. Versión 1.0.0.*
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: testing-python
|
|
3
|
+
description: Testing Python con pytest. Fixtures, mocking, parametrize, cobertura, factories, testing async y patrones de test doubles. TDD y testing de capas separadas.
|
|
4
|
+
version: "1.2.1"
|
|
5
|
+
herramientasPermitidas: [Read, Bash]
|
|
6
|
+
exclusiones:
|
|
7
|
+
- "No cargar para tests de integración específicos de FastAPI (TestClient, AsyncClient con httpx) — esos tienen fixtures específicos del framework; cargar `fastapi-experto` que incluye la sección de testing con httpx."
|
|
8
|
+
- "No cargar para tests de Django (pytest-django, `@pytest.mark.django_db`, factory-boy para modelos Django) — cargar `django-experto` que cubre el testing con el setup de settings y base de datos."
|
|
9
|
+
- "No cargar para métricas de calidad del código (cobertura, complejidad ciclomática, análisis estático) — esas métricas se cubren en `checklist-calidad`; este skill es para escribir tests, no para analizar su cobertura."
|
|
10
|
+
- "No cargar para evaluar la calidad de un test suite existente de un proyecto — para auditoría de tests cargar `checklist-calidad` o invocar `revisor-codigo-swl`."
|
|
11
|
+
evolvable: true # default para skill estandar
|
|
12
|
+
evolved: true
|
|
13
|
+
evolved-from: "5.12.3"
|
|
14
|
+
evolved-at: "2026-04-25"
|
|
15
|
+
evolved-by: "aprender"
|
|
16
|
+
evolved-note: "2 gotchas nuevos: chdir vs __dirname y sanitizar-antes-de-truncar"
|
|
17
|
+
---
|
|
18
|
+
# Testing Python con pytest
|
|
19
|
+
|
|
20
|
+
## Cuándo NO cargar
|
|
21
|
+
|
|
22
|
+
- Los tests son de FastAPI con AsyncClient/httpx — cargar `fastapi-experto` para el contexto de fixtures de ese framework.
|
|
23
|
+
- Los tests son de Django con pytest-django y `@pytest.mark.django_db` — cargar `django-experto`.
|
|
24
|
+
- La tarea es auditar la cobertura existente — cargar `checklist-calidad` para métricas de calidad.
|
|
25
|
+
|
|
26
|
+
## Principios de testing
|
|
27
|
+
|
|
28
|
+
- **Un test verifica un solo comportamiento** — no múltiples cosas a la vez.
|
|
29
|
+
- **Los tests son documentación** — el nombre del test debe describir QUÉ se prueba.
|
|
30
|
+
- **Test doubles solo cuando es necesario** — no mockear lo que puedes usar real.
|
|
31
|
+
- **Tests rápidos > tests lentos** — unitarios son milisegundos; de integración, segundos.
|
|
32
|
+
- **Cobertura de líneas no es meta** — cobertura de comportamientos sí lo es.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Estructura de proyecto de tests
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
tests/
|
|
40
|
+
├── conftest.py # Fixtures compartidos globalmente
|
|
41
|
+
├── unit/ # Tests unitarios (sin I/O)
|
|
42
|
+
│ ├── conftest.py
|
|
43
|
+
│ ├── test_factura_service.py
|
|
44
|
+
│ └── test_validaciones.py
|
|
45
|
+
├── integration/ # Tests con BD real (o en memoria)
|
|
46
|
+
│ ├── conftest.py
|
|
47
|
+
│ └── test_factura_api.py
|
|
48
|
+
└── e2e/ # Tests end-to-end (opcional)
|
|
49
|
+
└── test_flujo_facturacion.py
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Nomenclatura de tests
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
# Patrón: test_<qué>_<condición>_<resultado_esperado>
|
|
58
|
+
def test_calcular_iva_monto_positivo_retorna_monto_correcto(): ...
|
|
59
|
+
def test_calcular_iva_monto_negativo_lanza_valor_error(): ...
|
|
60
|
+
def test_crear_factura_cliente_inactivo_lanza_error_negocio(): ...
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Fixtures
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
# conftest.py
|
|
69
|
+
import pytest
|
|
70
|
+
from decimal import Decimal
|
|
71
|
+
|
|
72
|
+
@pytest.fixture
|
|
73
|
+
def monto_valido() -> Decimal:
|
|
74
|
+
return Decimal("1000.00")
|
|
75
|
+
|
|
76
|
+
@pytest.fixture
|
|
77
|
+
def factura_data() -> dict:
|
|
78
|
+
return {
|
|
79
|
+
"folio": "F-001",
|
|
80
|
+
"fecha": "2026-03-25",
|
|
81
|
+
"subtotal": Decimal("1000.00"),
|
|
82
|
+
"cliente_id": "cliente-123",
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
# Fixture con scope para BD — se crea una vez por sesión
|
|
86
|
+
@pytest.fixture(scope="session")
|
|
87
|
+
def engine():
|
|
88
|
+
from sqlalchemy import create_engine
|
|
89
|
+
engine = create_engine("sqlite:///:memory:")
|
|
90
|
+
Base.metadata.create_all(engine)
|
|
91
|
+
yield engine
|
|
92
|
+
Base.metadata.drop_all(engine)
|
|
93
|
+
|
|
94
|
+
@pytest.fixture
|
|
95
|
+
def db(engine):
|
|
96
|
+
from sqlalchemy.orm import Session
|
|
97
|
+
with Session(engine) as session:
|
|
98
|
+
yield session
|
|
99
|
+
session.rollback() # Limpiar después de cada test
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Parametrize — probar múltiples casos
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
import pytest
|
|
108
|
+
from decimal import Decimal
|
|
109
|
+
|
|
110
|
+
@pytest.mark.parametrize("monto,tasa,esperado", [
|
|
111
|
+
(Decimal("100.00"), 0.16, Decimal("16.00")),
|
|
112
|
+
(Decimal("0.00"), 0.16, Decimal("0.00")),
|
|
113
|
+
(Decimal("999.99"), 0.16, Decimal("159.998")),
|
|
114
|
+
])
|
|
115
|
+
def test_calcular_iva(monto, tasa, esperado):
|
|
116
|
+
resultado = calcular_iva(monto, tasa)
|
|
117
|
+
assert resultado == pytest.approx(float(esperado), rel=1e-4)
|
|
118
|
+
|
|
119
|
+
# Parametrize con IDs descriptivos
|
|
120
|
+
@pytest.mark.parametrize("estatus,puede_cancelar", [
|
|
121
|
+
pytest.param("borrador", True, id="borrador-puede-cancelar"),
|
|
122
|
+
pytest.param("cancelada", False, id="cancelada-ya-cancelada"),
|
|
123
|
+
])
|
|
124
|
+
def test_factura_puede_cancelar(estatus, puede_cancelar):
|
|
125
|
+
factura = Factura(estatus=estatus)
|
|
126
|
+
assert factura.puede_cancelar() == puede_cancelar
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Mocking — reglas clave
|
|
132
|
+
|
|
133
|
+
- Mockear en el boundary: BD, APIs externas, filesystem, reloj.
|
|
134
|
+
- NUNCA mockear código propio — señal de acoplamiento excesivo.
|
|
135
|
+
- Usar `pytest-mock` (mocker) sobre `unittest.mock.patch` para mayor limpieza.
|
|
136
|
+
- `AsyncMock` para funciones async.
|
|
137
|
+
|
|
138
|
+
Para ejemplos completos de mocking, testing async y factories, ver [recursos/ejemplos-completos.md](recursos/ejemplos-completos.md).
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Factories con factory_boy — resumen
|
|
143
|
+
|
|
144
|
+
Usar factories sobre fixtures hardcodeados. Permiten sobreescribir solo lo relevante:
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
# tests/factories.py — definir factories centralizados
|
|
148
|
+
class FacturaFactory(factory.Factory):
|
|
149
|
+
class Meta:
|
|
150
|
+
model = Factura
|
|
151
|
+
estatus = "borrador"
|
|
152
|
+
cliente = factory.SubFactory(ClienteFactory)
|
|
153
|
+
|
|
154
|
+
# En el test — solo los datos que importan
|
|
155
|
+
factura = FacturaFactory(estatus="pagada", total=Decimal("1000.00"))
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Para ejemplos completos de factories con factory_boy, ver [recursos/ejemplos-completos.md](recursos/ejemplos-completos.md).
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## Cobertura de tests
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
# Ejecutar con cobertura
|
|
166
|
+
pytest --cov=app --cov-report=term-missing --cov-report=html
|
|
167
|
+
|
|
168
|
+
# Requerir cobertura mínima (falla si no se alcanza)
|
|
169
|
+
pytest --cov=app --cov-fail-under=85
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
```toml
|
|
173
|
+
# pyproject.toml
|
|
174
|
+
[tool.coverage.run]
|
|
175
|
+
source = ["app"]
|
|
176
|
+
omit = ["app/migrations/*", "app/main.py", "*/tests/*"]
|
|
177
|
+
|
|
178
|
+
[tool.coverage.report]
|
|
179
|
+
exclude_lines = [
|
|
180
|
+
"pragma: no cover",
|
|
181
|
+
"def __repr__",
|
|
182
|
+
"if TYPE_CHECKING:",
|
|
183
|
+
"raise NotImplementedError",
|
|
184
|
+
]
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## Markers y organización
|
|
190
|
+
|
|
191
|
+
```python
|
|
192
|
+
# Registrar markers en pyproject.toml
|
|
193
|
+
# [tool.pytest.ini_options]
|
|
194
|
+
# markers = [
|
|
195
|
+
# "slow: tests que tardan más de 1 segundo",
|
|
196
|
+
# "integration: tests que requieren BD o red",
|
|
197
|
+
# "unit: tests puramente unitarios",
|
|
198
|
+
# ]
|
|
199
|
+
|
|
200
|
+
@pytest.mark.slow
|
|
201
|
+
@pytest.mark.integration
|
|
202
|
+
async def test_proceso_completo_facturacion(): ...
|
|
203
|
+
|
|
204
|
+
# pytest -m "not integration" # Ejecutar solo unitarios
|
|
205
|
+
# pytest -x # Parar al primer fallo
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## Anti-patrones principales
|
|
211
|
+
|
|
212
|
+
- **Test que verifica demasiado**: un solo test con 10+ asserts sobre comportamientos distintos. Dividir en tests separados.
|
|
213
|
+
- **Lógica de negocio en tests**: duplicar if/else del código de producción en el test. Usar valores concretos con parametrize.
|
|
214
|
+
- **Sleep en tests**: NUNCA `time.sleep()`. Mockear el reloj con `freezegun`.
|
|
215
|
+
|
|
216
|
+
Para ejemplos detallados MAL vs BIEN de anti-patrones, ver [recursos/ejemplos-completos.md](recursos/ejemplos-completos.md).
|
|
217
|
+
|
|
218
|
+
## Gotchas / Errores comunes no obvios
|
|
219
|
+
|
|
220
|
+
- **`@pytest.fixture(scope="session")` con base de datos SQLAlchemy falla cuando un test modifica datos y el siguiente test los asume en el estado original**: el scope `session` significa que el fixture se crea una vez para toda la sesión de tests — si un test modifica la BD, los tests posteriores ven los datos modificados. Causa: `scope="session"` no hace rollback entre tests, a diferencia de `scope="function"`. Solución: usar `scope="function"` (default) para fixtures de BD que necesitan aislamiento, o envolver cada test en una transacción que se revierte con `db.rollback()` en el teardown del fixture.
|
|
221
|
+
- **`mock.patch` parcheado en el módulo de tests en lugar de en el módulo que lo usa**: el mock no tiene efecto porque la función ya fue importada en el módulo objetivo antes del patch. Causa: `mock.patch("tests.test_factura.calcular_iva")` parchea la referencia en el módulo de tests, pero `factura_service.py` ya importó `calcular_iva` directamente y sigue usando la original. Solución: patchear siempre en el lugar donde se usa la función: `mock.patch("factura_service.calcular_iva")` — el destino del patch debe ser la ruta del módulo que importó la función, no donde está definida.
|
|
222
|
+
- **`pytest-asyncio` marca el test como `async def` y pasa, pero el `await` dentro no se ejecuta**: el test parece correr sin errores pero la coroutine interna nunca se ejecuta. Causa: sin `@pytest.mark.asyncio` o sin `asyncio_mode = "auto"` en pytest.ini, pytest ejecuta la función async como síncrona — la coroutine se crea y se descarta sin ejecutar. Solución: agregar `@pytest.mark.asyncio` al test o configurar `asyncio_mode = "auto"` en `pytest.ini`; verificar con `pytest --tb=short -v` que el test no termina instantáneamente.
|
|
223
|
+
- **Factory Boy `SubFactory` genera objetos nuevos en cada test aunque el fixture del objeto padre ya existe**: la factory crea una instancia nueva del modelo relacionado en la BD aunque ya exista el objeto padre en el test. Causa: `factory.SubFactory(ClienteFactory)` siempre instancia un nuevo `Cliente` — no reutiliza el fixture del test. Solución: pasar el objeto padre existente al instanciar la factory: `FacturaFactory(cliente=cliente_existente)` — la factory sobreescribe el campo `cliente` con el objeto ya creado en lugar de crear uno nuevo.
|
|
224
|
+
- **`os.chdir()` (Python) o `process.chdir()` (Node) en tests no afecta módulos cargados con paths relativos basados en `__dirname`/`__file__`**: si un módulo calcula su ruta de datos al cargar con `path.resolve(__dirname, ...)` o `Path(__file__).parent`, los tests no pueden redirigir esa ruta cambiando el cwd — el path se evaluó al `require`/`import` y queda fijado. Caso real: test que cambia `process.chdir(tmpDir)` antes de llamar funciones que escriben a `.planning/evolucion/nudges.jsonl` pero `RUTA_NUDGES = path.resolve(__dirname, '..', '..', '.planning', ...)` apunta al proyecto real. Solución: dos opciones: (1) test de integración con backup/restore del archivo real (más simple cuando son pocos tests), o (2) refactor del módulo para aceptar override de ruta vía parámetro o variable de entorno (preferible si el módulo es muy testeable). Aplica también a Python con `pathlib.Path(__file__).parent`.
|
|
225
|
+
- **Sanitizar antes de truncar invalida assertions de longitud en tests**: un test que verifica `truncar('a'.repeat(300), 100).length === 100` falla porque `'a'.repeat(300)` matchea la regex de redact `\b[A-Za-z0-9_-]{32,}\b` y la función sanitiza primero produciendo `[REDACTED]` (10 chars) que no se trunca. Causa: el orden `sanitizar → truncar` reduce el texto antes de que truncar opere. Solución en tests: usar fixtures que NO triggeren patrones de redact (ej: texto con espacios cada N chars como `'palabra corta '.repeat(N)`); separar tests de sanitización y truncado en casos disjuntos. NO modificar la función para reordenar — sanitizar antes es correcto en producción.
|
|
226
|
+
|
|
227
|
+
## Refactorizar parsers: fixtures multi-formato ANTES del cambio
|
|
228
|
+
|
|
229
|
+
### SIEMPRE: tener fixtures de cada formato soportado antes de modificar un parser
|
|
230
|
+
|
|
231
|
+
**Cuándo aplicar**: antes de cambiar un regex, gramática o heurística que ya pasa tests para un formato A, y se quiere extender para cubrir un formato B distinto (ej. otro convertidor produce markdown con artefactos diferentes, u otro proveedor genera JSON con shape alternativa).
|
|
232
|
+
|
|
233
|
+
**Problema que previene**: al hacer un regex "más permisivo" para aceptar el formato B, es frecuente romper silenciosamente el formato A porque el match se solapa o el grupo captura la estructura equivocada. Sin un fixture explícito de A, la regresión no se detecta hasta producción.
|
|
234
|
+
|
|
235
|
+
**Regla operativa**:
|
|
236
|
+
|
|
237
|
+
1. Crear `tests/fixtures/[dominio]/[nombre]-[formato].ext` para CADA formato conocido **antes** de tocar el parser.
|
|
238
|
+
2. Escribir tests de conteo/identidad para AMBOS formatos (ej: "produce exactamente 13 IDs canónicos") ANTES del cambio.
|
|
239
|
+
3. Modificar el regex/parser.
|
|
240
|
+
4. Verificar que AMBOS tests siguen verdes. Si uno se rompe, revertir y acotar más el cambio.
|
|
241
|
+
|
|
242
|
+
```python
|
|
243
|
+
# BIEN — fixtures explícitos de cada formato soportado, test antes del fix
|
|
244
|
+
FIXTURE_CANONICO = Path("tests/fixtures/cedulas/cedula-formato-v1.md")
|
|
245
|
+
FIXTURE_REEXTRAIDO = Path("tests/fixtures/cedulas/cedula-formato-v2.md")
|
|
246
|
+
|
|
247
|
+
@pytest.mark.parametrize("fixture", [FIXTURE_CANONICO, FIXTURE_REEXTRAIDO])
|
|
248
|
+
def test_parser_produce_13_ids_canonicos(fixture):
|
|
249
|
+
texto = fixture.read_text(encoding="utf-8")
|
|
250
|
+
ids = [h.get("id") for h in extraer(texto)]
|
|
251
|
+
assert len(ids) == 13
|
|
252
|
+
assert "X.X.x" not in ids # no debe caer al fallback legacy
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
**Beneficio medible**: tener fixtures explícitos de cada formato soportado permite aplicar un fix en un modo secundario sin romper el modo primario. Caso real: al extender un parser de markdown para un segundo convertidor con artefactos distintos, 31 tests nuevos pasaron con 0 regresión en 24 tests previos.
|
|
256
|
+
|
|
257
|
+
**Relacionado**: patrón "characterization test" de Michael Feathers — capturar el comportamiento actual como fixture byte-exact antes de refactorizar.
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
## Tests de idempotencia requieren 2 ejecuciones + diff del estado
|
|
264
|
+
|
|
265
|
+
### Regla
|
|
266
|
+
|
|
267
|
+
Para cualquier pipeline **resumable**, **reentrante** o **idempotente por diseño**
|
|
268
|
+
(walkers que marcan estado en cada paso, workers que dedupean por clave, jobs que
|
|
269
|
+
continúan donde se interrumpieron), el test unitario que pasa con 1 ejecución es
|
|
270
|
+
insuficiente. Se necesitan **2 ejecuciones consecutivas del mismo input** y un
|
|
271
|
+
assert sobre el **diff del estado**.
|
|
272
|
+
|
|
273
|
+
### Por qué
|
|
274
|
+
|
|
275
|
+
El bug más frecuente en pipelines resumables es que el dedupe solo considera
|
|
276
|
+
estado terminal (`estado == "ok"`), ignorando estados intermedios (`descubierto`,
|
|
277
|
+
`en_proceso`). En la segunda corrida, los ítems en estado intermedio se duplican
|
|
278
|
+
aunque el walker "sabe" que ya los vio. Este bug **nunca aparece** en tests
|
|
279
|
+
unitarios que solo verifican una corrida — se necesita la corrida N+1 para
|
|
280
|
+
observar la duplicación.
|
|
281
|
+
|
|
282
|
+
### Patrón canónico
|
|
283
|
+
|
|
284
|
+
```python
|
|
285
|
+
def test_walker_resumable_no_duplica_en_corridas_sucesivas(tmp_path):
|
|
286
|
+
# Arrange — dataset con 100 archivos
|
|
287
|
+
fuente = crear_fuente_con_100_archivos(tmp_path)
|
|
288
|
+
manifest = tmp_path / "manifest.jsonl"
|
|
289
|
+
|
|
290
|
+
# Act corrida 1
|
|
291
|
+
walker = Walker(fuente=fuente, manifest=manifest)
|
|
292
|
+
walker.ejecutar()
|
|
293
|
+
manifest_despues_1 = manifest.read_text().splitlines()
|
|
294
|
+
|
|
295
|
+
# Act corrida 2 (re-ejecución completa, sin reset)
|
|
296
|
+
walker2 = Walker(fuente=fuente, manifest=manifest)
|
|
297
|
+
walker2.ejecutar()
|
|
298
|
+
manifest_despues_2 = manifest.read_text().splitlines()
|
|
299
|
+
|
|
300
|
+
# Assert 1: la segunda corrida NO agrega entradas duplicadas
|
|
301
|
+
diff = len(manifest_despues_2) - len(manifest_despues_1)
|
|
302
|
+
assert diff == 0, (
|
|
303
|
+
f"Corrida 2 agregó {diff} entradas. El dedupe está ignorando "
|
|
304
|
+
f"algún estado intermedio. Manifest antes={len(manifest_despues_1)}, "
|
|
305
|
+
f"después={len(manifest_despues_2)}."
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
# Assert 2: todas las entradas son únicas por su clave de dedupe (SHA)
|
|
309
|
+
shas = [json.loads(l)["sha256"] for l in manifest_despues_2]
|
|
310
|
+
assert len(shas) == len(set(shas)), "Hay SHAs duplicados en manifest"
|
|
311
|
+
|
|
312
|
+
# Assert 3: el count final coincide con el dataset fuente
|
|
313
|
+
assert len(manifest_despues_2) == 100
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### Reglas
|
|
317
|
+
|
|
318
|
+
- **Dos corridas exactas** — mismo input, mismo código, diferente momento. No resetear estado entre corridas; eso simula el caso "pipeline interrumpido y retomado".
|
|
319
|
+
- **Assert sobre el DIFF**, no solo sobre el estado final. Un test que solo valida `len == 100` pasa aunque internamente haya 100 buenos + 50 duplicados si el dedupe corre al final.
|
|
320
|
+
- **Interrumpir una corrida a mitad** como variante avanzada: matar el proceso en un estado intermedio (`descubierto`, `en_proceso`) y verificar que la corrida 2 continúa sin duplicar ni perder items.
|
|
321
|
+
- **Dedupear por clave de contenido** (SHA256) no por clave secundaria (nombre, path, id secuencial) — ver también `patrones-python` "Caché por SHA256 en filesystem para idempotencia de pipelines costosos".
|
|
322
|
+
- **NO confiar en tests con mock del storage**: los bugs de idempotencia se manifiestan solo con I/O real al filesystem o BD. Usar `tmp_path` o base de datos in-memory, pero nunca mock del walker mismo.
|
|
323
|
+
|
|
324
|
+
### Anti-patrón
|
|
325
|
+
|
|
326
|
+
```python
|
|
327
|
+
# MAL — una sola corrida; el bug de dedupe parcial pasa desapercibido
|
|
328
|
+
def test_walker_procesa_100_archivos(tmp_path):
|
|
329
|
+
walker = Walker(fuente=crear_100_archivos(tmp_path))
|
|
330
|
+
walker.ejecutar()
|
|
331
|
+
assert len(walker.manifest) == 100 # pasa aunque dedupe solo cubra estado "ok"
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### Aplicabilidad
|
|
335
|
+
|
|
336
|
+
- Walkers de filesystem que marcan progreso en un manifest
|
|
337
|
+
- Workers con dead-letter queue que reintentan mensajes fallidos
|
|
338
|
+
- ETL con checkpoints parciales
|
|
339
|
+
- Migradores de datos con strategy `upsert` o `insert or ignore`
|
|
340
|
+
- Cualquier job que tolere interrupción y reanudación
|