@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,407 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: manejo-errores
|
|
3
|
+
description: >
|
|
4
|
+
Patrones de manejo de errores en Python y TypeScript. Jerarquía de excepciones,
|
|
5
|
+
excepciones personalizadas, códigos de error, logging estructurado, error boundaries,
|
|
6
|
+
degradación elegante, patrones de reintento y circuit breakers.
|
|
7
|
+
version: "1.1.0"
|
|
8
|
+
evolved: true
|
|
9
|
+
evolved-from: "1.0.2"
|
|
10
|
+
evolved-at: "2026-04-24"
|
|
11
|
+
evolved-by: "aprender"
|
|
12
|
+
evolved-note: "Sección nueva: except Exception: pass en cleanup es válido, pero con logger.debug para trazabilidad"
|
|
13
|
+
herramientasPermitidas: [Read]
|
|
14
|
+
exclusiones:
|
|
15
|
+
- "No cargar para monitoreo y alertas en producción (Prometheus, Grafana, PagerDuty) — para observabilidad cargar `monitoring-alertas` o `sre-patrones`."
|
|
16
|
+
- "No cargar para manejo de errores específico de frameworks (FastAPI exception handlers, Angular Error Interceptor) — los ejemplos aquí son patrones base; para frameworks específicos cargar el skill correspondiente."
|
|
17
|
+
- "No cargar para errores de compilación o build (TypeScript, Rust, Java) — para diagnóstico de errores de compilación cargar `typescript-diagnosticos` o el skill de build errors correspondiente."
|
|
18
|
+
- "No cargar para logging de auditoría o compliance (GDPR, SOC2 log requirements) — este skill cubre logging operacional de errores, no logging de compliance."
|
|
19
|
+
evolvable: true # default para skill estandar
|
|
20
|
+
---
|
|
21
|
+
# Manejo de Errores — Patrones de Producción
|
|
22
|
+
|
|
23
|
+
## Cuándo NO cargar
|
|
24
|
+
|
|
25
|
+
- La tarea es monitoreo y alertas en producción: Prometheus, Grafana, SLOs, PagerDuty — cargar `monitoring-alertas` o `sre-patrones`.
|
|
26
|
+
- El manejo de errores es específico de un framework: FastAPI exception handlers, Angular HTTP Interceptor — cargar el skill del framework; los patrones aquí son base.
|
|
27
|
+
- Los errores son de compilación o build: TypeScript errors, Rust borrow checker, Java compile errors — cargar `typescript-diagnosticos` o el skill de build errors del lenguaje.
|
|
28
|
+
- El logging es de auditoría o compliance (GDPR, SOC2) — este skill cubre logging operacional de errores en runtime, no logging de compliance.
|
|
29
|
+
|
|
30
|
+
## Principios Fundamentales
|
|
31
|
+
|
|
32
|
+
El manejo de errores no es una ocurrencia tardía: es parte del diseño de la interfaz.
|
|
33
|
+
Un módulo que no especifica su contrato de errores es un módulo incompleto.
|
|
34
|
+
|
|
35
|
+
**Reglas de oro:**
|
|
36
|
+
1. Nunca silencies una excepción sin loguear o re-lanzar.
|
|
37
|
+
2. Transforma excepciones de bajo nivel en errores de dominio en la frontera de tu módulo.
|
|
38
|
+
3. El mensaje de error debe responder: ¿qué pasó?, ¿dónde?, ¿qué hace el sistema ahora?
|
|
39
|
+
4. Los errores recuperables se manejan; los no recuperables se propagan.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Python — Jerarquía de Excepciones
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
# BIEN - jerarquía clara con códigos de error
|
|
47
|
+
class ErrorAplicacion(Exception):
|
|
48
|
+
"""Base de todos los errores de la aplicación."""
|
|
49
|
+
codigo: str = "APP_ERROR"
|
|
50
|
+
|
|
51
|
+
def __init__(self, mensaje: str, detalle: dict | None = None) -> None:
|
|
52
|
+
super().__init__(mensaje)
|
|
53
|
+
self.mensaje = mensaje
|
|
54
|
+
self.detalle = detalle or {}
|
|
55
|
+
|
|
56
|
+
def to_dict(self) -> dict:
|
|
57
|
+
return {
|
|
58
|
+
"codigo": self.codigo,
|
|
59
|
+
"mensaje": self.mensaje,
|
|
60
|
+
"detalle": self.detalle,
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
class ErrorValidacion(ErrorAplicacion):
|
|
64
|
+
codigo = "VALIDATION_ERROR"
|
|
65
|
+
|
|
66
|
+
class ErrorNoEncontrado(ErrorAplicacion):
|
|
67
|
+
codigo = "NOT_FOUND"
|
|
68
|
+
|
|
69
|
+
class ErrorConflicto(ErrorAplicacion):
|
|
70
|
+
codigo = "CONFLICT"
|
|
71
|
+
|
|
72
|
+
class ErrorExterno(ErrorAplicacion):
|
|
73
|
+
"""Para fallos en servicios de terceros."""
|
|
74
|
+
codigo = "EXTERNAL_SERVICE_ERROR"
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Python — Encadenamiento de Excepciones
|
|
80
|
+
|
|
81
|
+
SIEMPRE usar `raise ... from exc` para preservar la cadena de errores:
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
def cargar_config(ruta: str) -> Config:
|
|
85
|
+
try:
|
|
86
|
+
with open(ruta) as f:
|
|
87
|
+
datos = json.load(f)
|
|
88
|
+
except FileNotFoundError as exc:
|
|
89
|
+
raise ErrorNoEncontrado(
|
|
90
|
+
f"Archivo de configuración no encontrado: {ruta}",
|
|
91
|
+
) from exc
|
|
92
|
+
except json.JSONDecodeError as exc:
|
|
93
|
+
raise ErrorValidacion(
|
|
94
|
+
f"JSON malformado en {ruta}: línea {exc.lineno}",
|
|
95
|
+
) from exc
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Python — Logging Estructurado
|
|
101
|
+
|
|
102
|
+
NUNCA usar `print()` o `logging.error("Algo salió mal")` sin contexto.
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
import structlog
|
|
106
|
+
|
|
107
|
+
logger = structlog.get_logger(__name__)
|
|
108
|
+
|
|
109
|
+
def procesar_pedido(pedido_id: str, usuario_id: str) -> Resultado:
|
|
110
|
+
log = logger.bind(pedido_id=pedido_id, usuario_id=usuario_id)
|
|
111
|
+
log.info("iniciando_procesamiento_pedido")
|
|
112
|
+
|
|
113
|
+
try:
|
|
114
|
+
pedido = repositorio.obtener(pedido_id)
|
|
115
|
+
resultado = servicio_pago.cobrar(pedido)
|
|
116
|
+
log.info("pedido_procesado_exitosamente", monto=pedido.total)
|
|
117
|
+
return resultado
|
|
118
|
+
except ErrorExterno as exc:
|
|
119
|
+
log.error(
|
|
120
|
+
"fallo_servicio_externo",
|
|
121
|
+
servicio=exc.detalle.get("servicio"),
|
|
122
|
+
exc_info=True,
|
|
123
|
+
)
|
|
124
|
+
raise
|
|
125
|
+
except Exception as exc:
|
|
126
|
+
log.critical(
|
|
127
|
+
"error_inesperado_procesando_pedido",
|
|
128
|
+
tipo_excepcion=type(exc).__name__,
|
|
129
|
+
exc_info=True,
|
|
130
|
+
)
|
|
131
|
+
raise ErrorAplicacion("Error interno al procesar el pedido") from exc
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Python — Patrón Retry con Backoff Exponencial
|
|
137
|
+
|
|
138
|
+
NUNCA hacer retry infinito sin backoff ni logging.
|
|
139
|
+
|
|
140
|
+
```python
|
|
141
|
+
from tenacity import (
|
|
142
|
+
retry, stop_after_attempt, wait_exponential,
|
|
143
|
+
retry_if_exception_type, before_sleep_log,
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
@retry(
|
|
147
|
+
stop=stop_after_attempt(3),
|
|
148
|
+
wait=wait_exponential(multiplier=1, min=1, max=10),
|
|
149
|
+
retry=retry_if_exception_type(ErrorExterno),
|
|
150
|
+
before_sleep=before_sleep_log(logger_tenacity, logging.WARNING),
|
|
151
|
+
)
|
|
152
|
+
def llamar_api_con_retry(endpoint: str, payload: dict) -> dict:
|
|
153
|
+
respuesta = httpx.post(endpoint, json=payload, timeout=10.0)
|
|
154
|
+
if respuesta.status_code == 429:
|
|
155
|
+
raise ErrorExterno("Rate limit alcanzado", {"servicio": endpoint, "codigo_http": 429})
|
|
156
|
+
if respuesta.status_code >= 500:
|
|
157
|
+
raise ErrorExterno("Error del servidor externo", {"servicio": endpoint})
|
|
158
|
+
respuesta.raise_for_status()
|
|
159
|
+
return respuesta.json()
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Gotcha: Backoff en Workers y Reconexión de Servicios
|
|
165
|
+
|
|
166
|
+
En workers, colas de mensajes y conexiones a bases de datos, el retry debe usar
|
|
167
|
+
backoff exponencial con techo (cap). Sin techo, los reintentos se espacian tanto
|
|
168
|
+
que el worker parece muerto. Sin backoff, los reintentos saturan el servicio caído.
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
# Patrón de reconexión con backoff exponencial y cap
|
|
172
|
+
import time
|
|
173
|
+
import random
|
|
174
|
+
|
|
175
|
+
def conectar_con_backoff(crear_conexion, max_intentos=10, base=1.0, cap=30.0):
|
|
176
|
+
"""Reconecta con backoff exponencial: 1s → 2s → 4s → 8s → ... → 30s max."""
|
|
177
|
+
for intento in range(max_intentos):
|
|
178
|
+
try:
|
|
179
|
+
return crear_conexion()
|
|
180
|
+
except Exception as exc:
|
|
181
|
+
if intento == max_intentos - 1:
|
|
182
|
+
raise
|
|
183
|
+
delay = min(base * (2 ** intento), cap)
|
|
184
|
+
# Jitter para evitar thundering herd
|
|
185
|
+
delay *= 0.5 + random.random() * 0.5
|
|
186
|
+
logger.warning(
|
|
187
|
+
"reconexion_fallida",
|
|
188
|
+
intento=intento + 1,
|
|
189
|
+
proximo_reintento_seg=round(delay, 1),
|
|
190
|
+
error=str(exc),
|
|
191
|
+
)
|
|
192
|
+
time.sleep(delay)
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Reglas de backoff para workers
|
|
196
|
+
|
|
197
|
+
- **Base**: 1 segundo. Nunca 0 — un retry inmediato multiplica la carga.
|
|
198
|
+
- **Cap**: 30 segundos para servicios internos, 60 segundos para APIs externas.
|
|
199
|
+
- **Jitter obligatorio**: sin jitter, N workers reintentan al mismo instante (thundering herd).
|
|
200
|
+
- **Max intentos finito**: siempre. Un worker que reintenta infinitamente consume
|
|
201
|
+
recursos sin producir valor. Tras agotar intentos: enviar a dead letter queue.
|
|
202
|
+
- **Loguear cada reintento**: intento N de M, próximo reintento en X segundos.
|
|
203
|
+
Sin logs, un worker silenciosamente atascado es invisible.
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## Patrones Avanzados
|
|
208
|
+
|
|
209
|
+
Para implementaciones completas de Circuit Breaker, TypeScript Result types,
|
|
210
|
+
Angular Error Boundaries, Angular HTTP Interceptor y Graceful Degradation,
|
|
211
|
+
ver [recursos/implementaciones-completas.md](recursos/implementaciones-completas.md).
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## Gotchas / Errores comunes no obvios
|
|
216
|
+
|
|
217
|
+
**`raise ErrorDominio() from exc` con `exc` siendo `None` (Python) causa `TypeError` en el traceback**: si se hace `raise MiError("msg") from None` es válido (suprime el contexto), pero `raise MiError("msg") from variable_que_puede_ser_none` lanza `TypeError: exception causes must derive from BaseException` cuando la variable es `None`. Causa: Python valida que el cause sea una excepción o `None` literal, no una variable que puede ser `None`. Fix: usar `raise MiError("msg") from exc` solo cuando `exc` se capturó en un `except` — fuera de un bloque except usar simplemente `raise MiError("msg")`.
|
|
218
|
+
|
|
219
|
+
**`structlog` configurado con `JSONRenderer` en producción pero `ConsoleRenderer` en tests puede causar que los tests pasen con logs que explotan en producción**: la configuración de structlog se setea globalmente con `structlog.configure()` — si los tests usan una configuración diferente al código de producción, los procesadores que funcionan en tests pueden fallar en prod. Causa: structlog es un singleton de configuración global. Fix: no mezclar configuraciones — usar un mismo renderer en tests y en producción, o verificar que los procesadores configurados son compatibles con ambos entornos.
|
|
220
|
+
|
|
221
|
+
**`tenacity` con `retry_if_exception_type(ErrorExterno)` no reintenta si la excepción está envuelta en otra**: si el código lanza `ErrorServicio("msg") from exc` donde `ErrorServicio` hereda de `ErrorAplicacion` (no de `ErrorExterno`), el retry no se activa aunque el error subyacente sea externo. Causa: `retry_if_exception_type` verifica el tipo exacto de la excepción lanzada, no la causa. Fix: asegurarse de lanzar exactamente la excepción que coincide con `retry_if_exception_type`, o usar `retry_if_exception(lambda e: isinstance(e, (ErrorExterno, httpx.HTTPError)))` con una función predicado más amplia.
|
|
222
|
+
|
|
223
|
+
**El patrón de backoff con `random.random()` no es criptográficamente seguro pero eso no importa — lo que importa es usar `random.seed()` en tests**: si los tests verifican el comportamiento del backoff con un mock de `time.sleep`, los tests pueden ser no-deterministas si `random.random()` devuelve valores diferentes en cada ejecución. Causa: jitter aleatorio hace los tests flakys. Fix: en tests de backoff, mockear `random.random` para que retorne un valor fijo (`mocker.patch('random.random', return_value=0.5)`), o usar `secrets.randbelow` solo en contextos donde el timing de seguridad importa.
|
|
224
|
+
|
|
225
|
+
**Cachear "resultado nulo por fallo transitorio" con el mismo TTL que un resultado exitoso silencia alertas legítimas**: un hook/script verifica algo cada 24h (ej: consultar npm por una nueva versión); si la consulta falla (timeout, red caída, dependencia fuera de línea), cachear el fallo con TTL completo de 24h hace que el siguiente check no reintente hasta el día siguiente — el usuario queda sin aviso durante una ventana completa pese a que la infraestructura ya se recuperó minutos después. Causa: usar un solo TTL nominal sin distinguir éxito de fallo transitorio; el estado "no pude verificar" se trata como "verifiqué y todo OK". Fix: implementar **throttle adaptativo** con dos constantes distintas — `SUCCESS_TTL` largo (24h) y `FAILURE_TTL` corto (1h, 5min según el costo del check); al leer el cache, elegir el intervalo según si el resultado anterior fue exitoso o nulo. Patrón:
|
|
226
|
+
|
|
227
|
+
```js
|
|
228
|
+
function debeVerificar() {
|
|
229
|
+
try {
|
|
230
|
+
const data = JSON.parse(fs.readFileSync(flagPath(), 'utf8'));
|
|
231
|
+
const intervalo = data.resultado ? SUCCESS_TTL : FAILURE_TTL;
|
|
232
|
+
return (Date.now() - data.timestamp) > intervalo;
|
|
233
|
+
} catch {
|
|
234
|
+
return true;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Aplica a: health checks periódicos, verificación de nuevas versiones, consultas de cuota/rate-limit de APIs externas, chequeos de certificados, polling de colas. La línea divisoria es "¿el resultado anterior fue información real o fue ausencia de información?".
|
|
240
|
+
|
|
241
|
+
## Gotcha — Retry con cliente HTTP interno: propagar el timeout
|
|
242
|
+
|
|
243
|
+
### NUNCA: wrapper de retry que promete N segundos sobre cliente HTTP con timeout M << N
|
|
244
|
+
|
|
245
|
+
**Problema**: abstracciones de retry (cadenas de fallback, reintentos con backoff) suelen envolver el intento con `asyncio.wait_for(timeout=N)` pero llaman a un cliente HTTP que tiene su propio `read_timeout = M` fijo y más corto. Si el servidor tarda entre M y N segundos, el cliente HTTP aborta con `ReadTimeout` y el wrapper interpreta "fallo" → degrada a otro modelo o vía, aunque el wrapper prometía esperar N segundos.
|
|
246
|
+
|
|
247
|
+
Caso real observado: wrapper `llm_fallback` prometía 300s al primer modelo, pero `OllamaClient` tenía `httpx.Timeout(read=30.0)` fijo. Ollama necesita 60-120s para cargar un modelo grande en VRAM → aborta a los 30s, 3 reintentos fallan, wrapper degrada al siguiente modelo. Síntoma: log con mensajes de error vacíos (`fallo (intento 1/3): .`), porque `ReadTimeout` sin mensaje se convierte en `"."` vía `str(exc)`.
|
|
248
|
+
|
|
249
|
+
```python
|
|
250
|
+
# MAL — timeouts desalineados: el wrapper promete 300s, el cliente corta a 30s
|
|
251
|
+
class Client:
|
|
252
|
+
timeout = 30.0
|
|
253
|
+
def _crear(self): return httpx.AsyncClient(timeout=httpx.Timeout(read=self.timeout))
|
|
254
|
+
async def generate(self, model, prompt):
|
|
255
|
+
async with self._crear() as c: return await c.post("/generate", json=...)
|
|
256
|
+
|
|
257
|
+
async def fallback_chain(client, cadena):
|
|
258
|
+
for modelo, timeout_s in cadena:
|
|
259
|
+
try:
|
|
260
|
+
# timeout_s=300, pero client.generate aborta internamente a los 30s
|
|
261
|
+
return await asyncio.wait_for(
|
|
262
|
+
client.generate(modelo, prompt), timeout=timeout_s,
|
|
263
|
+
)
|
|
264
|
+
except (asyncio.TimeoutError, ReadTimeout):
|
|
265
|
+
continue # degrada al siguiente modelo innecesariamente
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
```python
|
|
269
|
+
# BIEN — cliente acepta override de timeout; wrapper lo propaga
|
|
270
|
+
class Client:
|
|
271
|
+
timeout = 30.0 # default para llamadas cortas
|
|
272
|
+
def _crear(self, read_timeout_override=None):
|
|
273
|
+
t = read_timeout_override if read_timeout_override is not None else self.timeout
|
|
274
|
+
return httpx.AsyncClient(timeout=httpx.Timeout(read=t))
|
|
275
|
+
async def generate(self, model, prompt, timeout=None):
|
|
276
|
+
async with self._crear(read_timeout_override=timeout) as c:
|
|
277
|
+
return await c.post("/generate", json=...)
|
|
278
|
+
|
|
279
|
+
async def fallback_chain(client, cadena):
|
|
280
|
+
for modelo, timeout_s in cadena:
|
|
281
|
+
try:
|
|
282
|
+
# Ahora el cliente tiene el mismo timeout que el wrapper
|
|
283
|
+
try:
|
|
284
|
+
call = client.generate(modelo, prompt, timeout=timeout_s)
|
|
285
|
+
except TypeError:
|
|
286
|
+
# Cliente hermano sin soporte de `timeout` kwarg
|
|
287
|
+
call = client.generate(modelo, prompt)
|
|
288
|
+
return await asyncio.wait_for(call, timeout=timeout_s)
|
|
289
|
+
except (asyncio.TimeoutError, ConnectionError):
|
|
290
|
+
continue
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
**Regla general**: en cualquier pila `wrapper_con_retry → cliente_HTTP`, el timeout del cliente HTTP debe ser ≥ al timeout que el wrapper comunica al usuario. Si el cliente tiene default corto (típico: 30s), aceptar un kwarg `timeout` y que el wrapper lo propague en cada intento.
|
|
294
|
+
|
|
295
|
+
**Aplicabilidad**: Celery + requests, nginx + backend, httpx + retry libraries, cualquier abstracción que prometa tiempos más largos que el default del transporte subyacente. Añadir verificación en code review cada vez que se implemente una capa de retry sobre HTTP.
|
|
296
|
+
|
|
297
|
+
## Checklist de Revisión — Manejo de Errores
|
|
298
|
+
|
|
299
|
+
- [ ] Toda excepción capturada tiene log o re-lanzamiento con contexto.
|
|
300
|
+
- [ ] Las excepciones de infraestructura se transforman en errores de dominio en la frontera del módulo.
|
|
301
|
+
- [ ] Los mensajes de error son accionables para quien los recibe.
|
|
302
|
+
- [ ] Los servicios externos tienen timeout + retry + circuit breaker.
|
|
303
|
+
- [ ] El logging incluye contexto suficiente para reproducir el error.
|
|
304
|
+
- [ ] Los errores HTTP tienen código de estado correcto (4xx vs 5xx).
|
|
305
|
+
- [ ] El tipo de error en TypeScript es explícito, no `any` ni `unknown` sin narrowing.
|
|
306
|
+
- [ ] Los errores de validación devuelven todos los campos inválidos de una vez (no uno por uno).
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
## `except Exception: pass` en cleanup es válido PERO con `logger.debug`
|
|
311
|
+
|
|
312
|
+
### Regla
|
|
313
|
+
|
|
314
|
+
El patrón `try: ... except Exception: pass` en código de **cleanup** (liberación
|
|
315
|
+
de recursos, eliminación de archivos temporales, cierre de conexiones al salir)
|
|
316
|
+
suele marcarse como anti-patrón automáticamente por linters y revisores. En
|
|
317
|
+
ese contexto específico es **válido**, pero debe acompañarse de `logger.debug`
|
|
318
|
+
para preservar trazabilidad.
|
|
319
|
+
|
|
320
|
+
### Por qué
|
|
321
|
+
|
|
322
|
+
El cleanup no debe bloquear el flujo principal ni escalar errores secundarios
|
|
323
|
+
sobre el error principal. Ejemplos:
|
|
324
|
+
|
|
325
|
+
- Eliminar un archivo `.tmp` cuya ruta ya no existe porque otro proceso lo borró.
|
|
326
|
+
- Cerrar un socket que el otro extremo cerró antes.
|
|
327
|
+
- Desregistrar un callback cuyo registro nunca se completó por error temprano.
|
|
328
|
+
|
|
329
|
+
En estos casos el error del cleanup es **irrelevante** para la lógica del
|
|
330
|
+
usuario — reintento no ayuda, propagación oculta el error real. Pero "no
|
|
331
|
+
hacer nada" a secas deja al operador ciego cuando un patrón de cleanup
|
|
332
|
+
sistemáticamente falla por bug real (ej: ruta mal construida, permisos).
|
|
333
|
+
|
|
334
|
+
### Patrón canónico
|
|
335
|
+
|
|
336
|
+
```python
|
|
337
|
+
import logging
|
|
338
|
+
logger = logging.getLogger(__name__)
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
def cerrar_recursos(recurso) -> None:
|
|
342
|
+
"""Cleanup: no retry, no raise, pero sí debug log."""
|
|
343
|
+
try:
|
|
344
|
+
recurso.close()
|
|
345
|
+
except Exception as exc:
|
|
346
|
+
# Nivel DEBUG: visible solo al investigar, silencioso en prod
|
|
347
|
+
logger.debug("fallo_silenciado_en_cleanup", extra={
|
|
348
|
+
"recurso": repr(recurso),
|
|
349
|
+
"tipo_excepcion": type(exc).__name__,
|
|
350
|
+
"mensaje": str(exc),
|
|
351
|
+
})
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
def limpiar_temporal(ruta: Path) -> None:
|
|
355
|
+
try:
|
|
356
|
+
ruta.unlink(missing_ok=True)
|
|
357
|
+
except Exception as exc:
|
|
358
|
+
logger.debug(f"no se pudo eliminar {ruta}: {exc}")
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
### Reglas
|
|
362
|
+
|
|
363
|
+
- **Solo en cleanup / teardown / finally**: no en flujo principal, no en
|
|
364
|
+
manejo de input de usuario, no en llamadas a APIs externas. Para esos
|
|
365
|
+
casos el manejo es específico de la excepción esperada.
|
|
366
|
+
- **Nivel `debug`, NO `warning`**: los logs de warning de cleanup saturan
|
|
367
|
+
la observabilidad y pierden señal. Debug queda invisible en producción
|
|
368
|
+
normal pero accesible cuando se investiga.
|
|
369
|
+
- **Incluir contexto mínimo**: tipo de excepción y mensaje, más el recurso
|
|
370
|
+
que se intentaba liberar. Sin eso el debug es tan inútil como el `pass`.
|
|
371
|
+
- **`except Exception`, no `except:` desnudo**: `except:` captura también
|
|
372
|
+
`KeyboardInterrupt` y `SystemExit`, convirtiendo Ctrl-C en un "cleanup
|
|
373
|
+
silencioso" que confunde al operador.
|
|
374
|
+
|
|
375
|
+
### Anti-patrones relacionados
|
|
376
|
+
|
|
377
|
+
```python
|
|
378
|
+
# MAL — pass desnudo sin trazabilidad
|
|
379
|
+
try:
|
|
380
|
+
socket.close()
|
|
381
|
+
except:
|
|
382
|
+
pass
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
# MAL — warning satura logs cuando el cleanup falla legítimamente
|
|
386
|
+
try:
|
|
387
|
+
archivo_tmp.unlink()
|
|
388
|
+
except OSError as exc:
|
|
389
|
+
logger.warning(f"fallo cleanup: {exc}") # satura observabilidad
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
# MAL — cleanup que lanza oculta el error principal
|
|
393
|
+
try:
|
|
394
|
+
operacion_principal()
|
|
395
|
+
finally:
|
|
396
|
+
recurso.close() # si esto lanza, el error original se pierde
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
### Aplicabilidad
|
|
400
|
+
|
|
401
|
+
Típico en:
|
|
402
|
+
|
|
403
|
+
- Métodos `__exit__` de context managers custom
|
|
404
|
+
- Bloques `finally` tras operaciones de red
|
|
405
|
+
- Lifecycle hooks de shutdown (atexit, signal handlers)
|
|
406
|
+
- Cleanup de recursos heredados (archivos, file handles, DB cursors, sockets)
|
|
407
|
+
- Garbage collection de objetos con `__del__`
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
# Manejo de Errores — Implementaciones Completas
|
|
2
|
+
|
|
3
|
+
## Python — Circuit Breaker
|
|
4
|
+
|
|
5
|
+
```python
|
|
6
|
+
# Circuit breaker manual para servicios externos críticos
|
|
7
|
+
import time
|
|
8
|
+
from enum import Enum
|
|
9
|
+
from threading import Lock
|
|
10
|
+
from dataclasses import dataclass, field
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class EstadoCircuito(Enum):
|
|
14
|
+
CERRADO = "CERRADO" # Normal: deja pasar llamadas
|
|
15
|
+
ABIERTO = "ABIERTO" # Fallo: bloquea llamadas
|
|
16
|
+
SEMI_ABIERTO = "SEMI_ABIERTO" # Prueba: permite una llamada
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class CircuitBreaker:
|
|
21
|
+
nombre: str
|
|
22
|
+
umbral_fallos: int = 5
|
|
23
|
+
ventana_segundos: float = 60.0
|
|
24
|
+
tiempo_recuperacion: float = 30.0
|
|
25
|
+
|
|
26
|
+
_estado: EstadoCircuito = field(default=EstadoCircuito.CERRADO, init=False)
|
|
27
|
+
_conteo_fallos: int = field(default=0, init=False)
|
|
28
|
+
_ultimo_fallo: float = field(default=0.0, init=False)
|
|
29
|
+
_lock: Lock = field(default_factory=Lock, init=False)
|
|
30
|
+
|
|
31
|
+
def __call__(self, func):
|
|
32
|
+
def wrapper(*args, **kwargs):
|
|
33
|
+
with self._lock:
|
|
34
|
+
if self._estado == EstadoCircuito.ABIERTO:
|
|
35
|
+
if time.time() - self._ultimo_fallo > self.tiempo_recuperacion:
|
|
36
|
+
self._estado = EstadoCircuito.SEMI_ABIERTO
|
|
37
|
+
else:
|
|
38
|
+
raise ErrorExterno(
|
|
39
|
+
f"Circuito {self.nombre!r} abierto — servicio no disponible",
|
|
40
|
+
{"circuito": self.nombre, "estado": self._estado.value},
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
try:
|
|
44
|
+
resultado = func(*args, **kwargs)
|
|
45
|
+
with self._lock:
|
|
46
|
+
self._conteo_fallos = 0
|
|
47
|
+
self._estado = EstadoCircuito.CERRADO
|
|
48
|
+
return resultado
|
|
49
|
+
|
|
50
|
+
except ErrorExterno:
|
|
51
|
+
with self._lock:
|
|
52
|
+
self._conteo_fallos += 1
|
|
53
|
+
self._ultimo_fallo = time.time()
|
|
54
|
+
if self._conteo_fallos >= self.umbral_fallos:
|
|
55
|
+
self._estado = EstadoCircuito.ABIERTO
|
|
56
|
+
logger.warning(
|
|
57
|
+
"circuito_abierto",
|
|
58
|
+
nombre=self.nombre,
|
|
59
|
+
fallos=self._conteo_fallos,
|
|
60
|
+
)
|
|
61
|
+
raise
|
|
62
|
+
|
|
63
|
+
return wrapper
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
circuito_pago = CircuitBreaker(nombre="servicio-pago", umbral_fallos=3)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@circuito_pago
|
|
70
|
+
def cobrar_tarjeta(monto: float, token: str) -> dict:
|
|
71
|
+
return cliente_pago.post("/cobrar", json={"monto": monto, "token": token})
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## TypeScript — Discriminated Unions para Resultados
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
// Result type explícito — errores tipados, sin try/catch en el caller
|
|
80
|
+
type Resultado<T, E = ErrorAplicacion> =
|
|
81
|
+
| { ok: true; valor: T }
|
|
82
|
+
| { ok: false; error: E };
|
|
83
|
+
|
|
84
|
+
function exito<T>(valor: T): Resultado<T> {
|
|
85
|
+
return { ok: true, valor };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function fallo<E extends ErrorAplicacion>(error: E): Resultado<never, E> {
|
|
89
|
+
return { ok: false, error };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
interface ErrorAplicacion {
|
|
93
|
+
codigo: string;
|
|
94
|
+
mensaje: string;
|
|
95
|
+
detalle?: Record<string, unknown>;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async function obtenerUsuario(
|
|
99
|
+
id: string
|
|
100
|
+
): Promise<Resultado<Usuario, ErrorAplicacion>> {
|
|
101
|
+
const usuario = await db.findById(id);
|
|
102
|
+
if (!usuario) {
|
|
103
|
+
return fallo({ codigo: "NOT_FOUND", mensaje: `Usuario ${id} no encontrado` });
|
|
104
|
+
}
|
|
105
|
+
return exito(usuario);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Uso
|
|
109
|
+
const resultado = await obtenerUsuario("abc");
|
|
110
|
+
if (!resultado.ok) {
|
|
111
|
+
// TypeScript sabe que resultado.error es ErrorAplicacion
|
|
112
|
+
console.error(resultado.error.codigo);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
// TypeScript sabe que resultado.valor es Usuario
|
|
116
|
+
console.log(resultado.valor.nombre);
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Angular — Error Boundaries con Componentes de Error
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
// error-boundary.component.ts
|
|
125
|
+
import { Component, ErrorHandler, Injectable, signal } from '@angular/core';
|
|
126
|
+
|
|
127
|
+
@Injectable()
|
|
128
|
+
export class ManejadorErroresGlobal implements ErrorHandler {
|
|
129
|
+
readonly ultimoError = signal<Error | null>(null);
|
|
130
|
+
|
|
131
|
+
handleError(error: Error): void {
|
|
132
|
+
console.error('[ERROR GLOBAL]', error);
|
|
133
|
+
this.ultimoError.set(error);
|
|
134
|
+
// Enviar a Sentry/DataDog aquí
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Uso en componente
|
|
139
|
+
@Component({
|
|
140
|
+
standalone: true,
|
|
141
|
+
template: `
|
|
142
|
+
@if (manejador.ultimoError()) {
|
|
143
|
+
<div class="error-boundary bg-red-50 border border-red-200 rounded p-4">
|
|
144
|
+
<p class="font-semibold text-red-700">Algo salió mal</p>
|
|
145
|
+
<p class="text-red-600 text-sm">{{ manejador.ultimoError()?.message }}</p>
|
|
146
|
+
<button (click)="reintentar()" class="mt-2 px-3 py-1 bg-red-600 text-white rounded text-sm">
|
|
147
|
+
Reintentar
|
|
148
|
+
</button>
|
|
149
|
+
</div>
|
|
150
|
+
} @else {
|
|
151
|
+
<ng-content />
|
|
152
|
+
}
|
|
153
|
+
`,
|
|
154
|
+
})
|
|
155
|
+
export class ErrorBoundaryComponent {
|
|
156
|
+
constructor(protected manejador: ManejadorErroresGlobal) {}
|
|
157
|
+
|
|
158
|
+
reintentar(): void {
|
|
159
|
+
this.manejador.ultimoError.set(null);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Angular — Interceptor HTTP con Manejo Centralizado
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
// error.interceptor.ts
|
|
170
|
+
import { HttpInterceptorFn, HttpErrorResponse } from '@angular/common/http';
|
|
171
|
+
import { inject } from '@angular/core';
|
|
172
|
+
import { catchError, throwError } from 'rxjs';
|
|
173
|
+
import { Router } from '@angular/router';
|
|
174
|
+
import { NotificacionService } from './notificacion.service';
|
|
175
|
+
|
|
176
|
+
export const errorInterceptor: HttpInterceptorFn = (req, next) => {
|
|
177
|
+
const router = inject(Router);
|
|
178
|
+
const notificaciones = inject(NotificacionService);
|
|
179
|
+
|
|
180
|
+
return next(req).pipe(
|
|
181
|
+
catchError((error: HttpErrorResponse) => {
|
|
182
|
+
switch (error.status) {
|
|
183
|
+
case 401:
|
|
184
|
+
router.navigate(['/login']);
|
|
185
|
+
break;
|
|
186
|
+
case 403:
|
|
187
|
+
notificaciones.error('Sin permisos para realizar esta acción');
|
|
188
|
+
break;
|
|
189
|
+
case 404:
|
|
190
|
+
notificaciones.advertencia('Recurso no encontrado');
|
|
191
|
+
break;
|
|
192
|
+
case 422:
|
|
193
|
+
// Errores de validación — el componente los maneja directamente
|
|
194
|
+
break;
|
|
195
|
+
case 429:
|
|
196
|
+
notificaciones.advertencia('Demasiadas solicitudes. Espere un momento.');
|
|
197
|
+
break;
|
|
198
|
+
case 0:
|
|
199
|
+
notificaciones.error('Sin conexión a la red');
|
|
200
|
+
break;
|
|
201
|
+
default:
|
|
202
|
+
if (error.status >= 500) {
|
|
203
|
+
notificaciones.error('Error interno del servidor. Intente más tarde.');
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return throwError(() => error);
|
|
207
|
+
})
|
|
208
|
+
);
|
|
209
|
+
};
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## Python — Degradación Elegante (Graceful Degradation)
|
|
215
|
+
|
|
216
|
+
```python
|
|
217
|
+
# Función con fallback explícito
|
|
218
|
+
from functools import wraps
|
|
219
|
+
from typing import TypeVar, Callable, Any
|
|
220
|
+
|
|
221
|
+
T = TypeVar("T")
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def con_fallback(valor_default: T, *, loguear: bool = True):
|
|
225
|
+
"""Decorador que devuelve un valor por defecto ante cualquier excepción."""
|
|
226
|
+
def decorador(func: Callable[..., T]) -> Callable[..., T]:
|
|
227
|
+
@wraps(func)
|
|
228
|
+
def wrapper(*args, **kwargs) -> T:
|
|
229
|
+
try:
|
|
230
|
+
return func(*args, **kwargs)
|
|
231
|
+
except Exception as exc:
|
|
232
|
+
if loguear:
|
|
233
|
+
logger.warning(
|
|
234
|
+
"usando_fallback",
|
|
235
|
+
funcion=func.__name__,
|
|
236
|
+
excepcion=type(exc).__name__,
|
|
237
|
+
mensaje=str(exc),
|
|
238
|
+
)
|
|
239
|
+
return valor_default
|
|
240
|
+
return wrapper
|
|
241
|
+
return decorador
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
@con_fallback(valor_default=[], loguear=True)
|
|
245
|
+
def obtener_recomendaciones(usuario_id: str) -> list[Producto]:
|
|
246
|
+
"""Si falla el motor de recomendaciones, devuelve lista vacía."""
|
|
247
|
+
return motor_recomendaciones.calcular(usuario_id)
|
|
248
|
+
```
|