@imolko/ultra-agent 0.1.0-beta
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/README.md +93 -0
- package/bin/cli.js +2 -0
- package/dist-node/bin.d.ts +2 -0
- package/dist-node/bin.d.ts.map +1 -0
- package/dist-node/bin.js +5 -0
- package/dist-node/bin.js.map +1 -0
- package/dist-node/cli.d.ts +41 -0
- package/dist-node/cli.d.ts.map +1 -0
- package/dist-node/cli.js +170 -0
- package/dist-node/cli.js.map +1 -0
- package/dist-node/index.d.ts +14 -0
- package/dist-node/index.d.ts.map +1 -0
- package/dist-node/index.js +40 -0
- package/dist-node/index.js.map +1 -0
- package/dist-node/init-lint.d.ts +43 -0
- package/dist-node/init-lint.d.ts.map +1 -0
- package/dist-node/init-lint.js +84 -0
- package/dist-node/init-lint.js.map +1 -0
- package/dist-node/manifest.d.ts +33 -0
- package/dist-node/manifest.d.ts.map +1 -0
- package/dist-node/manifest.js +59 -0
- package/dist-node/manifest.js.map +1 -0
- package/dist-node/materializer.d.ts +52 -0
- package/dist-node/materializer.d.ts.map +1 -0
- package/dist-node/materializer.js +194 -0
- package/dist-node/materializer.js.map +1 -0
- package/dist-node/package-root.d.ts +5 -0
- package/dist-node/package-root.d.ts.map +1 -0
- package/dist-node/package-root.js +17 -0
- package/dist-node/package-root.js.map +1 -0
- package/dist-node/payload.d.ts +22 -0
- package/dist-node/payload.d.ts.map +1 -0
- package/dist-node/payload.js +49 -0
- package/dist-node/payload.js.map +1 -0
- package/dist-node/pointer-hub.d.ts +22 -0
- package/dist-node/pointer-hub.d.ts.map +1 -0
- package/dist-node/pointer-hub.js +50 -0
- package/dist-node/pointer-hub.js.map +1 -0
- package/dist-node/publication.cli.d.ts +9 -0
- package/dist-node/publication.cli.d.ts.map +1 -0
- package/dist-node/publication.cli.js +17 -0
- package/dist-node/publication.cli.js.map +1 -0
- package/dist-node/publication.d.ts +23 -0
- package/dist-node/publication.d.ts.map +1 -0
- package/dist-node/publication.js +47 -0
- package/dist-node/publication.js.map +1 -0
- package/dist-node/reconcile.cli.d.ts +11 -0
- package/dist-node/reconcile.cli.d.ts.map +1 -0
- package/dist-node/reconcile.cli.js +39 -0
- package/dist-node/reconcile.cli.js.map +1 -0
- package/dist-node/reconcile.d.ts +30 -0
- package/dist-node/reconcile.d.ts.map +1 -0
- package/dist-node/reconcile.js +51 -0
- package/dist-node/reconcile.js.map +1 -0
- package/dist-node/ship/prompts/fix.md +22 -0
- package/dist-node/ship/prompts/implement.md +34 -0
- package/dist-node/ship/prompts/review.md +28 -0
- package/dist-node/ship/ship.agent.d.ts +22 -0
- package/dist-node/ship/ship.agent.d.ts.map +1 -0
- package/dist-node/ship/ship.agent.js +142 -0
- package/dist-node/ship/ship.agent.js.map +1 -0
- package/dist-node/ship/ship.args.d.ts +15 -0
- package/dist-node/ship/ship.args.d.ts.map +1 -0
- package/dist-node/ship/ship.args.js +93 -0
- package/dist-node/ship/ship.args.js.map +1 -0
- package/dist-node/ship/ship.context.d.ts +12 -0
- package/dist-node/ship/ship.context.d.ts.map +1 -0
- package/dist-node/ship/ship.context.js +3 -0
- package/dist-node/ship/ship.context.js.map +1 -0
- package/dist-node/ship/ship.d.ts +4 -0
- package/dist-node/ship/ship.d.ts.map +1 -0
- package/dist-node/ship/ship.js +221 -0
- package/dist-node/ship/ship.js.map +1 -0
- package/dist-node/ship/ship.mr.d.ts +9 -0
- package/dist-node/ship/ship.mr.d.ts.map +1 -0
- package/dist-node/ship/ship.mr.js +171 -0
- package/dist-node/ship/ship.mr.js.map +1 -0
- package/dist-node/ship/ship.output.d.ts +9 -0
- package/dist-node/ship/ship.output.d.ts.map +1 -0
- package/dist-node/ship/ship.output.js +21 -0
- package/dist-node/ship/ship.output.js.map +1 -0
- package/dist-node/ship/ship.tracker.d.ts +37 -0
- package/dist-node/ship/ship.tracker.d.ts.map +1 -0
- package/dist-node/ship/ship.tracker.js +220 -0
- package/dist-node/ship/ship.tracker.js.map +1 -0
- package/dist-node/version.cli.d.ts +9 -0
- package/dist-node/version.cli.d.ts.map +1 -0
- package/dist-node/version.cli.js +30 -0
- package/dist-node/version.cli.js.map +1 -0
- package/dist-node/version.d.ts +24 -0
- package/dist-node/version.d.ts.map +1 -0
- package/dist-node/version.js +76 -0
- package/dist-node/version.js.map +1 -0
- package/docs/adr/ADR-0000-proceso-y-formato-de-adrs.md +115 -0
- package/docs/adr/foundational/A-001-clean-architecture-4-capas.md +88 -0
- package/docs/adr/foundational/A-002-cqrs-semantico.md +58 -0
- package/docs/adr/foundational/A-003-tdd.md +97 -0
- package/docs/adr/foundational/A-005-either-return-custom-methods.md +80 -0
- package/docs/adr/foundational/A-006-feature-completion-criteria.md +47 -0
- package/docs/adr/foundational/A-007-data-contract-fail-fast.md +109 -0
- package/docs/adr/foundational/A-008-bounded-context-decomposition.md +130 -0
- package/docs/adr/foundational/A-009-use-case-taxonomy.md +99 -0
- package/docs/adr/foundational/A-010-propiedad-emision-eventos-dominio.md +95 -0
- package/docs/adr/foundational/A-011-characterization-first-refactoring.md +90 -0
- package/docs/adr/foundational/A-012-helm-mecanismo-primario-despliegue.md +67 -0
- package/docs/adr/foundational/A-013-contrato-settings-unica-fuente-configuracion.md +74 -0
- package/docs/adr/foundational/A-014-generacion-artefactos-despliegue-desde-contrato-settings.md +75 -0
- package/docs/adr/foundational/A-015-cloudevents-estandar-eventos.md +85 -0
- package/docs/adr/foundational/A-016-cicd-gitlab-pipeline-raiz-pipelines-modulares.md +92 -0
- package/docs/adr/foundational/A-017-taxonomia-repositorios-librerias-sistemas.md +93 -0
- package/docs/adr/foundational/A-018-practicas-build-imagenes-docker.md +85 -0
- package/docs/adr/foundational/A-019-convenciones-autoria-helm-charts.md +97 -0
- package/docs/adr/foundational/A-020-entorno-desarrollo-estandarizado-devcontainer.md +58 -0
- package/docs/adr/foundational/A-022-formato-reporte-tests-ctrf.md +55 -0
- package/docs/adr/foundational/A-023-composicion-bloques-prompt-agentes.md +65 -0
- package/docs/adr/foundational/A-024-features-slices-verticales-portables.md +41 -0
- package/docs/adr/foundational/A-025-anatomia-mecanica-feature-puertos-adaptadores-drivers.md +104 -0
- package/docs/adr/foundational/A-026-infraestructura-compartida.md +44 -0
- package/docs/adr/foundational/A-027-patron-wiring-entry-point.md +67 -0
- package/docs/adr/foundational/A-029-verbos-intencion-metodos-lado-negocio.md +54 -0
- package/docs/adr/foundational/A-030-global-development-tooling.md +52 -0
- package/docs/adr/foundational/A-031-ubiquitous-language.md +64 -0
- package/docs/adr/foundational/A-032-domain-artifact-documentation-format.md +128 -0
- package/docs/adr/foundational/A-033-estandar-mensajes-commit.md +127 -0
- package/docs/adr/foundational/A-034-canonical-skill-set.md +64 -0
- package/docs/adr/foundational/A-035-captura-ideas-reanclaje-candies.md +51 -0
- package/docs/adr/foundational/A-036-adopcion-wayfinder-ortogonalidad-labels.md +37 -0
- package/docs/adr/foundational/A-037-formalizacion-taxonomia-labels.md +98 -0
- package/docs/adr/foundational/A-038-issues-referencian-skills-nombre.md +54 -0
- package/docs/adr/foundational/A-039-pipeline-ship-issues-hijos-spec.md +64 -0
- package/docs/adr/foundational/A-040-punteros-configuracion-repo-skills.md +44 -0
- package/docs/adr/foundational/A-041-estrategia-de-ramas.md +45 -0
- package/docs/adr/foundational/A-042-formato-documentos-issue-tracker.md +78 -0
- package/docs/adr/foundational/A-043-vocabulario-etiquetas-adrs.md +42 -0
- package/docs/adr/foundational/A-044-composicion-funcional-procesos-operaciones.md +44 -0
- package/docs/adr/foundational/A-045-guardrails-aplicables-no-directrices.md +48 -0
- package/docs/adr/foundational/A-046-metricas-de-forma-y-legibilidad-del-codigo.md +50 -0
- package/docs/adr/foundational/A-047-dependencias-explicitas-sin-globales-ambientales.md +40 -0
- package/docs/adr/foundational/A-048-cobertura-minima-codigo.md +39 -0
- package/docs/adr/foundational/A-049-materializacion-bucle-guardrails-extension-nativa-pi.md +49 -0
- package/docs/adr/foundational/A-050-el-caso-de-uso-pide-al-dominio-no-lee-su-representacion.md +50 -0
- package/docs/adr/foundational/A-051-orden-imports-por-distancia.md +47 -0
- package/docs/adr/foundational/A-052-distribucion-metodologia-paquete-versionado.md +42 -0
- package/docs/adr/foundational/A-053-errores-compartidos-entre-features-dos-hogares-por-capa.md +43 -0
- package/docs/adr/foundational/A-054-contrato-costura-driver-port-raw-data-canal-error.md +65 -0
- package/docs/adr/foundational/index.md +98 -0
- package/docs/adr/index.md +19 -0
- package/docs/adr/python/P-001-dependencias-por-feature-fastapi-depends.md +63 -0
- package/docs/adr/python/P-002-inyeccion-token-por-request-sin-persistirlo.md +35 -0
- package/docs/adr/python/P-003-sqlalchemy-async-connection-pooling.md +59 -0
- package/docs/adr/python/P-004-serializer-json-custom-modelos-pydantic.md +47 -0
- package/docs/adr/python/P-005-patrones-pitfalls-nicegui.md +148 -0
- package/docs/adr/python/P-006-convenciones-pytest.md +96 -0
- package/docs/adr/python/P-007-ci-python-alineacion-poetry-version-unica.md +55 -0
- package/docs/adr/python/P-008-empaquetado-infraestructura-compartida-por-tecnologia.md +38 -0
- package/docs/adr/python/index.md +14 -0
- package/docs/adr/typescript/T-001-ddd-artifact-validation-zod-primitives.md +136 -0
- package/docs/adr/typescript/T-002-test-documentation-jsdoc.md +133 -0
- package/docs/adr/typescript/T-003-readme-formatting-domain-artifacts.md +51 -0
- package/docs/adr/typescript/T-005-input-optionality-use-case-not-entity.md +130 -0
- package/docs/adr/typescript/T-006-small-enums-inlined-zod-regex.md +77 -0
- package/docs/adr/typescript/T-007-controller-input-zod-schema.md +79 -0
- package/docs/adr/typescript/T-008-expressive-use-case-pipe.md +207 -0
- package/docs/adr/typescript/T-009-mdx-brace-escaping.md +80 -0
- package/docs/adr/typescript/T-010-stack-tooling-typescript-dominio.md +53 -0
- package/docs/adr/typescript/T-011-ultra-reporter-herramienta-canonica-documentacion.md +52 -0
- package/docs/adr/typescript/T-012-npm-workspaces-estructura-monorepo-clean-architecture.md +54 -0
- package/docs/adr/typescript/T-013-definicion-eventos-versionados-ultra-ddd.md +45 -0
- package/docs/adr/typescript/T-014-publicacion-distribucion-librerias-npm-semver.md +53 -0
- package/docs/adr/typescript/T-015-method-placement-definition.md +35 -0
- package/docs/adr/typescript/T-016-contrato-settings-zod.md +34 -0
- package/docs/adr/typescript/T-017-characterization-tests-golden-files.md +39 -0
- package/docs/adr/typescript/T-018-procedencia-campos-frontmatter.md +55 -0
- package/docs/adr/typescript/T-019-ultra-generator-herramienta-canonica-generacion.md +35 -0
- package/docs/adr/typescript/T-020-ultra-settings-herramienta-canonica-exportacion-despliegue.md +36 -0
- package/docs/adr/typescript/T-021-pipe-flujo-datos-operadores-semantica-contexto-inmutable.md +105 -0
- package/docs/adr/typescript/T-022-construction-is-execution-single-boundary.md +112 -0
- package/docs/adr/typescript/T-023-registry-cli-declara-dependencias-reales.md +84 -0
- package/docs/adr/typescript/T-024-paquete-ultra-agent-manifiesto-pi-peers-cli.md +39 -0
- package/docs/adr/typescript/T-025-publicacion-npm-trusted-publishing-procedencia.md +59 -0
- package/docs/adr/typescript/index.md +30 -0
- package/eslint/index.mjs +721 -0
- package/package.json +94 -0
- package/pi/extensions/handoff/README.md +92 -0
- package/pi/extensions/handoff/handoff-prompt.txt +9 -0
- package/pi/extensions/handoff/index.ts +296 -0
- package/pi/extensions/handoff/package.json +12 -0
- package/pi/extensions/qa-guardrail/README.md +108 -0
- package/pi/extensions/qa-guardrail/adapters/cli.ts +100 -0
- package/pi/extensions/qa-guardrail/adapters/eslint.ts +90 -0
- package/pi/extensions/qa-guardrail/adapters/registry.ts +45 -0
- package/pi/extensions/qa-guardrail/baseline.ts +74 -0
- package/pi/extensions/qa-guardrail/cerco.ts +134 -0
- package/pi/extensions/qa-guardrail/check.ts +185 -0
- package/pi/extensions/qa-guardrail/config.ts +189 -0
- package/pi/extensions/qa-guardrail/delta.ts +52 -0
- package/pi/extensions/qa-guardrail/feedback.ts +26 -0
- package/pi/extensions/qa-guardrail/index.ts +311 -0
- package/pi/extensions/qa-guardrail/nstop.ts +82 -0
- package/pi/extensions/qa-guardrail/package.json +12 -0
- package/pi/extensions/qa-guardrail/selection.ts +98 -0
- package/pi/extensions/qa-guardrail/types.ts +64 -0
- package/scripts/copy-assets.sh +10 -0
- package/scripts/reconcile-tags.sh +94 -0
- package/scripts/release-beta.sh +64 -0
- package/scripts/release-build.sh +53 -0
- package/scripts/release.sh +51 -0
- package/scripts/verify-adr-consistency.py +217 -0
- package/skills/code-review/SKILL.md +66 -0
- package/skills/codebase-design/SKILL.md +46 -0
- package/skills/configure-repo/SKILL.md +33 -0
- package/skills/diagnose-bug/SKILL.md +63 -0
- package/skills/domain-modeling/SKILL.md +56 -0
- package/skills/grill-me/SKILL.md +34 -0
- package/skills/i-have-an-idea/SKILL.md +166 -0
- package/skills/implement/SKILL.md +22 -0
- package/skills/research/SKILL.md +17 -0
- package/skills/ship/SKILL.md +26 -0
- package/skills/spike/SKILL.md +27 -0
- package/skills/tdd/SKILL.md +68 -0
- package/skills/tdd/references/deep-modules.md +53 -0
- package/skills/tdd/references/interface-design.md +74 -0
- package/skills/tdd/references/mocking.md +80 -0
- package/skills/tdd/references/refactoring.md +49 -0
- package/skills/tdd/references/tests.md +184 -0
- package/skills/to-specs/SKILL.md +141 -0
- package/skills/to-tasks/SKILL.md +143 -0
- package/skills/triage/SKILL.md +73 -0
- package/skills/ultra-generator/SKILL.md +263 -0
- package/skills/ultra-generator/references/aggregate-root.md +149 -0
- package/skills/ultra-generator/references/domain-event.md +174 -0
- package/skills/ultra-generator/references/entity.md +133 -0
- package/skills/ultra-generator/references/service.md +92 -0
- package/skills/ultra-generator/references/value-object.md +135 -0
- package/skills/wayfinder/SKILL.md +201 -0
- package/skills/workflow/SKILL.md +31 -0
- package/skills/writing-for-agents/SKILL-MECHANICS.md +32 -0
- package/skills/writing-for-agents/SKILL.md +86 -0
- package/skills/writing-for-agents/references/adrs.md +19 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# A-011: Refactoring con characterization tests
|
|
2
|
+
|
|
3
|
+
**Estado**: Aceptado
|
|
4
|
+
|
|
5
|
+
**Fecha**: 2025-07-09
|
|
6
|
+
|
|
7
|
+
**originally**: ultra-reporter-009, ultra-reporter-010
|
|
8
|
+
|
|
9
|
+
**tags**: testing
|
|
10
|
+
|
|
11
|
+
## Contexto
|
|
12
|
+
|
|
13
|
+
Al enfrentar código legacy o duplicado, el enfoque tradicional de "refactorizar y después probar" es riesgoso: sin tests que capturen el comportamiento actual, los cambios pueden introducir regresiones silenciosas. El riesgo se multiplica cuando existe lógica duplicada en múltiples ubicaciones (módulos compartidos, copias en diferentes lenguajes, métodos inline en clases monolíticas).
|
|
14
|
+
|
|
15
|
+
Antes de modificar la estructura del código, necesitamos una red de seguridad que garantice que el comportamiento observable se preserva.
|
|
16
|
+
|
|
17
|
+
## Decisión
|
|
18
|
+
|
|
19
|
+
Adoptar el enfoque **characterization-first**: escribir tests de caracterización antes de cualquier refactoring, siguiendo un orden estricto de tres fases.
|
|
20
|
+
|
|
21
|
+
### Fase 1: Caracterizar
|
|
22
|
+
|
|
23
|
+
Escribir tests que capturen el comportamiento actual del sistema, sin modificarlo. Estos tests:
|
|
24
|
+
|
|
25
|
+
- Ejecutan el código tal como existe hoy, con sus entradas y salidas reales
|
|
26
|
+
- No validan "corrección" — validan "así funciona ahora": el complemento del ciclo TDD, que especifica el comportamiento deseado ([A-003](./A-003-tdd.md))
|
|
27
|
+
- Cubren los caminos principales (happy path) y edge cases visibles
|
|
28
|
+
- Sirven como red de seguridad: si un refactor cambia el comportamiento, los tests lo detectan
|
|
29
|
+
|
|
30
|
+
### Fase 2: Refactorizar
|
|
31
|
+
|
|
32
|
+
Con los tests de caracterización en verde, modificar la estructura interna del código en un orden específico:
|
|
33
|
+
|
|
34
|
+
1. **Unificar código duplicado primero.** Extraer lógica común dispersa en múltiples ubicaciones hacia un módulo compartido. Todas las copias pasan a consumir la fuente unificada. Eliminar las copias redundantes.
|
|
35
|
+
2. **Migrar a la plataforma destino después.** Con la lógica ya consolidada en módulos compartidos, migrar los consumidores a la plataforma destino. Los módulos compartidos son la base estable sobre la que se construye la migración.
|
|
36
|
+
3. **Descomponer monolitos al final.** Con el código unificado y migrado, dividir archivos que exceden los límites de forma ([A-046](./A-046-metricas-de-forma-y-legibilidad-del-codigo.md)) en unidades composables ([A-044](./A-044-composicion-funcional-procesos-operaciones.md)).
|
|
37
|
+
|
|
38
|
+
**Por qué este orden:**
|
|
39
|
+
|
|
40
|
+
- Unificar primero crea la base compartida que necesitan la migración y la descomposición
|
|
41
|
+
- Migrar segundo se apoya en módulos ya consolidados, reduciendo la superficie de cambio
|
|
42
|
+
- Descomponer al final opera sobre código ya unificado y migrado, con dependencias claras
|
|
43
|
+
|
|
44
|
+
Cada paso tiene un punto de partida definido y tests que lo respaldan. Ningún paso requiere tocar más código del necesario.
|
|
45
|
+
|
|
46
|
+
### Qué código merece tests de caracterización
|
|
47
|
+
|
|
48
|
+
No todo el código justifica el esfuerzo de caracterizarlo. Aplicar estos criterios:
|
|
49
|
+
|
|
50
|
+
| Situación | ¿Caracterizar? | Razón |
|
|
51
|
+
|-----------|:---:|-------|
|
|
52
|
+
| **Fuente de verdad** (módulo canónico del que otras copias derivan) | ✅ Sí | Es la referencia contra la que se validan las copias |
|
|
53
|
+
| **Lógica duplicada** en múltiples ubicaciones | ❌ No | Testear solo la fuente de verdad; las copias se validan por comparación |
|
|
54
|
+
| **Código que será eliminado** (copias redundantes) | ❌ No | No invertir esfuerzo en código con fecha de caducidad |
|
|
55
|
+
| **Lógica de negocio compleja** con múltiples caminos | ✅ Sí | El riesgo de regresión justifica la inversión |
|
|
56
|
+
| **Coordinación I/O** (orquestación de llamadas externas) | ⚠️ Condicional | Solo si la orquestación tiene complejidad no trivial |
|
|
57
|
+
| **Configuración y wiring** (inyección de dependencias, setup) | ❌ No | Se valida con tests de integración post-refactor |
|
|
58
|
+
|
|
59
|
+
**Regla general**: testear solo la fuente de verdad, no las copias. Las copias se cubren indirectamente cuando, tras la unificación, consumen el módulo compartido.
|
|
60
|
+
|
|
61
|
+
### Orden de operaciones en la práctica
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
1. Identificar código duplicado y su fuente de verdad
|
|
65
|
+
2. Escribir characterization tests para la fuente de verdad
|
|
66
|
+
3. Extraer fuente de verdad a módulo compartido
|
|
67
|
+
4. Redirigir consumidores al módulo compartido
|
|
68
|
+
5. Eliminar copias redundantes
|
|
69
|
+
6. Migrar consumidores a plataforma destino
|
|
70
|
+
7. Descomponer monolitos en unidades composables
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Los tests de caracterización se ejecutan después de cada paso para verificar que el comportamiento se preserva.
|
|
74
|
+
|
|
75
|
+
## Consecuencias
|
|
76
|
+
|
|
77
|
+
### Positivas
|
|
78
|
+
|
|
79
|
+
- **Red de seguridad antes de tocar código**: los tests de caracterización capturan regresiones inmediatamente
|
|
80
|
+
- **Sin esfuerzo duplicado**: testear solo la fuente de verdad evita escribir los mismos tests para cada copia
|
|
81
|
+
- **Orden con fundamento**: cada fase construye la base para la siguiente, minimizando el riesgo
|
|
82
|
+
- **Criterios claros**: saber qué testear y qué no reduce la parálisis por análisis
|
|
83
|
+
- **Deuda técnica controlada**: las copias redundantes tienen fecha de caducidad desde el inicio
|
|
84
|
+
|
|
85
|
+
### Negativas
|
|
86
|
+
|
|
87
|
+
- **Inversión inicial**: caracterizar lleva tiempo antes de ver mejoras estructurales
|
|
88
|
+
- **Disciplina de orden**: saltarse una fase (ej. descomponer antes de unificar) crea retrabajo
|
|
89
|
+
- **Tests desechables**: los tests de caracterización pueden volverse redundantes una vez que se escriben tests unitarios post-refactor. Esto es aceptable: cumplieron su propósito como red de seguridad durante la transición
|
|
90
|
+
- **Compatibilidad temporal**: durante la fase de unificación puede ser necesario manejar diferencias de módulo entre el origen y la plataforma destino hasta completar la migración
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# A-012: Helm como mecanismo primario de despliegue para aplicaciones servidor
|
|
2
|
+
|
|
3
|
+
**Estado**: Aceptado
|
|
4
|
+
|
|
5
|
+
**Fecha**: 2026-08-12
|
|
6
|
+
|
|
7
|
+
**originally**: generator-021
|
|
8
|
+
|
|
9
|
+
**Enriquecido desde**: Generator ADR 021 — Helm Chart Deployment with Pre-Built Docker Image
|
|
10
|
+
|
|
11
|
+
**tags**: despliegue
|
|
12
|
+
|
|
13
|
+
## Contexto
|
|
14
|
+
|
|
15
|
+
Las aplicaciones servidor de Imolko se ejecutan sobre Kubernetes y requieren un mecanismo de despliegue que sea:
|
|
16
|
+
|
|
17
|
+
- **Declarativo**: el estado deseado se describe en archivos versionados, no en comandos improvisados.
|
|
18
|
+
- **Repetible**: desplegar dos veces con los mismos parámetros produce el mismo resultado.
|
|
19
|
+
- **Versionado**: cada despliegue queda asociado a una versión de chart y una versión de imagen, de modo que se puede volver atrás.
|
|
20
|
+
- **Validable**: los requisitos previos (secretos, config maps, recursos del clúster) se verifican antes de instalar.
|
|
21
|
+
|
|
22
|
+
Desplegar con `kubectl apply` de manifiestos sueltos, o con scripts ad-hoc, produce drift entre entornos, sin historial de versiones y sin validación de requisitos. Mantener esa maquinaria por app multiplica el trabajo y hace que cada app despliegue de una forma distinta.
|
|
23
|
+
|
|
24
|
+
## Decisión
|
|
25
|
+
|
|
26
|
+
Adoptar **Helm como mecanismo primario de despliegue para toda aplicación servidor**. Helm actúa como gestor de paquetes y de releases sobre Kubernetes: empaqueta los manifiestos en un chart versionado, los parametriza con valores y registra cada instalación como un release con historial y rollback.
|
|
27
|
+
|
|
28
|
+
### 1. Un chart por aplicación servidor
|
|
29
|
+
|
|
30
|
+
Cada aplicación servidor incluye su propio chart, co-localizado en el repositorio de la aplicación. No se usan charts compartidos entre aplicaciones: un chart expresa la topología de despliegue de una app concreta (deployment, servicio, config maps, secretos, hooks). Una app nueva copia el patrón de una existente y lo adapta.
|
|
31
|
+
|
|
32
|
+
### 2. Despliegue declarativo con install/upgrade
|
|
33
|
+
|
|
34
|
+
Todo despliegue se realiza con `helm install` (primera vez) y `helm upgrade` (cambios posteriores). No se aplican manifiestos sueltos con `kubectl` para recursos que pertenecen a la app. El historial de releases de Helm es la bitácora de despliegue: ante una regresión, `helm rollback` restaura la versión anterior.
|
|
35
|
+
|
|
36
|
+
### 3. Valores por entorno sobre una base común
|
|
37
|
+
|
|
38
|
+
Los valores se organizan con una base común y overlays por entorno (desarrollo, pruebas, producción), aplicando la regla de superposición del contrato de settings ([A-013](./A-013-contrato-settings-unica-fuente-configuracion.md)).
|
|
39
|
+
|
|
40
|
+
### 4. Artefactos de configuración generados, no mantenidos a mano
|
|
41
|
+
|
|
42
|
+
El chart consume los artefactos de configuración que se generan desde el contrato de settings (ver [A-014](./A-014-generacion-artefactos-despliegue-desde-contrato-settings.md)): plantillas de config map, de secretos y de valores de ejemplo. No se mantienen a mano listas de variables de entorno ni config maps duplicados; la fuente de verdad de qué configuración existe vive en el contrato de settings (ver [A-013](./A-013-contrato-settings-unica-fuente-configuracion.md)).
|
|
43
|
+
|
|
44
|
+
### 5. Imagen preconstruida por el pipeline
|
|
45
|
+
|
|
46
|
+
El chart despliega una imagen de contenedor que ya fue compilada y publicada por el pipeline de CI/CD. El clúster no compila, no instala dependencias y no ejecuta builds: solo despliega la imagen. Los fallos de compilación se detectan en el pipeline, no en producción. Compilar en el clúster — por ejemplo, en un Job hook de Helm en `post-install`/`post-upgrade` — reserva pods sobredimensionados para un trabajo que solo ocurre ocasionalmente.
|
|
47
|
+
|
|
48
|
+
### 6. Versión de chart e imagen rastreables
|
|
49
|
+
|
|
50
|
+
Cada despliegue referencia explícitamente una versión de chart y una etiqueta de imagen. No se despliega "la última": se despliega una versión concreta, de modo que un rollback es determinista.
|
|
51
|
+
|
|
52
|
+
## Consecuencias
|
|
53
|
+
|
|
54
|
+
### Positivas
|
|
55
|
+
|
|
56
|
+
- **Despliegues repetibles**: el mismo chart y los mismos valores producen el mismo resultado en cualquier entorno.
|
|
57
|
+
- **Historial y rollback**: cada cambio queda registrado como release; volver atrás es un comando, no una reconstrucción manual.
|
|
58
|
+
- **Separación build/deploy**: el pipeline construye, Helm despliega. El clúster no necesita acceso a redes de build ni recursos de compilación.
|
|
59
|
+
- **Configuración sin drift**: al consumir artefactos generados desde el contrato de settings, el chart no se desincroniza del código.
|
|
60
|
+
- **Validación de requisitos**: los hooks de Helm pueden verificar secretos y config maps antes de instalar, fallando con mensajes claros.
|
|
61
|
+
|
|
62
|
+
### Negativas
|
|
63
|
+
|
|
64
|
+
- **Curva de aprendizaje**: el equipo debe dominar la sintaxis de plantillas de Helm y el ciclo install/upgrade/rollback.
|
|
65
|
+
- **Mantenimiento de charts**: cada app mantiene su chart; un cambio de topología implica editar plantillas.
|
|
66
|
+
- **Depuración de plantillas opaca**: el resultado renderizado depende de la combinación de plantillas y valores; depurar por qué un manifiesto no salió como se esperaba puede ser no trivial.
|
|
67
|
+
- **Valores custom fuera del contrato**: los valores de Helm que no corresponden a un campo del contrato de settings se mantienen a mano en el values (excepción acotada, ver [A-014](./A-014-generacion-artefactos-despliegue-desde-contrato-settings.md)).
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# A-013: Contrato de settings como única fuente de configuración de la aplicación
|
|
2
|
+
|
|
3
|
+
**Estado**: Aceptado
|
|
4
|
+
|
|
5
|
+
**Fecha**: 2026-08-12
|
|
6
|
+
|
|
7
|
+
**originally**: cortex-001, generator-018
|
|
8
|
+
|
|
9
|
+
**Enriquecido desde**: Cortex ADR 001 — UltraSettings pattern; Generator ADR 018 — Settings Contract with schemas and auto-export
|
|
10
|
+
|
|
11
|
+
**tags**: configuracion
|
|
12
|
+
|
|
13
|
+
## Contexto
|
|
14
|
+
|
|
15
|
+
Cada aplicación necesita configuración que varía entre entornos (local, desarrollo, producción) y entre despliegues: URLs de servicios, flags de features, parámetros del clúster y secretos. Sin un contrato formal, la configuración se dispersa:
|
|
16
|
+
|
|
17
|
+
- Variables de entorno leídas ad-hoc en distintos puntos del código.
|
|
18
|
+
- Archivos de configuración duplicados entre entornos.
|
|
19
|
+
- Valores faltantes o mal tipados que producen errores en runtime en lugar de al arrancar.
|
|
20
|
+
- Drift entre la configuración que el código espera y la que el despliegue provee.
|
|
21
|
+
|
|
22
|
+
Cuando la configuración no tiene una definición única, nadie puede responder con certeza "¿qué settings existen y cuáles son obligatorios?" sin leer todo el código.
|
|
23
|
+
|
|
24
|
+
## Decisión
|
|
25
|
+
|
|
26
|
+
Declarar **toda la configuración de una aplicación en un único contrato de settings**, tipado y validado, que es la única fuente de verdad sobre qué configuración existe y cómo se valida.
|
|
27
|
+
|
|
28
|
+
### 1. Un contrato por aplicación, centralizado
|
|
29
|
+
|
|
30
|
+
Cada aplicación define exactamente un contrato de settings en un único archivo. Todo el código lee configuración a través de ese contrato; ningún módulo lee variables de entorno o archivos de configuración por su cuenta. Agregar un setting es editar un solo lugar.
|
|
31
|
+
|
|
32
|
+
### 2. Cada campo declara tipo y origen
|
|
33
|
+
|
|
34
|
+
Cada setting anota metadatos que clasifican su naturaleza. La taxonomía distingue al menos:
|
|
35
|
+
|
|
36
|
+
| Tipo | Prefijo | Cambia por entorno | Contiene secretos | Ejemplo abstracto |
|
|
37
|
+
|------|---------|:---:|:---:|-------------------|
|
|
38
|
+
| **Metadato** | `APP_` | No | No | nombre y resumen de la app |
|
|
39
|
+
| **Entorno** | `ENV_` | Sí | No | dominio o modo de ejecución |
|
|
40
|
+
| **Infraestructura** | `INFRA_` | Sí | No | URL de un servicio externo |
|
|
41
|
+
| **Clúster** | `CLUSTER_` | Sí | No | réplicas, imagen, recursos |
|
|
42
|
+
| **Secreto** | `SECRET_` | Sí | Sí | token de API, credencial |
|
|
43
|
+
|
|
44
|
+
El prefijo hace visible la clasificación en el propio nombre del setting: `APP_`, `ENV_`, `INFRA_`, `CLUSTER_` y `SECRET_`. Un nombre como `SECRET_API_TOKEN` declara su naturaleza sin necesidad de consultar el metadato.
|
|
45
|
+
|
|
46
|
+
El metadato de cada campo es lo que permite, más adelante, derivar artefactos automáticamente ([A-014](./A-014-generacion-artefactos-despliegue-desde-contrato-settings.md)).
|
|
47
|
+
|
|
48
|
+
### 3. Validación fail-fast al arrancar
|
|
49
|
+
|
|
50
|
+
El contrato se valida al cargar la configuración, antes de que la aplicación procese trabajo real. Una configuración inválida (campo faltante, tipo incorrecto, secreto vacío) detiene el arranque con un error claro que identifica el campo y la causa. No se toleran valores parcialmente correctos.
|
|
51
|
+
|
|
52
|
+
### 4. Definición y valores viven en lugares distintos
|
|
53
|
+
|
|
54
|
+
El contrato define **qué** settings existen y cómo se validan. Los **valores** concretos por entorno viven fuera del contrato: archivos de entorno para desarrollo local y overlays por entorno para el despliegue. La regla es "común por defecto, explícito por diferencia": los valores idénticos entre entornos se declaran una sola vez; cada overlay sobrescribe únicamente lo que cambia.
|
|
55
|
+
|
|
56
|
+
Los valores no secretos se versionan en el repositorio junto al contrato: la base común y los overlays por entorno quedan bajo control de versiones, de modo que cada cambio de configuración es rastreable y comparable entre versiones. La única excepción son los secretos: sus valores reales nunca se versionan — no se commitea ningún `.env`, credencial ni `kubeconfig` — y cada entorno los provee en el momento del despliegue, fuera del repositorio. El repositorio solo contiene plantillas `.sample` que muestran la forma de los valores (nombre y tipo) sin su contenido.
|
|
57
|
+
|
|
58
|
+
### 5. El contrato es agnóstico a la tecnología de validación
|
|
59
|
+
|
|
60
|
+
El patrón (definición central, metadatos por campo, validación fail-fast) es independiente del lenguaje. Cada ecosistema lo implementa con su librería de validación y su sistema de tipos; el contrato de settings no depende de una librería concreta. Lo que se comparte entre proyectos es el patrón, no la implementación.
|
|
61
|
+
|
|
62
|
+
## Consecuencias
|
|
63
|
+
|
|
64
|
+
### Positivas
|
|
65
|
+
|
|
66
|
+
- **Una sola fuente de verdad**: la pregunta "¿qué configuración existe?" se responde abriendo un archivo.
|
|
67
|
+
- **Fallos tempranos**: la configuración inválida detiene la app al arrancar con un mensaje claro, no en runtime.
|
|
68
|
+
- **Taxonomía útil**: el metadato de tipo/origen habilita generación de artefactos ([A-014](./A-014-generacion-artefactos-despliegue-desde-contrato-settings.md)).
|
|
69
|
+
- **Entornos coherentes**: los overlays sobre una base común evitan duplicar valores idénticos y hacen explícitas las diferencias.
|
|
70
|
+
|
|
71
|
+
### Negativas
|
|
72
|
+
|
|
73
|
+
- **El contrato crece**: con el tiempo, el archivo central acumula todos los settings de la app y se vuelve extenso; la taxonomía de tipos es la mitigación para navegarlo.
|
|
74
|
+
- **Riesgo de duplicación tipo/schema**: en ecosistemas con tipos fuertes, la definición puede existir dos veces (schema de validación y tipos del lenguaje); requiere disciplina para mantenerlos sincronizados.
|
package/docs/adr/foundational/A-014-generacion-artefactos-despliegue-desde-contrato-settings.md
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# A-014: Generación de artefactos de despliegue desde el contrato de settings
|
|
2
|
+
|
|
3
|
+
**Estado**: Aceptado
|
|
4
|
+
|
|
5
|
+
**Fecha**: 2026-08-12
|
|
6
|
+
|
|
7
|
+
**originally**: cortex-002
|
|
8
|
+
|
|
9
|
+
**Enriquecido desde**: Cortex ADR 002 — Settings-to-Helm contract generation
|
|
10
|
+
|
|
11
|
+
**tags**: despliegue, configuracion
|
|
12
|
+
|
|
13
|
+
## Contexto
|
|
14
|
+
|
|
15
|
+
Mantener a mano los artefactos de despliegue (valores de ejemplo, config maps, secretos y su esquema de validación) es propenso a errores. Un setting agregado al código pero olvidado en el chart produce una configuración errónea silenciosa en producción: la app arranca con un valor por defecto o falla en runtime, en lugar de fallar donde corresponde.
|
|
16
|
+
|
|
17
|
+
El contrato de settings ([A-013](./A-013-contrato-settings-unica-fuente-configuracion.md)) ya define qué configuración existe, con su tipo y su origen. Si ese contrato es la única fuente de verdad, los artefactos de despliegue deberían **derivarse** de él, no mantenerse como una copia paralela.
|
|
18
|
+
|
|
19
|
+
## Decisión
|
|
20
|
+
|
|
21
|
+
Generar **todos los artefactos de despliegue desde el contrato de settings**, mediante un comando de exportación que lee el contrato y renderiza plantillas.
|
|
22
|
+
|
|
23
|
+
### 1. Exportación automática, no edición manual
|
|
24
|
+
|
|
25
|
+
Un único comando de exportación lee el contrato de settings y genera, por cada aplicación:
|
|
26
|
+
|
|
27
|
+
- Muestra de entorno para desarrollo local.
|
|
28
|
+
- Plantilla de secretos para el despliegue.
|
|
29
|
+
- Plantilla de config map para el despliegue.
|
|
30
|
+
- Valores de ejemplo para el chart de Helm.
|
|
31
|
+
- Esquema de validación de valores para el chart.
|
|
32
|
+
|
|
33
|
+
No se editan a mano estos archivos generados. Un cambio de configuración se hace en el contrato y se propaga ejecutando el comando de exportación.
|
|
34
|
+
|
|
35
|
+
### 2. La clasificación del contrato decide el destino
|
|
36
|
+
|
|
37
|
+
El metadato de tipo/origen de cada campo ([A-013 §2](./A-013-contrato-settings-unica-fuente-configuracion.md)) determina a qué artefacto pertenece:
|
|
38
|
+
|
|
39
|
+
- Un campo marcado como **secreto** se renderiza en la plantilla de secretos, nunca en el config map.
|
|
40
|
+
- Un campo marcado como **clúster** se traduce a la forma que el orquestador espera (por ejemplo, un objeto con `kind`, `name` y `key` que referencia un recurso del clúster).
|
|
41
|
+
- Un campo de **infraestructura** o **entorno** no secreto se renderiza en el config map.
|
|
42
|
+
- Los **metadatos** alimentan la muestra de entorno y la documentación.
|
|
43
|
+
|
|
44
|
+
### 3. Esquema de validación generado
|
|
45
|
+
|
|
46
|
+
El esquema de valores del chart se genera desde el contrato, con los campos requeridos correctos y las formas de los objetos de clúster. El orquestador puede validar los valores antes de instalar, rechazando configuraciones que el contrato no contempla.
|
|
47
|
+
|
|
48
|
+
### 4. Excepción acotada: valores fuera del contrato
|
|
49
|
+
|
|
50
|
+
Los valores del chart que no corresponden a ningún campo del contrato (por ejemplo, recursos de cómputo puros o topología de despliegue) se mantienen a mano en el values. Esta excepción es acotada y deliberada: el contrato describe configuración de la aplicación; el chart también necesita valores que no son configuración de la aplicación.
|
|
51
|
+
|
|
52
|
+
### 5. El chart consume los artefactos generados
|
|
53
|
+
|
|
54
|
+
El chart de Helm ([A-012](./A-012-helm-mecanismo-primario-despliegue.md)) consume los artefactos generados en lugar de redefinir variables de entorno o config maps. La cadena completa es:
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
contrato de settings → comando de exportación → artefactos de despliegue → chart de Helm
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Un setting nuevo recorre la cadena completa sin intervención manual en el chart.
|
|
61
|
+
|
|
62
|
+
## Consecuencias
|
|
63
|
+
|
|
64
|
+
### Positivas
|
|
65
|
+
|
|
66
|
+
- **Cero drift entre código y chart**: agregar un setting al contrato y ejecutar la exportación actualiza todos los artefactos derivados.
|
|
67
|
+
- **Secretos a salvo por construcción**: un campo marcado como secreto no puede terminar en un config map porque la clasificación lo excluye del renderizado correspondiente.
|
|
68
|
+
- **Validación previa a la instalación**: el esquema generado permite que el orquestador rechace valores inválidos antes de desplegar.
|
|
69
|
+
- **Bootstrap rápido de apps nuevas**: una app nueva copia el patrón de contrato + plantillas y obtiene su chart generado desde el primer momento.
|
|
70
|
+
|
|
71
|
+
### Negativas
|
|
72
|
+
|
|
73
|
+
- **Paso de exportación obligatorio**: olvidar ejecutar la exportación tras cambiar un setting deja los artefactos desactualizados hasta el siguiente despliegue.
|
|
74
|
+
- **Depuración de la generación opaca**: el resultado renderizado depende de la combinación de metadatos del contrato y lógica de plantillas; un artefacto incorrecto puede requerir inspeccionar ambos.
|
|
75
|
+
- **Valores custom fuera del flujo**: lo que no mapea a un campo del contrato sigue manteniéndose a mano en el values, con su propio riesgo de drift.
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# A-015: CloudEvents como estándar de eventos
|
|
2
|
+
|
|
3
|
+
**Estado**: Aceptado
|
|
4
|
+
|
|
5
|
+
**Fecha**: 2026-08-12
|
|
6
|
+
|
|
7
|
+
**originally**: cloudevents
|
|
8
|
+
|
|
9
|
+
**Enriquecido desde**: CloudEvents Specification (CNCF) — cloudevents.io
|
|
10
|
+
|
|
11
|
+
**tags**: eventos
|
|
12
|
+
|
|
13
|
+
## Contexto
|
|
14
|
+
|
|
15
|
+
Los eventos son el medio por el que las distintas partes de un sistema se comunican: los de dominio, cuya emisión fija [A-010](./A-010-propiedad-emision-eventos-dominio.md), y los de integración que los adaptadores intercambian con sistemas externos. Sin una decisión canónica, cada productor y cada consumidor reinventa su propia envolvente, los eventos quedan legibles solo para quien los escribió y cada integración nueva exige un contrato ad-hoc:
|
|
16
|
+
|
|
17
|
+
- Campos con nombres distintos para lo mismo (`occurredAt` vs `fecha` vs `timestamp`).
|
|
18
|
+
- Falta de metadatos de enrutamiento (¿quién emitió esto? ¿de qué tipo es?).
|
|
19
|
+
- Imposibilidad de que la infraestructura (transporte, observabilidad, proyecciones) procese eventos sin conocer la data específica de cada dominio.
|
|
20
|
+
- Evolución de la data sin reglas: los consumidores se rompen silenciosamente cuando un productor cambia un campo.
|
|
21
|
+
|
|
22
|
+
## Decisión
|
|
23
|
+
|
|
24
|
+
Adoptar **CloudEvents como estándar para todo evento** que el sistema emite o consume — de dominio y de integración. CloudEvents es una especificación del CNCF que define una envolvente común para describir eventos, independiente del lenguaje y del transporte.
|
|
25
|
+
|
|
26
|
+
### 1. CloudEvents como envolvente único
|
|
27
|
+
|
|
28
|
+
Todo evento se representa como un CloudEvent conforme a la especificación CloudEvents (versión `1.0`). La especificación separa los atributos de contexto (metadatos estándar) de la data de negocio: la infraestructura enruta y observa usando el contexto, y solo los consumidores que interpretan un tipo concreto leen la data.
|
|
29
|
+
|
|
30
|
+
### 2. Atributos de contexto
|
|
31
|
+
|
|
32
|
+
Cada evento declara los atributos obligatorios de la especificación:
|
|
33
|
+
|
|
34
|
+
- `id`: identificador único del evento.
|
|
35
|
+
- `source`: fuente que emitió el evento (una URI).
|
|
36
|
+
- `specversion`: versión de la especificación CloudEvents (`1.0`).
|
|
37
|
+
- `type`: tipo semántico del evento (ver punto 3).
|
|
38
|
+
|
|
39
|
+
Y usa los atributos opcionales cuando aportan información:
|
|
40
|
+
|
|
41
|
+
- `time`: instante en que ocurrió el evento.
|
|
42
|
+
- `subject`: entidad a la que se refiere el evento (por ejemplo, el identificador de un agregado).
|
|
43
|
+
- `datacontenttype`: formato de la data (por ejemplo, `application/json`).
|
|
44
|
+
- `dataschema`: URI del esquema versionado de la data (ver punto 4).
|
|
45
|
+
|
|
46
|
+
La data de negocio viaja en el campo `data`, tipada para los consumidores que la interpretan y opaca para el resto.
|
|
47
|
+
|
|
48
|
+
### 3. `type` identifica la semántica y la familia de compatibilidad
|
|
49
|
+
|
|
50
|
+
El atributo `type` es un identificador estable y legible de la semántica del evento que codifica la familia de compatibilidad de la data: su versión mayor. Por ejemplo, `com.imolko.journal.v1.contact-created` marca la familia `1.x.x`, así que todos los eventos de ese `type` son compatibles dentro de `1.x.x`. El `type` no incluye la versión completa (`mayor.menor.patch`): la versión concreta se expresa por separado (ver punto 4). Así, el enrutamiento y las proyecciones no se rompen cuando la data evoluciona de forma compatible dentro de una familia, y un cambio incompatible produce un `type` nuevo (ver punto 4).
|
|
51
|
+
|
|
52
|
+
### 4. Versionado semver, append-only y compatible hacia atrás de la data
|
|
53
|
+
|
|
54
|
+
La data de cada tipo de evento se versiona con semver (`mayor.menor.patch`) y su historial de versiones es **append-only**:
|
|
55
|
+
|
|
56
|
+
- **Append-only**: una versión publicada nunca se modifica ni se elimina. Un cambio produce una entrada nueva en el historial, agregada al final; las versiones anteriores permanecen intactas y legibles para siempre.
|
|
57
|
+
- **Compatible hacia atrás**: dentro de una misma versión mayor, los cambios son aditivos — se agregan campos opcionales o se relajan validaciones. Nunca se renombra ni se elimina un campo requerido, ni se cambia su significado. Un consumidor viejo sigue leyendo eventos nuevos sin romperse.
|
|
58
|
+
- **Cambios incompatibles**: un cambio que rompe compatibilidad (eliminar o renombrar un campo requerido, cambiar el significado de un campo) no se publica como una versión nueva de la misma data. Se trata como un contrato nuevo — un `type` nuevo — con su propio ciclo de versionado y, si aplica, migración explícita.
|
|
59
|
+
|
|
60
|
+
La versión concreta de la data de un evento se declara de forma explícita: en el `dataschema` (URI versionada del esquema) y, cuando el ecosistema lo requiera, en un campo de versión dentro de la propia data. Los consumidores leen esa versión y aplican la regla de compatibilidad para interpretar la data.
|
|
61
|
+
|
|
62
|
+
### 5. Consumidores tolerantes a atributos desconocidos
|
|
63
|
+
|
|
64
|
+
Los consumidores ignoran los atributos de contexto y de data que no conocen, en lugar de fallar. CloudEvents permite atributos de extensión; un consumidor que rechaza eventos con atributos nuevos se convierte en un cuello de botella para la evolución del contrato. La tolerancia aplica tanto a atributos de contexto adicionales como a campos nuevos de la data (que son opcionales por la regla del punto 4).
|
|
65
|
+
|
|
66
|
+
### 6. Ámbito agnóstico al lenguaje
|
|
67
|
+
|
|
68
|
+
La decisión fija el formato y la disciplina de versionado, no una librería concreta. Cualquier proyecto de Imolko — hoy TypeScript, mañana otro lenguaje — emite y consume CloudEvents y aplica las mismas reglas de versionado. Lo que se comparte entre proyectos es el estándar y el contrato de versionado; la implementación de cada ecosistema es un detalle.
|
|
69
|
+
|
|
70
|
+
## Consecuencias
|
|
71
|
+
|
|
72
|
+
### Positivas
|
|
73
|
+
|
|
74
|
+
- **Interoperabilidad**: la infraestructura común (transporte, observabilidad, enrutamiento) procesa cualquier evento sin conocer su data.
|
|
75
|
+
- **Contratos estables**: la data evoluciona de forma aditiva y los consumidores viejos siguen funcionando con eventos nuevos.
|
|
76
|
+
- **Historial inmutable**: las versiones publicadas nunca cambian, por lo que reproducir o auditar un evento viejo siempre es posible.
|
|
77
|
+
- **Descubrimiento**: el `type` y el `dataschema` permiten localizar la definición y el esquema de un evento sin leer código del productor.
|
|
78
|
+
- **Desacoplamiento del lenguaje**: la decisión aplica igual a TypeScript, Python o cualquier ecosistema futuro.
|
|
79
|
+
|
|
80
|
+
### Negativas
|
|
81
|
+
|
|
82
|
+
- **Disciplina de versionado**: agregar un campo requiere pensar en compatibilidad y registrar una versión nueva, en lugar de editar libremente la data.
|
|
83
|
+
- **Curva de aprendizaje**: el equipo debe conocer los atributos de contexto de CloudEvents y las reglas semver aplicadas a eventos.
|
|
84
|
+
- **Romper compatibilidad es costoso**: un cambio incompatible no se arregla con una versión más; exige un `type` nuevo y, si aplica, una migración.
|
|
85
|
+
- **Manejo de atributos desconocidos**: la tolerancia del punto 5 es una responsabilidad de cada consumidor; un consumidor que la omita reintroduce el acoplamiento que el estándar busca evitar.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# A-016: CI/CD con GitLab: pipeline raíz y pipelines modulares por app
|
|
2
|
+
|
|
3
|
+
**Estado**: Aceptado
|
|
4
|
+
|
|
5
|
+
**Fecha**: 2026-08-12
|
|
6
|
+
|
|
7
|
+
**originally**: generator-019
|
|
8
|
+
|
|
9
|
+
**Enriquecido desde**: Generator ADR 019 — CI/CD Store Generator Pipeline; Cortex docs — GitLab CI/CD parent-child pipelines
|
|
10
|
+
|
|
11
|
+
**tags**: despliegue, tooling
|
|
12
|
+
|
|
13
|
+
## Contexto
|
|
14
|
+
|
|
15
|
+
Todo repositorio de Imolko necesita verificar sus cambios de forma automática — ejecutar tests, compilar, validar requisitos y publicar artefactos — y desplegar las aplicaciones que lo componen. Sin una plataforma y una estructura de pipeline canónicas, cada repositorio reinventa su proceso:
|
|
16
|
+
|
|
17
|
+
- Pipelines con nombres y stages distintos entre proyectos.
|
|
18
|
+
- Validaciones globales mezcladas con los pasos de despliegue de una app concreta.
|
|
19
|
+
- Todas las apps se re-ejecutan en cada cambio, aunque el cambio no las afecte.
|
|
20
|
+
- Un agente o un miembro nuevo no puede descubrir cómo se valida y despliega un proyecto sin leer pipeline a pipeline.
|
|
21
|
+
|
|
22
|
+
## Decisión
|
|
23
|
+
|
|
24
|
+
Adoptar **GitLab CI/CD como plataforma canónica de integración y despliegue continuo** para todos los proyectos de Imolko, con una estructura de **pipeline raíz + pipelines modulares por app**.
|
|
25
|
+
|
|
26
|
+
### 1. GitLab como plataforma canónica
|
|
27
|
+
|
|
28
|
+
Todo pipeline de CI/CD se define como un pipeline de GitLab. No se mantiene una segunda plataforma de CI/CD como mecanismo primario. La definición vive en archivos de pipeline versionados junto al código, de modo que la forma de validar y desplegar se descubre leyendo el repositorio.
|
|
29
|
+
|
|
30
|
+
### 2. Pipeline raíz: validaciones globales y orquestación
|
|
31
|
+
|
|
32
|
+
Cada repositorio tiene exactamente un **pipeline raíz** en su raíz. Su responsabilidad es el trabajo que concierne al repositorio como un todo, no el despliegue de una app concreta:
|
|
33
|
+
|
|
34
|
+
- **Reglas de workflow**: cuándo se ejecuta un pipeline y con qué intención (ver punto 4).
|
|
35
|
+
- **Validación de requisitos globales**: variables y condiciones previas que el pipeline necesita.
|
|
36
|
+
- **Trabajo compartido**: tests y builds de las capas comunes (el dominio y los casos de uso, según [A-001](./A-001-clean-architecture-4-capas.md)) y generación de documentación.
|
|
37
|
+
- **Reporte de tests unificado**: los jobs de test exportan sus resultados en formato CTRF (ver [A-022](./A-022-formato-reporte-tests-ctrf.md)); el pipeline y la documentación generada consumen ese reporte en lugar de los formatos propios de cada runner.
|
|
38
|
+
- **Orquestación de apps**: un job trigger por cada aplicación, que dispara el pipeline modular de esa app solo cuando corresponde (ver punto 3).
|
|
39
|
+
|
|
40
|
+
El pipeline raíz no contiene los pasos de build o despliegue de ninguna aplicación: ese trabajo pertenece al pipeline de la app.
|
|
41
|
+
|
|
42
|
+
### 3. Pipeline modular por app con disparo por cambios
|
|
43
|
+
|
|
44
|
+
Cada aplicación desplegable ([A-001](./A-001-clean-architecture-4-capas.md)) posee su **propio pipeline**, definido en el directorio de la app. El pipeline raíz lo dispara mediante **parent-child pipelines**, y el trigger usa reglas de **cambios** (`rules:changes`): el pipeline de una app se ejecuta únicamente cuando cambian los archivos que la afectan. Un cambio que no toca a una app no la recompila ni la redespliega.
|
|
45
|
+
|
|
46
|
+
El pipeline de la app es dueño del ciclo específico de esa app:
|
|
47
|
+
|
|
48
|
+
- Verificar los archivos requeridos para desplegar.
|
|
49
|
+
- Construir y publicar la imagen de contenedor.
|
|
50
|
+
- Desplegar con Helm ([A-012](./A-012-helm-mecanismo-primario-despliegue.md)), consumiendo los artefactos generados desde el contrato de settings ([A-014](./A-014-generacion-artefactos-despliegue-desde-contrato-settings.md)).
|
|
51
|
+
- Verificar el despliegue.
|
|
52
|
+
|
|
53
|
+
### 4. Intención del pipeline declarada en la raíz
|
|
54
|
+
|
|
55
|
+
El pipeline raíz declara la **intención** de cada ejecución en un único lugar, con dos valores:
|
|
56
|
+
|
|
57
|
+
- **Integración**: verificar cambios antes de fusionar (tests y builds, sin despliegue).
|
|
58
|
+
- **Entrega** (delivery): desplegar tras fusionar a una rama protegida.
|
|
59
|
+
|
|
60
|
+
La intención se decide en las reglas de workflow del raíz y se transmite a los pipelines de las apps. Los jobs de test se ejecutan en integración y entrega; los jobs de despliegue solo en entrega. Así, la semántica de "qué hace este pipeline" no se deduce de reglas dispersas.
|
|
61
|
+
|
|
62
|
+
### 5. Contrato explícito entre raíz y apps mediante inputs
|
|
63
|
+
|
|
64
|
+
Cada pipeline de app declara los parámetros que necesita (por ejemplo, nombre de la app, directorio, entorno a desplegar, intención) como **inputs** con su tipo y descripción. El pipeline raíz se los pasa al disparar el trigger. Los pipelines de app no leen variables globales sueltas: reciben un contrato explícito que documenta qué necesitan y quién se lo provee.
|
|
65
|
+
|
|
66
|
+
### 6. Convenciones operativas
|
|
67
|
+
|
|
68
|
+
Además de la estructura de dos niveles, los pipelines comparten convenciones operativas canónicas:
|
|
69
|
+
|
|
70
|
+
- **Helpers de CI versionados**: los pipelines usan el módulo de utilidades versionado en el repositorio (`ci-helpers.sh` en la raíz) en lugar de scripts externos no versionados. Ese archivo es la fuente canónica de las funciones disponibles —las familias de logging (`log_*`), las secciones colapsables (`section_start`, `section_end`) y las validaciones de requisitos (`validate_required_*`)—; los pipelines hacen `source` del archivo y consultan su documentación para conocer la lista completa, sin duplicarla en cada pipeline.
|
|
71
|
+
- **Naming `stage:job`**: los nombres de stage y de job siguen una convención explícita y uniforme, de modo que el propósito de un job se deduce de su nombre. La convención concreta vive en la fuente canónica de cada repositorio; este ADR fija la obligación de mantenerla, no duplica la tabla de nombres.
|
|
72
|
+
- **Versionado de imágenes con CalVer**: las imágenes de contenedor se versionan con **CalVer** (p. ej. `YYYY.MM.patch`), no con SemVer. El canal de desarrollo usa el sufijo `-beta` en la rama `develop`. Los **paquetes** (librerías publicables) siguen **SemVer**; CalVer aplica únicamente a imágenes desplegables. Esta distinción evita mezclar el ciclo de un artefacto desplegable con el de un artefacto consumible.
|
|
73
|
+
- **Despliegue Helm con espera y timeout**: el pipeline despliega con `helm upgrade --wait` y un `--timeout` explícito, de modo que Helm espera a que los pods reporten `ready` (coherente con el `readinessProbe` de [A-019](./A-019-convenciones-autoria-helm-charts.md)) antes de dar el despliegue por exitoso, en lugar de regresar de inmediato.
|
|
74
|
+
- **Sin scripts externos no versionados**: todo paso del pipeline ejecuta comandos en línea o scripts versionados en el repositorio. No se depende de scripts alojados fuera del repositorio ni de pasos que un miembro nuevo no pueda inspeccionar.
|
|
75
|
+
|
|
76
|
+
## Consecuencias
|
|
77
|
+
|
|
78
|
+
### Positivas
|
|
79
|
+
|
|
80
|
+
- **Modularidad**: cada app evoluciona su pipeline sin tocar el raíz, y el raíz cambia sin reescribir los pipelines de las apps.
|
|
81
|
+
- **Ejecución selectiva**: solo se ejecutan las apps afectadas por un cambio, lo que reduce tiempo y recursos.
|
|
82
|
+
- **Descubribilidad**: la estructura de dos niveles es predecible; un agente o un miembro nuevo encuentra el pipeline raíz y el de cada app sin ambigüedad.
|
|
83
|
+
- **Intención única**: integración versus entrega se define en un solo sitio, sin reglas contradictorias entre jobs.
|
|
84
|
+
- **Contrato claro**: los inputs de cada app documentan sus requisitos de pipeline.
|
|
85
|
+
|
|
86
|
+
### Negativas
|
|
87
|
+
|
|
88
|
+
- **Complejidad de dos niveles**: depurar parent-child pipelines es más difícil que un pipeline único.
|
|
89
|
+
- **Mantenimiento por app**: cada app mantiene su archivo de pipeline; una app nueva debe crear el suyo.
|
|
90
|
+
- **Reglas de cambios frágiles**: si `rules:changes` no lista todos los paths que afectan a una app, el pipeline puede saltarse un cambio necesario.
|
|
91
|
+
- **Boilerplate de inputs**: declarar y pasar inputs agrega verbosidad frente a usar variables globales.
|
|
92
|
+
- **Disciplina**: mantener el raíz libre de pasos específicos de app requiere revisión constante.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# A-017: Taxonomía de repositorios: sistemas, librerías y configuración
|
|
2
|
+
|
|
3
|
+
**Estado**: Aceptado
|
|
4
|
+
|
|
5
|
+
**Fecha**: 2026-08-12
|
|
6
|
+
|
|
7
|
+
**originally**: imolko
|
|
8
|
+
|
|
9
|
+
**tags**: arquitectura
|
|
10
|
+
|
|
11
|
+
## Contexto
|
|
12
|
+
|
|
13
|
+
Imolko mantiene varios repositorios de naturaleza distinta. Algunos encapsulan una funcionalidad técnica reutilizable (validación, resultados, generación, documentación); otros modelan un dominio de negocio completo con sus reglas y sus aplicaciones desplegables; otros guardan artefactos de despliegue y configuración. Sin una taxonomía explícita:
|
|
14
|
+
|
|
15
|
+
- No queda claro qué estándares aplican a cada repositorio.
|
|
16
|
+
- Se corre el riesgo de imponer estructura de negocio a una utilidad, o de tratar un producto como si fuera una pieza reutilizable.
|
|
17
|
+
- Un miembro nuevo o un agente no puede deducir el propósito de un repositorio a partir de una convención escrita.
|
|
18
|
+
|
|
19
|
+
Las prácticas del equipo se aplican de forma distinta según la naturaleza del repositorio. Fijar la taxonomía permite saber, para cada repositorio, qué reglas aplican.
|
|
20
|
+
|
|
21
|
+
## Decisión
|
|
22
|
+
|
|
23
|
+
Clasificar todo repositorio de Imolko en exactamente una de tres categorías: **sistema**, **librería** o **configuración**. La clasificación depende del propósito del repositorio — un dominio de negocio, una funcionalidad técnica reutilizable, o artefactos de despliegue y configuración — no del tamaño, del lenguaje ni de la forma de despliegue.
|
|
24
|
+
|
|
25
|
+
### 1. Librería
|
|
26
|
+
|
|
27
|
+
Una **librería** es un repositorio que provee **funcionalidad específica y reutilizable**, sin un dominio de negocio propio. La «funcionalidad» incluye metodología y documentación reutilizables —por ejemplo skills de agentes y un registro ADR de metodología, como `@imolko/ultra-agent`—, no solo código ejecutable.
|
|
28
|
+
|
|
29
|
+
Características:
|
|
30
|
+
|
|
31
|
+
- **Sin lenguaje ubicuo**: su vocabulario es técnico, no de negocio.
|
|
32
|
+
- **Sin Clean Architecture**: no se organiza en capas concéntricas de negocio. Es una pieza cohesionada — típicamente uno o unos pocos paquetes — que otros repositorios consumen como dependencia.
|
|
33
|
+
- **Problema acotado**: resuelve un problema bien delimitado, no un proceso de negocio completo.
|
|
34
|
+
|
|
35
|
+
Ejemplos abstractos: una librería de validación, una librería de resultados, una herramienta de documentación.
|
|
36
|
+
|
|
37
|
+
### 2. Sistema
|
|
38
|
+
|
|
39
|
+
Un **sistema** es un repositorio que modela un **dominio de negocio**.
|
|
40
|
+
|
|
41
|
+
Características:
|
|
42
|
+
|
|
43
|
+
- **Con lenguaje ubicuo**: los artefactos y los nombres hablan el lenguaje del negocio.
|
|
44
|
+
- **Clean Architecture**: se organiza en capas concéntricas de negocio.
|
|
45
|
+
- **Aplicaciones desplegables**: posee apps que componen casos de uso y se despliegan de forma independiente.
|
|
46
|
+
|
|
47
|
+
Ejemplos abstractos: un sistema de inventario, un punto de venta, una plataforma de compras.
|
|
48
|
+
|
|
49
|
+
### 3. Configuración
|
|
50
|
+
|
|
51
|
+
Un repositorio de **configuración** guarda artefactos de despliegue y configuración (settings, Helm charts, pipelines, entornos). No modela un dominio de negocio ni provee funcionalidad reutilizable.
|
|
52
|
+
|
|
53
|
+
Características:
|
|
54
|
+
|
|
55
|
+
- **Sin dominio de negocio**: no tiene capa `domain/` ni lenguaje ubicuo.
|
|
56
|
+
- **Sin lógica reutilizable**: no se consume como dependencia; lo consumen las herramientas de despliegue y el entorno.
|
|
57
|
+
- **Referencia a lo que configura**: un chart o un contrato de settings apunta al sistema (o librería) que despliega o configura.
|
|
58
|
+
|
|
59
|
+
Ejemplos abstractos: un repositorio de Helm charts, un contrato de settings, pipelines de CI/CD.
|
|
60
|
+
|
|
61
|
+
### 4. Regla de dependencia entre categorías
|
|
62
|
+
|
|
63
|
+
Una **librería nunca depende de un sistema**. Los sistemas pueden depender de librerías, nunca al revés. Esta dirección mantiene las piezas reutilizables independientes de cualquier negocio concreto.
|
|
64
|
+
|
|
65
|
+
La **configuración** no participa del grafo de dependencias de código: referencia los sistemas y librerías que despliega o configura, y ningún repositorio la importa como dependencia.
|
|
66
|
+
|
|
67
|
+
### 5. La taxonomía decide qué estándares aplican
|
|
68
|
+
|
|
69
|
+
La clasificación determina las expectativas estructurales:
|
|
70
|
+
|
|
71
|
+
- Un **sistema** debe exhibir lenguaje ubicuo ([A-031](./A-031-ubiquitous-language.md)) y Clean Architecture ([A-001](./A-001-clean-architecture-4-capas.md)); es candidato a un dominio DDD completo con sus bounded contexts ([A-008](./A-008-bounded-context-decomposition.md)).
|
|
72
|
+
- Una **librería** no debe cargar con capas de negocio; se mantiene como una pieza técnica cohesionada. Imponerle Clean Architecture es sobre-ingeniería.
|
|
73
|
+
- Un repositorio de **configuración** no aplica DDD ni Clean Architecture; sigue los estándares de despliegue y configuración (settings [A-013](./A-013-contrato-settings-unica-fuente-configuracion.md), Helm [A-019](./A-019-convenciones-autoria-helm-charts.md), CI/CD [A-016](./A-016-cicd-gitlab-pipeline-raiz-pipelines-modulares.md)).
|
|
74
|
+
|
|
75
|
+
### 6. Clasificación por naturaleza dominante
|
|
76
|
+
|
|
77
|
+
Un repositorio se clasifica por su naturaleza dominante. Si un repositorio adquiere con el tiempo la naturaleza de otra categoría, se reclasifica — y esa reclasificación es una decisión que puede implicar migración estructural.
|
|
78
|
+
|
|
79
|
+
## Consecuencias
|
|
80
|
+
|
|
81
|
+
### Positivas
|
|
82
|
+
|
|
83
|
+
- **Clasificación inmediata**: la taxonomía responde "¿es un sistema, una librería o configuración?" sin ambigüedad.
|
|
84
|
+
- **Estándares correctos por repositorio**: un sistema exige DDD y Clean Architecture; una librería no arrastra esa ceremonia; la configuración sigue los estándares de despliegue, sin DDD ni Clean Architecture.
|
|
85
|
+
- **Dirección de dependencias sana**: las librerías permanecen libres de negocio y reutilizables entre sistemas.
|
|
86
|
+
- **Descubribilidad**: un agente o un miembro nuevo sabe qué buscar y qué reglas aplicar al entrar a un repositorio.
|
|
87
|
+
- **Distribución de librerías natural**: las librerías quedan identificadas como candidatas a publicación y versionado independientes.
|
|
88
|
+
|
|
89
|
+
### Negativas
|
|
90
|
+
|
|
91
|
+
- **Solapamiento parcial**: un sistema puede alojar sus charts y settings en su propio repositorio; la categoría la decide la naturaleza dominante, lo que admite cierto criterio.
|
|
92
|
+
- **Reclasificación costosa**: una librería que crece hasta tener dominio debe migrar a la estructura de sistema.
|
|
93
|
+
- **Riesgo de sobre-ingeniería inversa**: aplicar la taxonomía a la inversa (forzar DDD en una librería, o aplanar un sistema) degrada el diseño.
|