@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,201 @@
|
|
|
1
|
+
# Regla: Patrones de Arquitectura Kotlin
|
|
2
|
+
|
|
3
|
+
Esta regla define los patrones obligatorios para estructurar código Kotlin en Android
|
|
4
|
+
y backend (Ktor/Spring). La arquitectura limpia con coroutines y Flow es el estándar.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Coroutines y Flow para async/reactive
|
|
9
|
+
|
|
10
|
+
- Todo código asíncrono usa `suspend` functions o `Flow`. Sin `AsyncTask`, `Thread` ni callbacks.
|
|
11
|
+
- `Flow` para streams de datos que cambian en el tiempo (lista de items, estados de UI).
|
|
12
|
+
- `StateFlow` para estado que siempre tiene un valor actual (reemplaza `LiveData`).
|
|
13
|
+
- `SharedFlow` para eventos de un solo disparo (navegación, mensajes de error).
|
|
14
|
+
|
|
15
|
+
```kotlin
|
|
16
|
+
// MAL — LiveData expone detalles de Android en el dominio
|
|
17
|
+
class PedidoRepository {
|
|
18
|
+
fun obtenerPedidos(): LiveData<List<Pedido>> = ...
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// BIEN — Flow es independiente del framework
|
|
22
|
+
class PedidoRepository {
|
|
23
|
+
fun obtenerPedidos(): Flow<List<Pedido>> = flow {
|
|
24
|
+
emit(dao.obtenerTodos())
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Repository pattern con interfaces
|
|
32
|
+
|
|
33
|
+
Toda fuente de datos se abstrae detrás de una interfaz.
|
|
34
|
+
El dominio depende de la interfaz, no de la implementación concreta.
|
|
35
|
+
|
|
36
|
+
```kotlin
|
|
37
|
+
// Interfaz en la capa de dominio
|
|
38
|
+
interface PedidoRepository {
|
|
39
|
+
suspend fun obtenerPorId(id: UUID): Pedido?
|
|
40
|
+
fun obtenerTodos(): Flow<List<Pedido>>
|
|
41
|
+
suspend fun guardar(pedido: Pedido)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Implementación en la capa de datos
|
|
45
|
+
class PedidoRepositoryImpl(
|
|
46
|
+
private val dao: PedidoDao,
|
|
47
|
+
private val api: PedidoApi,
|
|
48
|
+
) : PedidoRepository {
|
|
49
|
+
override suspend fun obtenerPorId(id: UUID): Pedido? =
|
|
50
|
+
dao.buscarPorId(id)?.toDomain()
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Use cases / interactors para lógica de negocio
|
|
57
|
+
|
|
58
|
+
Cada acción de negocio tiene su propio use case. Un use case = una responsabilidad.
|
|
59
|
+
El ViewModel llama al use case; no contiene lógica de negocio.
|
|
60
|
+
|
|
61
|
+
```kotlin
|
|
62
|
+
// BIEN — use case con responsabilidad única
|
|
63
|
+
class CrearPedidoUseCase(
|
|
64
|
+
private val pedidoRepo: PedidoRepository,
|
|
65
|
+
private val inventarioRepo: InventarioRepository,
|
|
66
|
+
) {
|
|
67
|
+
suspend operator fun invoke(items: List<ItemPedido>): Result<Pedido> {
|
|
68
|
+
if (items.isEmpty()) return Result.failure(IllegalArgumentException("Sin items"))
|
|
69
|
+
val disponible = inventarioRepo.verificarDisponibilidad(items)
|
|
70
|
+
if (!disponible) return Result.failure(StockInsuficienteException())
|
|
71
|
+
val pedido = Pedido.crear(items)
|
|
72
|
+
pedidoRepo.guardar(pedido)
|
|
73
|
+
return Result.success(pedido)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// MAL — lógica de negocio en el ViewModel
|
|
78
|
+
class PedidoViewModel(private val repo: PedidoRepository) : ViewModel() {
|
|
79
|
+
fun crearPedido(items: List<ItemPedido>) {
|
|
80
|
+
if (items.isEmpty()) { /* lógica aquí — MAL */ }
|
|
81
|
+
viewModelScope.launch { repo.guardar(...) }
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Sealed classes para estados de UI
|
|
89
|
+
|
|
90
|
+
```kotlin
|
|
91
|
+
sealed class UiState<out T> {
|
|
92
|
+
object Loading : UiState<Nothing>()
|
|
93
|
+
data class Success<T>(val data: T) : UiState<T>()
|
|
94
|
+
data class Error(val mensaje: String, val causa: Throwable? = null) : UiState<Nothing>()
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// ViewModel expone StateFlow de UiState
|
|
98
|
+
class PedidoViewModel(private val obtenerPedidos: ObtenerPedidosUseCase) : ViewModel() {
|
|
99
|
+
private val _uiState = MutableStateFlow<UiState<List<Pedido>>>(UiState.Loading)
|
|
100
|
+
val uiState: StateFlow<UiState<List<Pedido>>> = _uiState.asStateFlow()
|
|
101
|
+
|
|
102
|
+
init {
|
|
103
|
+
cargarPedidos()
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
private fun cargarPedidos() {
|
|
107
|
+
viewModelScope.launch {
|
|
108
|
+
obtenerPedidos()
|
|
109
|
+
.onStart { _uiState.value = UiState.Loading }
|
|
110
|
+
.catch { e -> _uiState.value = UiState.Error(e.message ?: "Error") }
|
|
111
|
+
.collect { pedidos -> _uiState.value = UiState.Success(pedidos) }
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Dependency Injection con Hilt (Android) o Koin
|
|
120
|
+
|
|
121
|
+
Preferir **Hilt** en proyectos Android nuevos por su integración con el compilador.
|
|
122
|
+
**Koin** es aceptable en proyectos multiplatforma o Ktor.
|
|
123
|
+
|
|
124
|
+
```kotlin
|
|
125
|
+
// Hilt — módulo de provisión
|
|
126
|
+
@Module
|
|
127
|
+
@InstallIn(SingletonComponent::class)
|
|
128
|
+
object RepositorioModule {
|
|
129
|
+
@Provides
|
|
130
|
+
@Singleton
|
|
131
|
+
fun providePedidoRepository(dao: PedidoDao): PedidoRepository =
|
|
132
|
+
PedidoRepositoryImpl(dao)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ViewModel con Hilt
|
|
136
|
+
@HiltViewModel
|
|
137
|
+
class PedidoViewModel @Inject constructor(
|
|
138
|
+
private val obtenerPedidos: ObtenerPedidosUseCase,
|
|
139
|
+
) : ViewModel()
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## Jetpack Compose: remember, LaunchedEffect, derivedStateOf
|
|
145
|
+
|
|
146
|
+
```kotlin
|
|
147
|
+
@Composable
|
|
148
|
+
fun PantallaPedidos(viewModel: PedidoViewModel = hiltViewModel()) {
|
|
149
|
+
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
|
150
|
+
|
|
151
|
+
// remember: cálculos costosos que no deben recalcularse en cada recomposición
|
|
152
|
+
val pedidosOrdenados = remember(uiState) {
|
|
153
|
+
if (uiState is UiState.Success) {
|
|
154
|
+
(uiState as UiState.Success).data.sortedByDescending { it.fecha }
|
|
155
|
+
} else emptyList()
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// derivedStateOf: valor derivado de otro estado, evita recomposiciones en cascada
|
|
159
|
+
val hayPedidos by remember { derivedStateOf { pedidosOrdenados.isNotEmpty() } }
|
|
160
|
+
|
|
161
|
+
// LaunchedEffect: efectos de un solo disparo atados al ciclo de vida del composable
|
|
162
|
+
LaunchedEffect(Unit) {
|
|
163
|
+
viewModel.eventos.collect { evento ->
|
|
164
|
+
when (evento) {
|
|
165
|
+
is Evento.NavegacionDetalle -> navController.navigate(...)
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## Clean Architecture: capas y dependencias
|
|
175
|
+
|
|
176
|
+
```
|
|
177
|
+
presentation/ — Composables, ViewModels, UiState
|
|
178
|
+
|
|
|
179
|
+
v
|
|
180
|
+
domain/ — UseCases, interfaces de Repository, modelos de dominio
|
|
181
|
+
|
|
|
182
|
+
v
|
|
183
|
+
data/ — implementaciones de Repository, DAOs, API clients, mappers
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
La capa `domain` NO importa nada de `presentation` ni `data`.
|
|
187
|
+
Los modelos de datos de Room o Retrofit NO se exponen a la capa de presentación.
|
|
188
|
+
Usar mappers explícitos entre capas.
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## Checklist de patrones Kotlin antes de abrir PR
|
|
193
|
+
|
|
194
|
+
- [ ] Sin callbacks: todo async usa suspend o Flow
|
|
195
|
+
- [ ] Repositories tienen interfaz en dominio e implementación en datos
|
|
196
|
+
- [ ] Lógica de negocio en use cases, no en ViewModels ni Repositories
|
|
197
|
+
- [ ] Estados de UI representados con sealed class (Loading/Success/Error)
|
|
198
|
+
- [ ] ViewModels exponen StateFlow, no MutableStateFlow público
|
|
199
|
+
- [ ] DI configurada con Hilt o Koin, sin instanciación manual de dependencias
|
|
200
|
+
- [ ] Composables con remember/derivedStateOf para evitar recomposiciones
|
|
201
|
+
- [ ] Capas de Clean Architecture sin dependencias inversas
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# Regla: Seguridad en Kotlin / Android
|
|
2
|
+
|
|
3
|
+
Esta regla es OBLIGATORIA para todo código Kotlin en Android o backend.
|
|
4
|
+
Los errores de seguridad en mobile son especialmente costosos: los APKs son
|
|
5
|
+
descargables y analizables por cualquier actor malicioso.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## ProGuard/R8: ofuscación obligatoria en release
|
|
10
|
+
|
|
11
|
+
R8 (sucesor de ProGuard) es la herramienta de ofuscación, shrinking y optimización
|
|
12
|
+
integrada en Gradle. Debe estar habilitada en todos los builds de release.
|
|
13
|
+
|
|
14
|
+
```kotlin
|
|
15
|
+
// build.gradle.kts — Android
|
|
16
|
+
android {
|
|
17
|
+
buildTypes {
|
|
18
|
+
release {
|
|
19
|
+
isMinifyEnabled = true // ofuscar y reducir código
|
|
20
|
+
isShrinkResources = true // eliminar recursos no usados
|
|
21
|
+
proguardFiles(
|
|
22
|
+
getDefaultProguardFile("proguard-android-optimize.txt"),
|
|
23
|
+
"proguard-rules.pro"
|
|
24
|
+
)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Verificar que las reglas de ProGuard no excluyen clases sensibles innecesariamente.
|
|
31
|
+
Los modelos de datos serializados con Retrofit/Gson/Moshi necesitan reglas explícitas.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## EncryptedSharedPreferences para datos sensibles locales
|
|
36
|
+
|
|
37
|
+
`SharedPreferences` guarda datos en texto plano. NUNCA almacenar tokens, IDs de sesión
|
|
38
|
+
ni datos personales en SharedPreferences sin cifrado.
|
|
39
|
+
|
|
40
|
+
```kotlin
|
|
41
|
+
// MAL — texto plano, legible con adb o en dispositivos rooteados
|
|
42
|
+
sharedPreferences.edit().putString("access_token", token).apply()
|
|
43
|
+
|
|
44
|
+
// BIEN — cifrado con EncryptedSharedPreferences (Jetpack Security)
|
|
45
|
+
val masterKey = MasterKey.Builder(context)
|
|
46
|
+
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
|
|
47
|
+
.build()
|
|
48
|
+
|
|
49
|
+
val prefs = EncryptedSharedPreferences.create(
|
|
50
|
+
context,
|
|
51
|
+
"prefs_seguras",
|
|
52
|
+
masterKey,
|
|
53
|
+
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
|
54
|
+
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM,
|
|
55
|
+
)
|
|
56
|
+
prefs.edit().putString("access_token", token).apply()
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## No almacenar secretos en BuildConfig
|
|
62
|
+
|
|
63
|
+
Los valores en `BuildConfig` quedan en el DEX y son extraíbles con herramientas
|
|
64
|
+
como `jadx` o `apktool`. Un atacante puede recuperar la API key en minutos.
|
|
65
|
+
|
|
66
|
+
```kotlin
|
|
67
|
+
// MAL — la key queda en el APK descompilado
|
|
68
|
+
buildConfigField("String", "API_KEY", "\"sk-prod-abc123\"")
|
|
69
|
+
|
|
70
|
+
// BIEN — la key se obtiene del servidor en runtime, autenticando al usuario primero
|
|
71
|
+
// El backend actua como proxy: el cliente nunca toca la API key directamente
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Para claves que el cliente DEBE conocer (ej: clave pública de SDK de pagos):
|
|
75
|
+
usar Android Keystore + certificate pinning como defensa adicional.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Certificate Pinning en OkHttp
|
|
80
|
+
|
|
81
|
+
Sin certificate pinning, un atacante con un certificado CA fraudulento puede
|
|
82
|
+
interceptar el tráfico HTTPS (man-in-the-middle).
|
|
83
|
+
|
|
84
|
+
```kotlin
|
|
85
|
+
// BIEN — pinning por clave pública (más flexible que hash del certificado)
|
|
86
|
+
val certificatePinner = CertificatePinner.Builder()
|
|
87
|
+
.add("api.miapp.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
|
|
88
|
+
.add("api.miapp.com", "sha256/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=") // backup pin
|
|
89
|
+
.build()
|
|
90
|
+
|
|
91
|
+
val okHttpClient = OkHttpClient.Builder()
|
|
92
|
+
.certificatePinner(certificatePinner)
|
|
93
|
+
.build()
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Incluir SIEMPRE un pin de respaldo (backup pin) para permitir rotación de certificados
|
|
97
|
+
sin dejar la app sin servicio. Documentar el proceso de rotación de pins.
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## SQL con Room: queries parametrizados
|
|
102
|
+
|
|
103
|
+
Room previene SQL injection por defecto cuando se usan los parámetros correctamente.
|
|
104
|
+
NUNCA construir queries con concatenación de strings.
|
|
105
|
+
|
|
106
|
+
```kotlin
|
|
107
|
+
// MAL — SQL injection posible
|
|
108
|
+
@Dao
|
|
109
|
+
interface PedidoDao {
|
|
110
|
+
@RawQuery
|
|
111
|
+
fun buscarPorEstatus(query: SupportSQLiteQuery): List<Pedido>
|
|
112
|
+
}
|
|
113
|
+
// Uso peligroso: SimpleSQLiteQuery("SELECT * FROM pedidos WHERE estatus = '$estatus'")
|
|
114
|
+
|
|
115
|
+
// BIEN — parámetro tipado, Room lo escapa automáticamente
|
|
116
|
+
@Dao
|
|
117
|
+
interface PedidoDao {
|
|
118
|
+
@Query("SELECT * FROM pedidos WHERE estatus = :estatus")
|
|
119
|
+
suspend fun buscarPorEstatus(estatus: String): List<Pedido>
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Autenticación biométrica con BiometricPrompt
|
|
126
|
+
|
|
127
|
+
```kotlin
|
|
128
|
+
// BIEN — BiometricPrompt con manejo correcto de resultados
|
|
129
|
+
fun autenticarConBiometria(actividad: FragmentActivity, onExito: () -> Unit) {
|
|
130
|
+
val executor = ContextCompat.getMainExecutor(actividad)
|
|
131
|
+
val callback = object : BiometricPrompt.AuthenticationCallback() {
|
|
132
|
+
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
|
|
133
|
+
onExito()
|
|
134
|
+
}
|
|
135
|
+
override fun onAuthenticationError(código: Int, mensaje: CharSequence) {
|
|
136
|
+
// Registrar error, NO exponer detalles al usuario
|
|
137
|
+
logger.warn("Biometric error $codigo")
|
|
138
|
+
}
|
|
139
|
+
override fun onAuthenticationFailed() {
|
|
140
|
+
// Intento fallido — no bloquear todavía, BiometricPrompt maneja reintentos
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
BiometricPrompt(actividad, executor, callback).authenticate(
|
|
145
|
+
BiometricPrompt.PromptInfo.Builder()
|
|
146
|
+
.setTitle("Confirmar identidad")
|
|
147
|
+
.setNegativeButtonText("Cancelar")
|
|
148
|
+
.setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG)
|
|
149
|
+
.build()
|
|
150
|
+
)
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## Network Security Config y validación de deep links
|
|
157
|
+
|
|
158
|
+
```xml
|
|
159
|
+
<!-- res/xml/network_security_config.xml -->
|
|
160
|
+
<network-security-config>
|
|
161
|
+
<base-config cleartextTrafficPermitted="false">
|
|
162
|
+
<trust-anchors>
|
|
163
|
+
<certificates src="system" />
|
|
164
|
+
</trust-anchors>
|
|
165
|
+
</base-config>
|
|
166
|
+
<!-- Solo en debug — nunca en release -->
|
|
167
|
+
<debug-overrides>
|
|
168
|
+
<trust-anchors>
|
|
169
|
+
<certificates src="user" />
|
|
170
|
+
</trust-anchors>
|
|
171
|
+
</debug-overrides>
|
|
172
|
+
</network-security-config>
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
```kotlin
|
|
176
|
+
// Validar deep links — nunca confiar en el scheme/host sin verificar
|
|
177
|
+
override fun onCreate(savedInstanceState: Bundle?) {
|
|
178
|
+
super.onCreate(savedInstanceState)
|
|
179
|
+
val uri = intent?.data ?: return
|
|
180
|
+
if (uri.host != "miapp.com" || uri.scheme != "https") {
|
|
181
|
+
logger.warn("Deep link invalido: $uri")
|
|
182
|
+
finish()
|
|
183
|
+
return
|
|
184
|
+
}
|
|
185
|
+
val pedidoId = uri.getQueryParameter("pedido_id")
|
|
186
|
+
?.let { UUID.fromString(it) } // lanza si no es UUID válido
|
|
187
|
+
?: run { finish(); return }
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## Checklist de seguridad Kotlin antes de abrir PR
|
|
194
|
+
|
|
195
|
+
- [ ] R8/ProGuard habilitado en builds de release
|
|
196
|
+
- [ ] Sin `SharedPreferences` sin cifrar para datos sensibles
|
|
197
|
+
- [ ] Sin secretos en `BuildConfig` o strings.xml
|
|
198
|
+
- [ ] Certificate pinning configurado con pin de respaldo
|
|
199
|
+
- [ ] Queries de Room con parámetros tipados (sin concatenación de strings)
|
|
200
|
+
- [ ] Deep links validados en host y scheme antes de procesar parámetros
|
|
201
|
+
- [ ] `clearTextTrafficPermitted="false"` en network security config
|
|
202
|
+
- [ ] Biometric auth usa `BiometricPrompt`, no APIs deprecadas
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
# Regla: Testing en Kotlin
|
|
2
|
+
|
|
3
|
+
Esta regla es OBLIGATORIA para todo código Kotlin nuevo o modificado.
|
|
4
|
+
Las coroutines y los flows requieren herramientas específicas para testearse
|
|
5
|
+
correctamente. Un test de coroutine sin `runTest` no es determinista.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Stack de testing obligatorio
|
|
10
|
+
|
|
11
|
+
| Herramienta | Propósito |
|
|
12
|
+
|------------|----------|
|
|
13
|
+
| JUnit 5 | Framework base de tests |
|
|
14
|
+
| MockK | Mocking idiomático para Kotlin (no Mockito) |
|
|
15
|
+
| Turbine | Testing de `Flow` con `test {}` API |
|
|
16
|
+
| kotlinx-coroutines-test | `runTest`, `TestDispatcher`, `advanceUntilIdle` |
|
|
17
|
+
| Kover | Cobertura de código (reemplaza JaCoCo para Kotlin) |
|
|
18
|
+
| Compose UI Test | Testing de Composables con `createComposeRule` |
|
|
19
|
+
|
|
20
|
+
```kotlin
|
|
21
|
+
// build.gradle.kts — dependencias de test
|
|
22
|
+
dependencies {
|
|
23
|
+
testImplementation("org.junit.jupiter:junit-jupiter:5.10.2")
|
|
24
|
+
testImplementation("io.mockk:mockk:1.13.10")
|
|
25
|
+
testImplementation("app.cash.turbine:turbine:1.1.0")
|
|
26
|
+
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.8.0")
|
|
27
|
+
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## runTest y TestDispatcher para coroutines
|
|
34
|
+
|
|
35
|
+
`runTest` es el único punto de entrada correcto para tests de suspend functions.
|
|
36
|
+
Reemplaza `runBlocking` en tests — maneja los virtual timers del test framework.
|
|
37
|
+
|
|
38
|
+
```kotlin
|
|
39
|
+
// MAL — runBlocking no controla el tiempo virtual ni los dispatchers de coroutines
|
|
40
|
+
@Test
|
|
41
|
+
fun `obtener usuario por id retorna usuario correcto`() = runBlocking {
|
|
42
|
+
val resultado = repositorio.obtenerPorId(uuid)
|
|
43
|
+
assertEquals("Juan", resultado?.nombre)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// BIEN — runTest con dispatcher controlado
|
|
47
|
+
@Test
|
|
48
|
+
fun `obtener usuario por id retorna usuario correcto`() = runTest {
|
|
49
|
+
val resultado = repositorio.obtenerPorId(uuid)
|
|
50
|
+
assertEquals("Juan", resultado?.nombre)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// BIEN — StandardTestDispatcher para controlar avance del tiempo
|
|
54
|
+
@Test
|
|
55
|
+
fun `cargando pedidos emite loading luego success`() = runTest {
|
|
56
|
+
val dispatcher = StandardTestDispatcher(testScheduler)
|
|
57
|
+
val viewModel = PedidoViewModel(obtenerPedidosUseCase, dispatcher)
|
|
58
|
+
|
|
59
|
+
val estados = mutableListOf<UiState<*>>()
|
|
60
|
+
val job = launch { viewModel.uiState.collect { estados.add(it) } }
|
|
61
|
+
|
|
62
|
+
advanceUntilIdle()
|
|
63
|
+
|
|
64
|
+
assertEquals(UiState.Loading, estados.first())
|
|
65
|
+
assertIs<UiState.Success<*>>(estados.last())
|
|
66
|
+
job.cancel()
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Turbine para testing de Flow
|
|
73
|
+
|
|
74
|
+
```kotlin
|
|
75
|
+
// MAL — collect en test puede colgarse o perder emisiones
|
|
76
|
+
@Test
|
|
77
|
+
fun `flujo emite pedidos`() = runTest {
|
|
78
|
+
val emisiones = mutableListOf<List<Pedido>>()
|
|
79
|
+
val job = launch { repositorio.obtenerTodos().collect { emisiones.add(it) } }
|
|
80
|
+
job.cancel()
|
|
81
|
+
assertTrue(emisiones.isNotEmpty())
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// BIEN — Turbine provee API declarativa y determinista
|
|
85
|
+
@Test
|
|
86
|
+
fun `flujo emite lista de pedidos`() = runTest {
|
|
87
|
+
repositorio.obtenerTodos().test {
|
|
88
|
+
val pedidos = awaitItem()
|
|
89
|
+
assertEquals(3, pedidos.size)
|
|
90
|
+
cancelAndIgnoreRemainingEvents()
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// BIEN — verificar múltiples emisiones en orden
|
|
95
|
+
@Test
|
|
96
|
+
fun `estado transiciona de loading a success`() = runTest {
|
|
97
|
+
viewModel.uiState.test {
|
|
98
|
+
assertIs<UiState.Loading>(awaitItem())
|
|
99
|
+
val success = awaitItem()
|
|
100
|
+
assertIs<UiState.Success<*>>(success)
|
|
101
|
+
cancelAndIgnoreRemainingEvents()
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## MockK para mocks idiomáticos en Kotlin
|
|
109
|
+
|
|
110
|
+
```kotlin
|
|
111
|
+
// Configuración típica con MockK
|
|
112
|
+
@ExtendWith(MockKExtension::class)
|
|
113
|
+
class CrearPedidoUseCaseTest {
|
|
114
|
+
|
|
115
|
+
@MockK
|
|
116
|
+
private lateinit var pedidoRepo: PedidoRepository
|
|
117
|
+
|
|
118
|
+
@MockK
|
|
119
|
+
private lateinit var inventarioRepo: InventarioRepository
|
|
120
|
+
|
|
121
|
+
private lateinit var crearPedido: CrearPedidoUseCase
|
|
122
|
+
|
|
123
|
+
@BeforeEach
|
|
124
|
+
fun setup() {
|
|
125
|
+
crearPedido = CrearPedidoUseCase(pedidoRepo, inventarioRepo)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
@Test
|
|
129
|
+
fun `crear pedido con items disponibles retorna pedido creado`() = runTest {
|
|
130
|
+
// Given
|
|
131
|
+
val items = listOf(ItemPedido(productoId = uuid, cantidad = 2))
|
|
132
|
+
coEvery { inventarioRepo.verificarDisponibilidad(items) } returns true
|
|
133
|
+
coJustRun { pedidoRepo.guardar(any()) }
|
|
134
|
+
|
|
135
|
+
// When
|
|
136
|
+
val resultado = crearPedido(items)
|
|
137
|
+
|
|
138
|
+
// Then
|
|
139
|
+
assertTrue(resultado.isSuccess)
|
|
140
|
+
coVerify(exactly = 1) { pedidoRepo.guardar(any()) }
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Patrón Given-When-Then
|
|
148
|
+
|
|
149
|
+
Todos los tests siguen el patrón Given-When-Then con comentarios explícitos.
|
|
150
|
+
|
|
151
|
+
```kotlin
|
|
152
|
+
@Test
|
|
153
|
+
fun `crear pedido sin items retorna error de validación`() = runTest {
|
|
154
|
+
// Given
|
|
155
|
+
val itemsVacios = emptyList<ItemPedido>()
|
|
156
|
+
|
|
157
|
+
// When
|
|
158
|
+
val resultado = crearPedido(itemsVacios)
|
|
159
|
+
|
|
160
|
+
// Then
|
|
161
|
+
assertTrue(resultado.isFailure)
|
|
162
|
+
assertIs<IllegalArgumentException>(resultado.exceptionOrNull())
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## Cobertura con Kover
|
|
169
|
+
|
|
170
|
+
```kotlin
|
|
171
|
+
// build.gradle.kts
|
|
172
|
+
plugins {
|
|
173
|
+
id("org.jetbrains.kotlinx.kover") version "0.7.6"
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
koverReport {
|
|
177
|
+
filters {
|
|
178
|
+
excludes {
|
|
179
|
+
classes("**/di/**", "**/*Module*", "**/*_Factory*")
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
verify {
|
|
183
|
+
rule {
|
|
184
|
+
bound { minValue = 80 }
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## Testing de Composables
|
|
193
|
+
|
|
194
|
+
```kotlin
|
|
195
|
+
class PantallaPedidosTest {
|
|
196
|
+
|
|
197
|
+
@get:Rule
|
|
198
|
+
val composeTestRule = createComposeRule()
|
|
199
|
+
|
|
200
|
+
@Test
|
|
201
|
+
fun `muestra lista de pedidos cuando estado es success`() {
|
|
202
|
+
// Given
|
|
203
|
+
val pedidos = listOf(Pedido(id = uuid, total = 150.0))
|
|
204
|
+
val estado = UiState.Success(pedidos)
|
|
205
|
+
|
|
206
|
+
// When
|
|
207
|
+
composeTestRule.setContent {
|
|
208
|
+
PantallaPedidos(uiState = estado)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Then
|
|
212
|
+
composeTestRule.onNodeWithText("$150.00").assertIsDisplayed()
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
@Test
|
|
216
|
+
fun `muestra indicador de carga cuando estado es loading`() {
|
|
217
|
+
composeTestRule.setContent {
|
|
218
|
+
PantallaPedidos(uiState = UiState.Loading)
|
|
219
|
+
}
|
|
220
|
+
composeTestRule.onNodeWithTag("loading_indicator").assertIsDisplayed()
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## Checklist de testing Kotlin antes de abrir PR
|
|
228
|
+
|
|
229
|
+
- [ ] Tests de coroutines usan `runTest`, no `runBlocking`
|
|
230
|
+
- [ ] Tests de Flow usan Turbine (`flow.test { }`)
|
|
231
|
+
- [ ] Mocks creados con MockK, no con Mockito
|
|
232
|
+
- [ ] `coEvery`/`coVerify` para suspend functions mockeadas
|
|
233
|
+
- [ ] Patrón Given-When-Then con comentarios explícitos
|
|
234
|
+
- [ ] Cobertura >= 80% verificada con Kover
|
|
235
|
+
- [ ] Tests de Composables con `createComposeRule`
|
|
236
|
+
- [ ] Sin `Thread.sleep()` ni delays fijos en tests
|