@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,39 @@
|
|
|
1
|
+
# T-017: Golden files para characterization tests
|
|
2
|
+
|
|
3
|
+
**Estado**: Aceptado
|
|
4
|
+
|
|
5
|
+
**Fecha**: 2026-09-06
|
|
6
|
+
|
|
7
|
+
**originally**: imolko
|
|
8
|
+
|
|
9
|
+
## Contexto
|
|
10
|
+
|
|
11
|
+
Los tests de caracterización capturan el comportamiento actual de código legacy antes de refactorizarlo y producen una salida esperada que debe conservarse en el repositorio. El *cuándo* y *por qué* de esa práctica es una decisión fundacional (ver [A-011](../foundational/A-011-characterization-first-refactoring.md)); el *cómo* guardar la salida esperada en el ecosistema TypeScript necesita una decisión propia.
|
|
12
|
+
|
|
13
|
+
Las anotaciones JSDoc de [T-002](./T-002-test-documentation-jsdoc.md) documentan *qué* verifica cada test para la documentación generada; *dónde* se guarda la salida esperada de un characterization test es un eje distinto, y por eso se decide por separado.
|
|
14
|
+
|
|
15
|
+
## Decisión
|
|
16
|
+
|
|
17
|
+
En TypeScript, la salida esperada de los characterization tests se guarda en **golden files**: archivos de referencia en disco, legibles por humanos, almacenados en `__fixtures__/expected/`.
|
|
18
|
+
|
|
19
|
+
### Reglas
|
|
20
|
+
|
|
21
|
+
| Regla | Descripción |
|
|
22
|
+
|-------|-------------|
|
|
23
|
+
| Golden files en disco | La salida esperada vive en `__fixtures__/expected/`, junto al proyecto fixture. El golden file contiene la salida en texto plano; `git diff` muestra exactamente qué cambió. |
|
|
24
|
+
| Comparación normalizada | Cada test lee la salida generada, normaliza el contenido no determinista (rutas, timestamps) y la compara contra el golden file. Si hay diferencias, el test falla: el refactoring cambió el comportamiento. |
|
|
25
|
+
| Regeneración por flag | Los golden files se regeneran con un flag de entorno; el comando concreto vive en los scripts de `package.json`. |
|
|
26
|
+
| Snapshots para funciones puras | Para funciones puras con salidas pequeñas se prefieren los snapshots inline de Jest; los golden files se reservan para salidas complejas (documentación, reportes, archivos). |
|
|
27
|
+
|
|
28
|
+
## Consecuencias
|
|
29
|
+
|
|
30
|
+
### Positivas
|
|
31
|
+
|
|
32
|
+
- **Red de seguridad legible**: los golden files se inspeccionan en cualquier editor y `git diff` muestra exactamente qué cambió.
|
|
33
|
+
- **Regeneración reproducible**: un único flag de entorno regenera todos los golden files, sin scripts adicionales.
|
|
34
|
+
- **Un mecanismo por tipo de salida**: golden files para salidas complejas, snapshots inline para funciones puras.
|
|
35
|
+
|
|
36
|
+
### Negativas
|
|
37
|
+
|
|
38
|
+
- **Normalización a mantener**: los patrones no deterministas (rutas, timestamps) deben normalizarse antes de comparar, y la función de normalización crece con cada patrón nuevo.
|
|
39
|
+
- **Golden files grandes**: los archivos generados pueden ser grandes y deben versionarse en el repositorio.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# T-018: Procedencia de los campos del frontmatter del README
|
|
2
|
+
|
|
3
|
+
**Estado**: Aceptado
|
|
4
|
+
|
|
5
|
+
**Fecha**: 2026-09-07
|
|
6
|
+
|
|
7
|
+
**originally**: imolko
|
|
8
|
+
|
|
9
|
+
## Contexto
|
|
10
|
+
|
|
11
|
+
El frontmatter del `README.md` de cada artefacto de dominio y feature sigue el esquema canónico de [A-032](../foundational/A-032-domain-artifact-documentation-format.md). En el ecosistema TypeScript, cada campo tiene una fuente de autoría concreta y un consumidor: ultra-reporter ([T-011](./T-011-ultra-reporter-herramienta-canonica-documentacion.md)) lee `description`, `title`, `boundedContext` y `tags` del frontmatter del `README.md`, y determina el `type` por el archivo marcador del artefacto. Los decoradores `@Register*` alimentan el registro de ultra-ddd (`DomainDirectory`), no el pipeline de documentación.
|
|
12
|
+
|
|
13
|
+
[T-003](./T-003-readme-formatting-domain-artifacts.md) fija la mecánica de reconciliar el scaffold de `ultra-generator` ([T-019](./T-019-ultra-generator-herramienta-canonica-generacion.md)) con el formato de A-032. La procedencia de cada campo del frontmatter — de qué fuente sale y quién lo consume — es un eje distinto, y es el que cubre este ADR.
|
|
14
|
+
|
|
15
|
+
## Decisión
|
|
16
|
+
|
|
17
|
+
Cada campo del frontmatter del `README.md` se deriva de una fuente específica del ecosistema TypeScript.
|
|
18
|
+
|
|
19
|
+
**Artefactos de dominio:**
|
|
20
|
+
|
|
21
|
+
| Campo | Fuente |
|
|
22
|
+
|-------|--------|
|
|
23
|
+
| `description` | Decorador del artefacto (`@Register*`) — expandido con detalle del dominio |
|
|
24
|
+
| `domain` | `ultra-reporter.config.json` → `domain.name` (nombre del proyecto) |
|
|
25
|
+
| `boundedContext` | El bounded context donde reside el artefacto ([A-008](../foundational/A-008-bounded-context-decomposition.md)); ultra-reporter lo usa para la columna Context (agrupación y fallback: [T-011](./T-011-ultra-reporter-herramienta-canonica-documentacion.md)) |
|
|
26
|
+
| `type` | El archivo marcador del artefacto: `aggregate.ts` → `aggregate`, `entity.ts` → `entity`, `value-object.ts` → `value-object`, `event.ts` → `domain-event`, `service.ts` → `service`; los enums se detectan por `enum.ts` → `enum` |
|
|
27
|
+
| `tags` | `completed` para artefactos implementados |
|
|
28
|
+
|
|
29
|
+
**Features (casos de uso):**
|
|
30
|
+
|
|
31
|
+
| Campo | Fuente |
|
|
32
|
+
|-------|--------|
|
|
33
|
+
| `feature` | Nombre del directorio del feature en kebab-case |
|
|
34
|
+
| `type` | `command` o `query` ([A-002](../foundational/A-002-cqrs-semantico.md)) |
|
|
35
|
+
| `title` | Redactado por el equipo: el título legible del caso de uso |
|
|
36
|
+
| `description` | Redactado por el equipo: la descripción corta del caso de uso |
|
|
37
|
+
| `tags` | `completed` o `pending`, más un `scope::*` (taxonomía de [A-009](../foundational/A-009-use-case-taxonomy.md)) |
|
|
38
|
+
|
|
39
|
+
### Enmienda (2026-09-28): la identidad del caso de uso no viaja en el artefacto
|
|
40
|
+
|
|
41
|
+
La evaluación posterior contra las fuentes primarias confirmó que el registro de ultra-ddd (`DomainDirectory`) no tiene consumidores: la documentación descubre features escaneando carpetas ([T-011](./T-011-ultra-reporter-herramienta-canonica-documentacion.md)) y el catálogo ejecutable vive en el manifiesto de la app ([T-023](./T-023-registry-cli-declara-dependencias-reales.md)). La procedencia de los campos del frontmatter no cambia; se precisa así:
|
|
42
|
+
|
|
43
|
+
- La identidad del caso de uso sigue siendo el **nombre del directorio** en kebab-case (`feature`). No se introduce un `NAME` en el artefacto ni un decorador `@RegisterUseCase`: no tendrían consumidor.
|
|
44
|
+
- El `name` del manifiesto ([T-023](./T-023-registry-cli-declara-dependencias-reales.md)) es una clave de composición, no una fuente del frontmatter.
|
|
45
|
+
|
|
46
|
+
## Consecuencias
|
|
47
|
+
|
|
48
|
+
### Positivas
|
|
49
|
+
|
|
50
|
+
- **Una fuente por campo**: cada campo del frontmatter tiene una procedencia inequívoca, alineada con el esquema de A-032.
|
|
51
|
+
- **Fuente y consumidor separados**: ultra-reporter lee el frontmatter; los decoradores alimentan el registro de ultra-ddd; cada uno con su rol documentado.
|
|
52
|
+
|
|
53
|
+
### Negativas
|
|
54
|
+
|
|
55
|
+
- **Campos escritos a mano**: la coherencia entre decorador y frontmatter depende de que el agente los complete correctamente; no hay validación automática que los cruce.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# T-019: ultra-generator como herramienta canónica de generación de artefactos
|
|
2
|
+
|
|
3
|
+
**Estado**: Aceptado
|
|
4
|
+
|
|
5
|
+
**Fecha**: 2026-09-07
|
|
6
|
+
|
|
7
|
+
**originally**: imolko
|
|
8
|
+
|
|
9
|
+
## Contexto
|
|
10
|
+
|
|
11
|
+
El dominio y los casos de uso siguen convenciones estrictas de ultra-ddd: cada artefacto de dominio (los tipos que enumera [A-032](../foundational/A-032-domain-artifact-documentation-format.md)) y cada feature (command o query, [A-002](../foundational/A-002-cqrs-semantico.md)) tiene una anatomía fija de archivos por tipo. Escribir ese scaffold a mano es repetitivo y propenso a divergencias.
|
|
12
|
+
|
|
13
|
+
`ultra-generator` (paquete `@imolko/ultra-generator` y skill `/skill:ultra-generator`) ya produce ese scaffold compilable y testeable, y trabaja en tándem con ultra-reporter ([T-011](./T-011-ultra-reporter-herramienta-canonica-documentacion.md)), que documenta lo generado. El stack del dominio ([T-010](./T-010-stack-tooling-typescript-dominio.md)) excluye explícitamente al generador como pieza separada. Falta decidir que es la herramienta canónica de scaffolding.
|
|
14
|
+
|
|
15
|
+
## Decisión
|
|
16
|
+
|
|
17
|
+
Adoptar **ultra-generator** como la herramienta canónica de generación de artefactos del dominio y de features. Todo scaffold nuevo sale de ultra-generator; no se mantiene un generador alternativo ni se escriben artefactos a mano cuando existe un comando que los genera.
|
|
18
|
+
|
|
19
|
+
Los subcomandos del generador y qué produce cada uno los define `@imolko/ultra-generator`; este ADR no los transcribe. La invocación y la versión en uso viven en el `package.json` (script `ultra-generator`), la fuente canónica; este ADR no lista versiones. La invocación desde issues sigue [A-038](../foundational/A-038-issues-referencian-skills-nombre.md): el issue nombra el skill, no se invoca el generador implícitamente.
|
|
20
|
+
|
|
21
|
+
El scaffold es un **punto de partida, no el artefacto final**: después de generar, el agente lo reconcilia con el formato canónico (ver [T-003](./T-003-readme-formatting-domain-artifacts.md)) y completa el frontmatter del `README.md` (ver [T-018](./T-018-procedencia-campos-frontmatter.md)).
|
|
22
|
+
|
|
23
|
+
## Consecuencias
|
|
24
|
+
|
|
25
|
+
### Positivas
|
|
26
|
+
|
|
27
|
+
- **Un solo generador**: todo artefacto y feature nace del mismo scaffold, sin variantes escritas a mano.
|
|
28
|
+
- **Anatomía consistente**: el layout de archivos es uniforme y compatible con ultra-ddd y ultra-reporter.
|
|
29
|
+
- **Descubribilidad**: un agente o miembro nuevo sabe que todo scaffold sale de ultra-generator y que se invoca desde el script `ultra-generator` del `package.json`.
|
|
30
|
+
|
|
31
|
+
### Negativas
|
|
32
|
+
|
|
33
|
+
- **Scaffold no canónico**: el `README.md` del scaffold trae secciones en inglés y `Relaciones` en bullet points; requiere el paso de reconciliación de T-003.
|
|
34
|
+
- **Regeneración destructiva**: re-ejecutar ultra-generator puede regenerar `.instructions.md` y sobrescribir ajustes manuales (ver [T-003](./T-003-readme-formatting-domain-artifacts.md) y [T-009](./T-009-mdx-brace-escaping.md)).
|
|
35
|
+
- **Dependencia del ecosistema `@imolko`**: migrar fuera de ultra-generator implica reescribir el flujo de scaffolding.
|
package/docs/adr/typescript/T-020-ultra-settings-herramienta-canonica-exportacion-despliegue.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# T-020: ultra-settings como herramienta canónica de exportación de artefactos de despliegue
|
|
2
|
+
|
|
3
|
+
**Estado**: Aceptado
|
|
4
|
+
|
|
5
|
+
**Fecha**: 2026-09-08
|
|
6
|
+
|
|
7
|
+
**originally**: imolko
|
|
8
|
+
|
|
9
|
+
## Contexto
|
|
10
|
+
|
|
11
|
+
El contrato de settings como única fuente de configuración ([A-013](../foundational/A-013-contrato-settings-unica-fuente-configuracion.md)) declara qué settings existen y cómo se validan, y la generación de artefactos de despliegue desde ese contrato ([A-014](../foundational/A-014-generacion-artefactos-despliegue-desde-contrato-settings.md)) decide que esos artefactos se derivan del contrato en lugar de mantenerse a mano. El patrón es agnóstico a la tecnología de validación ([A-013 §5](../foundational/A-013-contrato-settings-unica-fuente-configuracion.md)); [A-014](../foundational/A-014-generacion-artefactos-despliegue-desde-contrato-settings.md) fija *qué* se exporta y *desde dónde*, pero no *con qué herramienta*.
|
|
12
|
+
|
|
13
|
+
En TypeScript el contrato ya se materializa como schema Zod ([T-016](./T-016-contrato-settings-zod.md)). Falta la pieza complementaria: el mecanismo que lee ese schema y produce los artefactos de despliegue. Sin una herramienta canónica, cada aplicación escribiría su propio script de exportación y reaparecería el drift entre contrato y artefactos que [A-014](../foundational/A-014-generacion-artefactos-despliegue-desde-contrato-settings.md) evita. `@imolko/ultra-settings` cubre ese hueco: lee el schema Zod del contrato y renderiza los artefactos de despliegue.
|
|
14
|
+
|
|
15
|
+
La exportación es una responsabilidad distinta de la del stack del dominio ([T-010](./T-010-stack-tooling-typescript-dominio.md)), que cubre el tooling para *escribir* código de dominio; por eso se decide en un ADR propio.
|
|
16
|
+
|
|
17
|
+
## Decisión
|
|
18
|
+
|
|
19
|
+
Adoptar **ultra-settings** (`@imolko/ultra-settings`) como la herramienta canónica de exportación de artefactos de despliegue desde el contrato de settings. Toda exportación sale de ultra-settings leyendo el schema Zod del contrato; no se mantienen scripts de exportación ad-hoc por aplicación.
|
|
20
|
+
|
|
21
|
+
La entrada de ultra-settings es el schema Zod del contrato ([T-016](./T-016-contrato-settings-zod.md)); la clasificación de cada campo, la taxonomía de [A-013 §2](../foundational/A-013-contrato-settings-unica-fuente-configuracion.md), es lo que decide el destino de cada artefacto según [A-014](../foundational/A-014-generacion-artefactos-despliegue-desde-contrato-settings.md). La taxonomía y la lista de artefactos las definen esos dos ADRs; ultra-settings solo las ejecuta en TypeScript.
|
|
22
|
+
|
|
23
|
+
Los comandos y la versión se declaran en `package.json` (dependencia y scripts), que es la fuente canónica; este ADR no duplica comandos ni versiones.
|
|
24
|
+
|
|
25
|
+
## Consecuencias
|
|
26
|
+
|
|
27
|
+
### Positivas
|
|
28
|
+
|
|
29
|
+
- **Un solo exportador**: todas las aplicaciones derivan sus artefactos del mismo mecanismo, sin scripts por aplicación.
|
|
30
|
+
- **Cero drift entre contrato y artefactos**: un cambio en el schema se propaga re-ejecutando la exportación, materializando en TypeScript la promesa de [A-014](../foundational/A-014-generacion-artefactos-despliegue-desde-contrato-settings.md).
|
|
31
|
+
- **Descubribilidad**: un agente o miembro nuevo sabe con qué herramienta se exporta la configuración.
|
|
32
|
+
|
|
33
|
+
### Negativas
|
|
34
|
+
|
|
35
|
+
- **Acoplamiento al ecosistema `@imolko`**: migrar fuera de ultra-settings implica reescribir el flujo de exportación.
|
|
36
|
+
- **Atado al schema Zod**: la exportación solo aplica donde el contrato es un schema Zod ([T-016](./T-016-contrato-settings-zod.md)); el patrón es agnóstico ([A-013 §5](../foundational/A-013-contrato-settings-unica-fuente-configuracion.md)), la herramienta no.
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# T-021: El pipe del caso de uso fluye datos: operadores por semántica y contexto inmutable
|
|
2
|
+
|
|
3
|
+
**Estado**: Aceptado
|
|
4
|
+
|
|
5
|
+
**Fecha**: 2026-09-16
|
|
6
|
+
**originally**: imolko
|
|
7
|
+
|
|
8
|
+
## Contexto
|
|
9
|
+
|
|
10
|
+
El pipe del caso de uso ([T-008](./T-008-expressive-use-case-pipe.md)) encadena pasos que comparten un contexto: la salida de un paso alimenta al siguiente ([A-044](../foundational/A-044-composicion-funcional-procesos-operaciones.md)). Los operadores que unen pasos tienen semánticas de flujo distintas, no evidentes por su nombre:
|
|
11
|
+
|
|
12
|
+
- `chain` reemplaza el valor del pipe con el resultado del paso.
|
|
13
|
+
- `sequence`/`executeSequentially` ejecuta pasos por sus efectos y devuelve el valor original.
|
|
14
|
+
|
|
15
|
+
Elegir el operador por cuántas funciones caben — y no por esa semántica — produce tres olores:
|
|
16
|
+
|
|
17
|
+
1. **Lista de un elemento**: `TE.executeSequentially([paso])` o `TV.checkRequirements([paso])` cuando el paso produce el siguiente valor; `chain` es el operador que coincide.
|
|
18
|
+
2. **Contexto mutado**: el paso asigna al contexto recibido (`ctx.claimId = ...`) y retorna `void`, forzando el pass-through de `sequence` para arrastrar la mutación. El flujo de datos queda escondido detrás de un efecto.
|
|
19
|
+
3. **Operador anidado**: el mapper de un `chain`/`map` invoca otro operador (`chain` que llama a `map`, o viceversa). Esconde un paso del proceso: extráelo a una función nombrada y encadénalo a primer nivel.
|
|
20
|
+
|
|
21
|
+
Sin regla canónica, el olor reaparece: en `abrir-reclamo` se manifestó dos veces en la misma feature (`combineValidationResults` y `executeSequentially([openClaim])`).
|
|
22
|
+
|
|
23
|
+
## Decisión
|
|
24
|
+
|
|
25
|
+
El pipe es **flujo de datos**: cada paso retorna el valor que el siguiente consume. El contexto que fluye no se asigna; se retorna.
|
|
26
|
+
|
|
27
|
+
| Regla | Descripción |
|
|
28
|
+
|-------|-------------|
|
|
29
|
+
| **Operador por semántica** | `chain` cuando el paso produce el siguiente valor (`TV.chain`/`TE.chain`). `sequence`/`executeSequentially` solo cuando hay más de un paso con efectos que deben conservar el acumulador de entrada. |
|
|
30
|
+
| **Lista de uno es señal** | Una función sola envuelta en `[...]` indica que `chain` es el operador correcto. |
|
|
31
|
+
| **Sin operadores anidados** | El mapper de un `chain`/`map` no invoca otro operador; cada paso es una función nombrada encadenada a primer nivel. |
|
|
32
|
+
| **El paso retorna el siguiente valor** | Un paso devuelve el contexto enriquecido (`ok({ ...ctx, claimId })`); no asigna a su argumento. |
|
|
33
|
+
| **El contexto tipifica la fase** | El tipo del contexto refleja la fase que lo produce (validación vs ejecución), de modo que la frontera `TE.startExecutions()` ([T-008](./T-008-expressive-use-case-pipe.md)) coincide con una frontera de tipos. |
|
|
34
|
+
|
|
35
|
+
La fase la decide el **tipo del puerto** ([A-025](../foundational/A-025-anatomia-mecanica-feature-puertos-adaptadores-drivers.md)); la forma de fluir el valor la deciden los **tipos del contexto**. El compilador impide ejecutar una lectura como mutación y leer un campo que la fase anterior no produjo.
|
|
36
|
+
|
|
37
|
+
❌ Incorrecto — el paso muta y la lista de uno fuerza el pass-through:
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
// use-case.ts — el paso asigna al contexto y retorna void
|
|
41
|
+
private openClaim = (ctx: PipelineContext): TaskExecution<Errors, void> => {
|
|
42
|
+
return async () => {
|
|
43
|
+
// … construir y persistir …
|
|
44
|
+
ctx.claimId = claimIdValue;
|
|
45
|
+
return ok(undefined);
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// el pipe conserva el contexto mutado con un sequence de un solo elemento
|
|
50
|
+
TE.executeSequentially([this.openClaim]),
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
✅ Correcto — el paso retorna el contexto enriquecido y el pipe usa `chain`:
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
// use-case.ts — el paso retorna el siguiente valor, sin mutar el recibido
|
|
57
|
+
private openClaim = (ctx: ValidationContext): TaskExecution<Errors, ExecutionContext> => {
|
|
58
|
+
return async () => {
|
|
59
|
+
// … construir y persistir …
|
|
60
|
+
return ok({ ...ctx, claimId: claimIdValue });
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// el pipe encadena: la salida del paso es la entrada del siguiente
|
|
65
|
+
TE.chain(this.openClaim),
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Cuando el paso es una **verificación** que delega en un puerto que retorna `void` ([A-025](../foundational/A-025-anatomia-mecanica-feature-puertos-adaptadores-drivers.md) "puerto de verificación"), el caso de uso conserva el contexto con `TV.map(() => ctx, verification)`: re-emite el valor previo, sin mutarlo. No es el olor de "contexto mutado" — el contexto no se asigna.
|
|
69
|
+
|
|
70
|
+
### Contexto por fase
|
|
71
|
+
|
|
72
|
+
El contexto que fluye se declara por fase, y la frontera entre fases es también una frontera de tipos:
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
/** Contexto de la fase de validación. */
|
|
76
|
+
interface ValidationContext {
|
|
77
|
+
input: InputDto;
|
|
78
|
+
resolvedReferenceId: string;
|
|
79
|
+
stockMovement: StockMovement;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Contexto de la fase de ejecución: agrega lo que esa fase produce. */
|
|
83
|
+
interface ExecutionContext extends ValidationContext {
|
|
84
|
+
claimId: string;
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Así `toOutput` recibe un `ExecutionContext` con `claimId` obligatorio y no necesita non-null assertion: el compilador garantiza que el campo existe cuando el paso lo lee.
|
|
89
|
+
|
|
90
|
+
La fase a la que pertenece un paso de construcción de objetos (ejecución, no validación) y la unicidad de la frontera `TE.startExecutions()` las fija [T-022](./T-022-construction-is-execution-single-boundary.md).
|
|
91
|
+
|
|
92
|
+
## Consecuencias
|
|
93
|
+
|
|
94
|
+
### Positivas
|
|
95
|
+
|
|
96
|
+
- **Flujo explícito**: leyendo el pipe se ve qué valor produce cada paso, sin rastrear mutaciones.
|
|
97
|
+
- **El compilador vigila**: el contexto por fase elimina las non-null assertions y hace de `TE.startExecutions()` una frontera de tipos.
|
|
98
|
+
- **Sin operadores engañosos**: no hay listas de un elemento ni pass-through implícito.
|
|
99
|
+
- **Pasos testables**: cada paso retorna su resultado, sin efectos sobre el argumento.
|
|
100
|
+
|
|
101
|
+
### Negativas
|
|
102
|
+
|
|
103
|
+
- **Un tipo de contexto por fase**: dos interfaces donde antes bastaba una con campo opcional.
|
|
104
|
+
- **Semántica a aprender**: `chain` vs `sequence` no es evidente por el nombre; exige leer el contrato del operador.
|
|
105
|
+
- **Migración**: las features existentes que mutan el contexto o usan `sequence` de un elemento deben refactorizarse, como el paso post-generación de [T-008](./T-008-expressive-use-case-pipe.md).
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# T-022: Construction is Execution; single Validation→Execution boundary
|
|
2
|
+
|
|
3
|
+
**Estado**: Aceptado
|
|
4
|
+
|
|
5
|
+
**Fecha**: 2026-09-16
|
|
6
|
+
|
|
7
|
+
**originally**: imolko
|
|
8
|
+
|
|
9
|
+
## Contexto
|
|
10
|
+
|
|
11
|
+
El pipe del caso de uso ([T-008](./T-008-expressive-use-case-pipe.md)) separa validación y ejecución con una única frontera: `TE.startExecutions()`. La fase la decide el tipo del puerto ([A-025](../foundational/A-025-anatomia-mecanica-feature-puertos-adaptadores-drivers.md)); la forma de fluir el valor la deciden los tipos del contexto ([T-021](./T-021-pipe-flujo-datos-operadores-semantica-contexto-inmutable.md)).
|
|
12
|
+
|
|
13
|
+
Los constructores del dominio (`ClaimId.of`, `Claim.create`) retornan `Validation`, porque construir valida y puede fallar. Eso invita a tipar también el paso del caso de uso que construye el agregado como `Validation` e invocarlo *dentro* de la fase de ejecución, levantándolo con una conversión `Validation → Execution` escondida (`E.fromValidation`) después de `TE.startExecutions()`. En `abrir-reclamo` se manifestó así: `buildClaim` retornaba `Validation` pero vivía dentro de `openClaim`, que lo levantaba a `Execution` — una segunda frontera de fase que contradice [T-008](./T-008-expressive-use-case-pipe.md).
|
|
14
|
+
|
|
15
|
+
La causa es una ambigüedad de vocabulario entre dos librerías del stack:
|
|
16
|
+
|
|
17
|
+
- **ultra-result** define `Execution` como "cualquier función que hace un cambio en la aplicación. **Esto incluye creación de objetos, transformación de objetos**", y `Validation` como "una validación… **no realiza transformación en ninguna variable o estado**".
|
|
18
|
+
- **ultra-ddd** tipa la *construcción* del dominio (`*.of`, `*.create`) como `Validation`, porque la construcción valida.
|
|
19
|
+
|
|
20
|
+
Sin una regla canónica, el olor reaparece: la creación de objetos queda tipada como `Validation` y arrastrada a una fase que no le corresponde, o escondida dentro de un paso mayor.
|
|
21
|
+
|
|
22
|
+
## Decisión
|
|
23
|
+
|
|
24
|
+
La **construcción de objetos es `Execution`**, no `Validation`. El paso del caso de uso que crea el agregado o value object es un paso de la fase de ejecución, aunque internamente componga los `Validation` que retornan los constructores del dominio.
|
|
25
|
+
|
|
26
|
+
| Regla | Descripción |
|
|
27
|
+
|-------|-------------|
|
|
28
|
+
| **La construcción es Execution** | El paso que crea un agregado/VO (p. ej. `buildClaim` con `Claim.open`) retorna `Execution`/`TaskExecution`, no `Validation`. Compone los `Validation` del dominio con los operadores de `V` y los levanta una vez con `E.fromValidation`, detrás de su propia interfaz `Execution`. |
|
|
29
|
+
| **Una sola frontera de fase** | `TE.startExecutions()` es la única conversión `Validation → Execution` del pipe. La conversión interna de un paso de construcción no es una segunda frontera del pipe: queda oculta tras la interfaz `Execution` de ese paso. |
|
|
30
|
+
| **La construcción fallible es un paso visible** | Si crear el objeto puede fallar, es su propio paso nombrado en el pipe (`TE.chain(this.buildClaim)`): su canal de error queda a la vista. No se entierra dentro de otro paso. |
|
|
31
|
+
|
|
32
|
+
❌ Incorrecto — la construcción tipada `Validation` se invoca después de la frontera:
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
// use-case.ts — buildClaim retorna Validation pero vive en la fase de ejecución
|
|
36
|
+
private openClaim = (ctx) => TE.fromAsync(async () => {
|
|
37
|
+
const built = this.buildClaim(ctx); // Validation<Errors, BuiltClaim>
|
|
38
|
+
if (!built.isOk()) return built as unknown as Execution<…>;
|
|
39
|
+
// … segunda conversión Validation → Execution escondida en openClaim
|
|
40
|
+
});
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
✅ Correcto — la construcción es un paso `Execution` visible tras la frontera:
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
const process = seedTV(input).pipe(
|
|
47
|
+
TV.chain(this.resolveReference),
|
|
48
|
+
TV.chain(this.loadReferencedMovement),
|
|
49
|
+
TV.chain(this.verifyNoOpenClaimExists),
|
|
50
|
+
TE.startExecutions(),
|
|
51
|
+
TE.chain(this.buildClaim), // Execution: puede fallar, visible
|
|
52
|
+
TE.executeSequentially([this.recordClaim, this.publishClaimEvents]),
|
|
53
|
+
TE.map(this.toOutput),
|
|
54
|
+
);
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### El pipe de construcción
|
|
58
|
+
|
|
59
|
+
El paso `buildClaim` compone a primer nivel ([T-021](./T-021-pipe-flujo-datos-operadores-semantica-contexto-inmutable.md)): genera los identificadores con pasos nombrados y construye en el último. Dentro del paso que construye, la `Validation` del dominio se levanta una única vez:
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
private buildClaim = (ctx: ValidationContext): TaskExecution<Errors, ExecutionContext> =>
|
|
63
|
+
seedTE(ctx).pipe(
|
|
64
|
+
TE.chain(this.withClaimId),
|
|
65
|
+
TE.chain(this.withUuid),
|
|
66
|
+
TE.chain(this.buildClaimFromIds),
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
private buildClaimFromIds = (ctx: ClaimBuildContext): TaskExecution<Errors, ExecutionContext> => {
|
|
70
|
+
const built: Validation<Errors, ExecutionContext> = V.map(
|
|
71
|
+
(claim: Claim): ExecutionContext => ({ ...ctx, claim }),
|
|
72
|
+
Claim.open({
|
|
73
|
+
id: ctx.uuid,
|
|
74
|
+
claimId: ctx.claimId,
|
|
75
|
+
movement: ctx.stockMovement,
|
|
76
|
+
reason: ctx.input.reason,
|
|
77
|
+
description: ctx.input.description,
|
|
78
|
+
createdBy: ctx.input.createdBy,
|
|
79
|
+
}),
|
|
80
|
+
);
|
|
81
|
+
return TE.fromExecution(E.fromValidation(built));
|
|
82
|
+
};
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
La generación de identificadores vive en el puerto `IdGeneratorPort`, que entrega los value objects (`ClaimId`, `Uuid`) ya construidos ([A-050](../foundational/A-050-el-caso-de-uso-pide-al-dominio-no-lee-su-representacion.md)); `Claim.open` es la fábrica del dominio que ensambla el agregado desde el movimiento ([T-015](./T-015-method-placement-definition.md)).
|
|
86
|
+
|
|
87
|
+
### No re-validar value objects que el input ya porta
|
|
88
|
+
|
|
89
|
+
El `InputDto` del caso de uso ya entrega value objects construidos y válidos (p. ej. `reason: ClaimReason`, `createdBy: UserId`). El paso de construcción no los reconstruye con `Xxx.of({ value: input.reason.getPrimitive() })`: usa el value object del input tal cual. Reconstruirlos es trabajo muerto (ya vienen válidos; nunca falla).
|
|
90
|
+
|
|
91
|
+
### Pitfall de librería: `V.allProperties`
|
|
92
|
+
|
|
93
|
+
`V.allProperties` tipa su canal de error como la **lista** (`Errors[]`) en lugar del elemento (`Errors`), lo que impide ensanchar la unión sin cast. Para reunir construcciones heterogéneas, componer con `V.chain`/`V.map` y ensanchar el error por anotación (`const r: Validation<Errors, T> = Xxx.of(...)`), no con `V.allProperties`.
|
|
94
|
+
|
|
95
|
+
### Pitfall de librería: `V.fromValue(undefined)` devuelve el operador curriado
|
|
96
|
+
|
|
97
|
+
`V.fromValue(undefined)` retorna la función curriada en lugar de `Valid(undefined)`: el helper interno `curry1` de ultra-result usa `undefined` como centinela de "argumento ausente". Para una hoja `undefined`, usar el factory `valid(undefined)` (o `V.fromValue` con un valor distinto de `undefined`). El mismo centinela aplica a cualquier operador curriado al que se le pase `undefined` como argumento.
|
|
98
|
+
|
|
99
|
+
## Consecuencias
|
|
100
|
+
|
|
101
|
+
### Positivas
|
|
102
|
+
|
|
103
|
+
- **Frontera única y visible**: el pipe declara exactamente dónde acaba la validación y empieza la ejecución, sin conversiones escondidas.
|
|
104
|
+
- **Fallos de construcción a la vista**: `TE.chain(this.buildClaim)` deja el canal de error de la creación en el proceso, no enterrado en un helper.
|
|
105
|
+
- **Vocabulario alineado con ultra-result**: "creación = Execution" resuelve la ambigüedad con ultra-ddd y da un criterio para colocar cada paso.
|
|
106
|
+
- **Sin aserciones**: el tipo del pipe se infiere de las firmas de paso; `execute()` retorna `process` sin `as`.
|
|
107
|
+
|
|
108
|
+
### Negativas
|
|
109
|
+
|
|
110
|
+
- **Una conversión interna por paso de construcción**: cada paso que construye debe levantar `Validation → Execution` con `E.fromValidation` (una línea, detrás de su interfaz).
|
|
111
|
+
- **Vocabulario a aprender**: distinguir `Validation` (lectura/verificación) de `Execution` (creación/mutación) exige leer las definiciones de ultra-result, no el nombre del constructor.
|
|
112
|
+
- **Migración**: los pasos que hoy construyen dentro de un paso mayor deben promoverse a pasos nombrados del pipe.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# T-023: El registry del CLI declara las dependencias reales del caso de uso
|
|
2
|
+
|
|
3
|
+
**Estado**: Aceptado
|
|
4
|
+
|
|
5
|
+
**Fecha**: 2026-09-19
|
|
6
|
+
|
|
7
|
+
**originally**: imolko
|
|
8
|
+
|
|
9
|
+
## Contexto
|
|
10
|
+
|
|
11
|
+
`apps/cli` registra sus ~41 casos de uso en un registry plano (`USE_CASE_REGISTRY`), un archivo por feature. Cada entrada es un descriptor tipado `UseCaseEntry<TSchema, TDeps>` con tres piezas: `schema` valida el input externo, `deps(drivers): TDeps` construye las dependencias desde los drivers compartidos y `execute(input, deps)` traduce el input validado y ejecuta.
|
|
12
|
+
|
|
13
|
+
La forma inicial de `TDeps` era opaca:
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
interface AbrirReclamoDeps { useCase: AbrirReclamoUseCase; }
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
El seam (`Deps`) no decía qué necesita el caso de uso: había que abrir el `use-case.ts` de la feature para verlo. Y sin embargo el caso de uso **ya** declara sus puertos (`UseCaseDeps` en `use-case.ts`); el registry los ensamblaba y los escondía detrás de `{ useCase }`, duplicando información que ya existía en el constructor.
|
|
20
|
+
|
|
21
|
+
[A-027](../foundational/A-027-patron-wiring-entry-point.md) fija la separación wiring/lógica, pero para un único entry-point que recibe "el controlador ya cableado"; no cubre un registry de N casos de uso compuestos a mano, sin contenedor de inyección.
|
|
22
|
+
|
|
23
|
+
## Decisión
|
|
24
|
+
|
|
25
|
+
El seam de cada entrada del registry declara las **dependencias reales del caso de uso** — sus puertos — y el ensamblado se mueve a la función de ejecución.
|
|
26
|
+
|
|
27
|
+
### Ámbito
|
|
28
|
+
|
|
29
|
+
Aplica a las apps que **componen casos de uso manualmente** (registry o wiring explícito, hoy `apps/cli`). **No aplica** a apps con contenedor de inyección de dependencias (p. ej. NestJS): ahí el constructor del caso de uso ya *es* la declaración de dependencias y no existe un seam manual que documentar; registrar los adaptadores como providers e inyectarlos es suficiente ([A-027](../foundational/A-027-patron-wiring-entry-point.md)).
|
|
30
|
+
|
|
31
|
+
### Reglas
|
|
32
|
+
|
|
33
|
+
| Regla | Descripción |
|
|
34
|
+
|-------|-------------|
|
|
35
|
+
| **`Deps` expone los puertos** | `type XxxDeps = ConstructorParameters<typeof XxxUseCase>[0]` — el seam es exactamente el `UseCaseDeps` del caso de uso (`stockMovementRepositoryPort`, `claimRepositoryPort`, `eventBusPort`, `idGeneratorPort`), no `{ useCase }`. |
|
|
36
|
+
| **`deps` construye solo puertos/adaptadores** | La raíz de composición instancia los adaptadores que implementan esos puertos ([A-025](../foundational/A-025-anatomia-mecanica-feature-puertos-adaptadores-drivers.md)); no ensambla el caso de uso. |
|
|
37
|
+
| **`execute` ensambla y ejecuta** | `execute(input, deps)` hace `new XxxUseCase(deps)`, construye el controlador y ejecuta. |
|
|
38
|
+
| **Contrato público intacto** | `UseCaseEntry`, `USE_CASE_REGISTRY`, `listUseCases()` y las dos convenciones de retorno no cambian: `undefined` (legacy, escribe en el presenter) o el JSON ya serializado (canónico, con `executionToJson`). |
|
|
39
|
+
|
|
40
|
+
### Forma
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
type AbrirReclamoDeps = ConstructorParameters<typeof AbrirReclamoUseCase>[0];
|
|
44
|
+
|
|
45
|
+
function buildDeps(drivers: DriverMap): AbrirReclamoDeps {
|
|
46
|
+
return {
|
|
47
|
+
stockMovementRepositoryPort: new AbrirReclamoStockMovementRepositoryAdapter(
|
|
48
|
+
new InMemoryStockMovementRepositoryDriver(drivers.stockMovementStore),
|
|
49
|
+
),
|
|
50
|
+
claimRepositoryPort: new AbrirReclamoClaimRepositoryAdapter(
|
|
51
|
+
new InMemoryClaimRepositoryDriver(drivers.claimStore),
|
|
52
|
+
),
|
|
53
|
+
eventBusPort: new AbrirReclamoEventBusAdapter(drivers.eventBus),
|
|
54
|
+
idGeneratorPort: new AbrirReclamoIdGeneratorAdapter(new CryptoIdGeneratorDriver()),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async function run(input: AbrirReclamoInput, deps: AbrirReclamoDeps) {
|
|
59
|
+
const useCase = new AbrirReclamoUseCase(deps);
|
|
60
|
+
const controller = new AbrirReclamoController(useCase);
|
|
61
|
+
return executionToJson(await controller.execute(input)());
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`ConstructorParameters` deriva el seam del constructor del caso de uso: si `UseCaseDeps` cambia, el compilador fuerza a `buildDeps` a proveer los puertos nuevos o actualizados, sin un `interface` duplicado que mantener a mano.
|
|
66
|
+
|
|
67
|
+
### Desviación de A-027
|
|
68
|
+
|
|
69
|
+
[A-027](../foundational/A-027-patron-wiring-entry-point.md) prescribe que la función de lógica recibe "el controlador ya cableado". Aquí `execute` recibe los **puertos** y ensambla caso de uso + controlador. Es una variante deliberada: el seam cruza los puertos — no el controlador — para que documente las dependencias reales del caso de uso, a costa de mover un paso de ensamblado a la función de lógica.
|
|
70
|
+
|
|
71
|
+
## Consecuencias
|
|
72
|
+
|
|
73
|
+
### Positivas
|
|
74
|
+
|
|
75
|
+
- **Seam autodocumentado**: leer el archivo del registry muestra de un vistazo los puertos que consume el caso de uso.
|
|
76
|
+
- **Sin duplicación**: el seam se deriva del constructor; no se redeclara `UseCaseDeps` ni se mantiene un `interface` espejo.
|
|
77
|
+
- **Paridad forzada por el compilador**: añadir o cambiar un puerto en el caso de uso rompe `buildDeps` en compilación, no en runtime.
|
|
78
|
+
- **Contrato público estable**: `cli-commands.ts` sigue resolviendo `entry.deps(driverMap)` y llamando `entry.execute(parsedInput, deps)`; el cambio es solo el shape del `Deps` por archivo.
|
|
79
|
+
|
|
80
|
+
### Negativas
|
|
81
|
+
|
|
82
|
+
- **Ensamblado en la lógica**: `execute` instancia el caso de uso concreto, un paso más allá de la "lógica pura con controlador cableado" de A-027.
|
|
83
|
+
- **Depende de un constructor de un solo objeto**: `ConstructorParameters<...[0]>` asume que el caso de uso recibe exactamente un objeto de dependencias; un constructor posicional rompe el idiom.
|
|
84
|
+
- **No es una regla de DI**: la decisión no se traslada a apps con contenedor; solo documenta el caso de composición manual.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# T-024: El paquete `@imolko/ultra-agent` — manifiesto pi, peers y CLI
|
|
2
|
+
|
|
3
|
+
**Estado**: Aceptado
|
|
4
|
+
|
|
5
|
+
**Fecha**: 2026-10-02
|
|
6
|
+
|
|
7
|
+
**originally**: imolko
|
|
8
|
+
|
|
9
|
+
## Contexto
|
|
10
|
+
|
|
11
|
+
[A-052](../foundational/A-052-distribucion-metodologia-paquete-versionado.md) fija el modelo de distribución (paquete npm de suscripción con CLI). Falta su materialización TypeScript/npm: cómo se declaran los skills y las extensiones para que pi los descubra, qué dependencias de runtime arrastra el paquete y cómo se publica con el pipeline de GitLab.
|
|
12
|
+
|
|
13
|
+
Las librerías TypeScript del ecosistema ya tienen convenciones de empaquetado y publicación ([T-014](./T-014-publicacion-distribucion-librerias-npm-semver.md), [T-010](./T-010-stack-tooling-typescript-dominio.md)), pero ninguna cubre un paquete cuyo payload principal es markdown (skills + ADRs) más extensiones de pi.
|
|
14
|
+
|
|
15
|
+
## Decisión
|
|
16
|
+
|
|
17
|
+
Materializar `@imolko/ultra-agent` como una **librería de un solo paquete**, sin workspaces ([T-012](./T-012-npm-workspaces-estructura-monorepo-clean-architecture.md) aplica a monorepos de sistema, no a una librería):
|
|
18
|
+
|
|
19
|
+
1. **Estructura.** `skills/` (skills canónicos), `docs/adr/` (payload A/T/P), `pi/extensions/` (extensiones de pi), `eslint/` (el preset flat-config que materializa las reglas ejecutables del tier A, expuesto como `@imolko/ultra-agent/eslint`), `src/` + `bin/` (CLI, incluido el subcomando `ship` con sus prompts de fase `implement`/`review`/`fix` empaquetados como payload). Un único `package.json`, MIT, `publishConfig.access: public`, `engines.node >=20`, `tsconfig` extendiendo `@imolko/ultra-tsconfig/library/tsconfig.json`.
|
|
20
|
+
2. **Manifiesto `pi`.** El `package.json` declara `pi.skills: ["./skills"]` y `pi.extensions: ["./pi/extensions"]`, de modo que `pi install npm:@imolko/ultra-agent` descubre skills y extensiones por el descubrimiento inteligente de pi (cada extensión por su `index.ts`, sin cargar sus `*.test.ts`).
|
|
21
|
+
3. **Dependencias.** `eslint` como `dependency` —peer de runtime del adaptador de `qa-guardrail`, que cierra el punto abierto de [A-049](../foundational/A-049-materializacion-bucle-guardrails-extension-nativa-pi.md)—, junto con los plugins que el preset declara (`@typescript-eslint/eslint-plugin`, `@typescript-eslint/parser`, `eslint-plugin-boundaries`, `eslint-plugin-import-x`, `eslint-import-resolver-typescript`), y `@earendil-works/pi-coding-agent` como `peerDependency: "*"` (lo proporciona pi; no se empaqueta). Las extensiones se publican como fuente `.ts`, que pi transpila; no requieren build propio.
|
|
22
|
+
4. **CLI.** Un `bin` (`ultra-agent`) con `install`, `update`, `status`, `ship` e `init-lint`, según [A-052](../foundational/A-052-distribucion-metodologia-paquete-versionado.md) y [A-039](../foundational/A-039-pipeline-ship-issues-hijos-spec.md). El subcomando `ship` dispara el pipeline de A-039 y lee los prompts de fase (`implement`/`review`/`fix`) del payload del paquete — no de `.pi/agents/` del consumidor. El subcomando `init-lint` escribe en el espacio de extensión del consumidor un `eslint.config.mjs` mínimo que importa el preset y, si falta, el `.pi/qa-guardrail.json` de solo selección; es idempotente, nunca sobrescribe y `update` nunca lo reconcilia.
|
|
23
|
+
5. **Publicación.** El pipeline de GitLab ([A-016](../foundational/A-016-cicd-gitlab-pipeline-raiz-pipelines-modulares.md)) publica con el flujo de release de las librerías: `develop` → `-beta`, `main` → estable. La versión SemVer se deriva de los tipos de Conventional Commits ([A-033](../foundational/A-033-estandar-mensajes-commit.md) §7), sustituyendo las keywords ad hoc `UPGRADE_MAJOR_VERSION`/`UPGRADE_MINOR_VERSION` heredadas de los flujos previos a la migración de A-033.
|
|
24
|
+
|
|
25
|
+
## Consecuencias
|
|
26
|
+
|
|
27
|
+
### Positivas
|
|
28
|
+
|
|
29
|
+
- **Un solo punto de instalación**: `pi install` cubre skills y extensiones; el CLI cubre el espejo de ADRs, los punteros y el pipeline `ship`.
|
|
30
|
+
- **Sin build para el payload**: skills y ADRs se publican tal cual; solo el CLI se compila.
|
|
31
|
+
- **Peer explícito**: `qa-guardrail` pasa a ser distribuible al declarar `eslint` (cierra la consecuencia negativa de A-049).
|
|
32
|
+
- **Versionado coherente con el resto de librerías**: SemVer por tipos de commit, no por keywords ad hoc.
|
|
33
|
+
|
|
34
|
+
### Negativas
|
|
35
|
+
|
|
36
|
+
- **Dependencia de runtime de pi**: `eslint` y los plugins del preset como dependencia obligan a instalarlos aunque el consumidor solo quiera skills; se asume el coste por la extensión y el preset.
|
|
37
|
+
- **`pi` como superficie de manifiesto**: la declaración `pi.skills`/`pi.extensions` es específica de pi; otros harnesses ignoran el manifiesto y reciben los skills por el CLI.
|
|
38
|
+
- **Dependencia de runtime de `ship`**: el subcomando `ship` exige `glab` (tracker) y `pi` (sub-agentes) disponibles en el PATH del consumidor; no se empaquetan.
|
|
39
|
+
- **Migración del flujo de release pendiente**: adoptar la derivación por tipos implica no copiar los scripts de release con keywords de las librerías previas.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# T-025: Publicación npm con Trusted Publishing, procedencia y fallback por staging
|
|
2
|
+
|
|
3
|
+
**Estado**: Aceptado
|
|
4
|
+
|
|
5
|
+
**Fecha**: 2026-10-03
|
|
6
|
+
|
|
7
|
+
**originally**: imolko
|
|
8
|
+
|
|
9
|
+
## Contexto
|
|
10
|
+
|
|
11
|
+
[T-014](./T-014-publicacion-distribucion-librerias-npm-semver.md) fija **qué** se publica —librerías TypeScript en el registry público de npm, bajo el scope `@imolko`, con SemVer y canal `-beta`— y delega el **cómo** al flujo de release de los scripts. Faltaba decidir **cómo se autentica** esa publicación y **qué garantías de cadena de suministro** ofrece. El repositorio lo arrastraba sin decisión: el pipeline declaraba tokens OIDC de GitLab pero los escribía como si fueran tokens de npm, y el push de etiquetas no tenía credencial.
|
|
12
|
+
|
|
13
|
+
Tres fuerzas restringen la decisión:
|
|
14
|
+
|
|
15
|
+
- **Trusted Publishing (OIDC) de npm** elimina los tokens de larga vida y genera atestaciones de procedencia de forma automática, pero solo acepta runners alojados en la nube (runners compartidos de GitLab.com). Los runners autoalojados quedan fuera de soporte, y un runner autoalojado lo delata el claim `runner_environment` de su token OIDC.
|
|
16
|
+
- **Cuota de minutos de GitLab.com.** Los namespaces Free tienen una cuota mensual de minutos de cómputo; cuando se agota, los runners alojados dejan de ejecutar jobs y solo quedan los runners propios. Bloquear una publicación hasta que reinicie la cuota es inaceptable: [A-041](../foundational/A-041-estrategia-de-ramas.md) fija la publicación tras la fusión y [A-016](../foundational/A-016-cicd-gitlab-pipeline-raiz-pipelines-modulares.md) gobierna el pipeline.
|
|
17
|
+
- **Deprecación de npm.** Los *granular access tokens* que omiten 2FA pierden la publicación directa (objetivo enero 2027), pero conservan la capacidad de **stage** (`npm stage publish`), que un mantenedor aprueba con 2FA.
|
|
18
|
+
|
|
19
|
+
## Decisión
|
|
20
|
+
|
|
21
|
+
### 1. Dos caminos de publicación, un solo job
|
|
22
|
+
|
|
23
|
+
- **Runner alojado de GitLab → Trusted Publishing (OIDC).** El job declara `id_tokens` para npm y para sigstore; `npm publish` publica directo y npm genera la atestación de procedencia.
|
|
24
|
+
- **Runner propio (cuota agotada) → staging.** El `.npmrc` lleva un *granular access token* **stage-only**; el release script publica con `npm stage publish` y un mantenedor aprueba la versión con 2FA.
|
|
25
|
+
|
|
26
|
+
El fallback es automático: npm intenta el intercambio OIDC primero y, cuando no está disponible, publica con la credencial configurada.
|
|
27
|
+
|
|
28
|
+
### 2. Enrutado por `runner_environment`
|
|
29
|
+
|
|
30
|
+
El job de publicación no se fija a un runner alojado: debe poder correr en los runners propios cuando la cuota se agota. El release script elige el camino leyendo el claim `runner_environment` del token OIDC —el mismo hecho que npm usa para aceptar o rechazar OIDC—: `gitlab-hosted` publica directo; cualquier otro valor hace staging.
|
|
31
|
+
|
|
32
|
+
### 3. Procedencia como mejor esfuerzo
|
|
33
|
+
|
|
34
|
+
La procedencia es automática en el camino OIDC y **ausente** en el fallback por staging; no es un requisito bloqueante. En consecuencia, el paquete **no** activa «Require two-factor authentication and disallow tokens»: esa configuración eliminaría el fallback.
|
|
35
|
+
|
|
36
|
+
### 4. Arranque
|
|
37
|
+
|
|
38
|
+
npm no puede publicar la **primera** versión por OIDC y npmjs.com no permite adjuntar un *trusted publisher* a un paquete que todavía no existe. La primera versión se publica **una vez, a mano, con 2FA interactivo**; después se adjunta el *trusted publisher* y CI toma el control.
|
|
39
|
+
|
|
40
|
+
### 5. Etiquetado por reconciliación
|
|
41
|
+
|
|
42
|
+
El etiquetado de release no ocurre durante la publicación. Un paso de **reconciliación** —cada versión publicada en el registry que no tenga etiqueta git se etiqueta en el commit que la produjo, que el registry expone como `gitHead`— corre **antes** de cada derivación de versión y es el único mecanismo que crea etiquetas de release, para ambos caminos. El push de etiquetas usa una variable enmascarada (`GITLAB_TOKEN`), como el resto de librerías. Así, una versión en staging solo se etiqueta cuando queda aprobada y viva.
|
|
43
|
+
|
|
44
|
+
## Consecuencias
|
|
45
|
+
|
|
46
|
+
### Positivas
|
|
47
|
+
|
|
48
|
+
- **Sin credenciales npm de larga vida en el camino normal**: OIDC autentica y npm genera la procedencia.
|
|
49
|
+
- **La publicación no se bloquea por la cuota**: cuando no hay runners alojados, el fallback hace staging desde los runners propios y un mantenedor aprueba.
|
|
50
|
+
- **El fallback sobrevive la deprecación de npm**: el staging sigue disponible cuando los tokens que omiten 2FA pierdan la publicación directa.
|
|
51
|
+
- **Token de mínimo privilegio**: el único token que permanece es stage-only; aunque se filtre, no puede saltarse la revisión.
|
|
52
|
+
- **Invariante etiqueta↔publicado**: la reconciliación crea etiquetas solo para versiones publicadas, en su commit real; la derivación de versión nunca etiqueta versiones que no existen.
|
|
53
|
+
|
|
54
|
+
### Negativas
|
|
55
|
+
|
|
56
|
+
- **Aprobación humana en el fallback**: cada release por staging exige un 2FA de un mantenedor, y esa versión no lleva procedencia.
|
|
57
|
+
- **Dos comportamientos a probar**: camino OIDC y camino staging.
|
|
58
|
+
- **Acoplamiento al claim `runner_environment`**: el enrutado depende de un detalle del token OIDC de GitLab.
|
|
59
|
+
- **Dependencias externas**: el camino normal depende de la configuración de *trusted publisher* en npmjs.com y de versiones mínimas de npm/Node; el fallback depende de un token stage-only con expiración y de la variable enmascarada `GITLAB_TOKEN`.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# ADRs de TypeScript (T-XXX)
|
|
2
|
+
|
|
3
|
+
Decisiones de arquitectura específicas del ecosistema TypeScript/Zod/Node.js. Definen cómo implementar conceptos fundacionales en TypeScript.
|
|
4
|
+
|
|
5
|
+
| # | Título | Estado |
|
|
6
|
+
|---|--------|--------|
|
|
7
|
+
| T-001 | [Validación de artefactos DDD con primitivas Zod y errores built-in](./T-001-ddd-artifact-validation-zod-primitives.md) | Aceptado |
|
|
8
|
+
| T-002 | [Documentación de tests con JSDoc para ultra-reporter](./T-002-test-documentation-jsdoc.md) | Aceptado |
|
|
9
|
+
| T-003 | [Reconciliación del scaffold de ultra-generator con el formato canónico](./T-003-readme-formatting-domain-artifacts.md) | Aceptado |
|
|
10
|
+
| T-005 | [Contratos de input reflejan la interfaz externa; el caso de uso resuelve la opcionalidad](./T-005-input-optionality-use-case-not-entity.md) | Aceptado |
|
|
11
|
+
| T-006 | [Enums pequeños y estables se inlinean con Zod regex](./T-006-small-enums-inlined-zod-regex.md) | Aceptado |
|
|
12
|
+
| T-007 | [Todo controlador de feature expone un esquema Zod para su InputDto](./T-007-controller-input-zod-schema.md) | Aceptado |
|
|
13
|
+
| T-008 | [El pipe del caso de uso expresa el proceso con funciones nombradas](./T-008-expressive-use-case-pipe.md) | Aceptado |
|
|
14
|
+
| T-009 | [Escape de llaves en contenido generado para MDX/Docusaurus](./T-009-mdx-brace-escaping.md) | Aceptado |
|
|
15
|
+
| T-010 | [Stack de tooling TypeScript del dominio (ultra-ddd, ultra-v8n, ultra-result, ultra-tsconfig)](./T-010-stack-tooling-typescript-dominio.md) | Aceptado |
|
|
16
|
+
| T-011 | [ultra-reporter como herramienta canónica de documentación](./T-011-ultra-reporter-herramienta-canonica-documentacion.md) | Aceptado |
|
|
17
|
+
| T-012 | [npm workspaces como estructura del monorepo Clean Architecture](./T-012-npm-workspaces-estructura-monorepo-clean-architecture.md) | Aceptado |
|
|
18
|
+
| T-013 | [Definición de eventos versionados con ultra-ddd](./T-013-definicion-eventos-versionados-ultra-ddd.md) | Aceptado |
|
|
19
|
+
| T-014 | [Publicación y distribución de librerías TypeScript con npm y semver](./T-014-publicacion-distribucion-librerias-npm-semver.md) | Aceptado |
|
|
20
|
+
| T-015 | [`Definition` como único hogar de los métodos de dominio](./T-015-method-placement-definition.md) | Aceptado |
|
|
21
|
+
| T-016 | [Implementación del contrato de settings con schema Zod](./T-016-contrato-settings-zod.md) | Aceptado |
|
|
22
|
+
| T-017 | [Golden files para characterization tests](./T-017-characterization-tests-golden-files.md) | Aceptado |
|
|
23
|
+
| T-018 | [Procedencia de los campos del frontmatter del README](./T-018-procedencia-campos-frontmatter.md) | Aceptado |
|
|
24
|
+
| T-019 | [ultra-generator como herramienta canónica de generación de artefactos](./T-019-ultra-generator-herramienta-canonica-generacion.md) | Aceptado |
|
|
25
|
+
| T-020 | [ultra-settings como herramienta canónica de exportación de artefactos de despliegue](./T-020-ultra-settings-herramienta-canonica-exportacion-despliegue.md) | Aceptado |
|
|
26
|
+
| T-021 | [El pipe del caso de uso fluye datos: operadores por semántica y contexto inmutable](./T-021-pipe-flujo-datos-operadores-semantica-contexto-inmutable.md) | Aceptado |
|
|
27
|
+
| T-022 | [Construction is Execution; single Validation→Execution boundary](./T-022-construction-is-execution-single-boundary.md) | Aceptado |
|
|
28
|
+
| T-023 | [El registry del CLI declara las dependencias reales del caso de uso](./T-023-registry-cli-declara-dependencias-reales.md) | Aceptado |
|
|
29
|
+
| T-024 | [El paquete `@imolko/ultra-agent` — manifiesto pi, peers y CLI](./T-024-paquete-ultra-agent-manifiesto-pi-peers-cli.md) | Aceptado |
|
|
30
|
+
| T-025 | [Publicación npm con Trusted Publishing, procedencia y fallback por staging](./T-025-publicacion-npm-trusted-publishing-procedencia.md) | Aceptado |
|