@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.
Files changed (239) hide show
  1. package/README.md +93 -0
  2. package/bin/cli.js +2 -0
  3. package/dist-node/bin.d.ts +2 -0
  4. package/dist-node/bin.d.ts.map +1 -0
  5. package/dist-node/bin.js +5 -0
  6. package/dist-node/bin.js.map +1 -0
  7. package/dist-node/cli.d.ts +41 -0
  8. package/dist-node/cli.d.ts.map +1 -0
  9. package/dist-node/cli.js +170 -0
  10. package/dist-node/cli.js.map +1 -0
  11. package/dist-node/index.d.ts +14 -0
  12. package/dist-node/index.d.ts.map +1 -0
  13. package/dist-node/index.js +40 -0
  14. package/dist-node/index.js.map +1 -0
  15. package/dist-node/init-lint.d.ts +43 -0
  16. package/dist-node/init-lint.d.ts.map +1 -0
  17. package/dist-node/init-lint.js +84 -0
  18. package/dist-node/init-lint.js.map +1 -0
  19. package/dist-node/manifest.d.ts +33 -0
  20. package/dist-node/manifest.d.ts.map +1 -0
  21. package/dist-node/manifest.js +59 -0
  22. package/dist-node/manifest.js.map +1 -0
  23. package/dist-node/materializer.d.ts +52 -0
  24. package/dist-node/materializer.d.ts.map +1 -0
  25. package/dist-node/materializer.js +194 -0
  26. package/dist-node/materializer.js.map +1 -0
  27. package/dist-node/package-root.d.ts +5 -0
  28. package/dist-node/package-root.d.ts.map +1 -0
  29. package/dist-node/package-root.js +17 -0
  30. package/dist-node/package-root.js.map +1 -0
  31. package/dist-node/payload.d.ts +22 -0
  32. package/dist-node/payload.d.ts.map +1 -0
  33. package/dist-node/payload.js +49 -0
  34. package/dist-node/payload.js.map +1 -0
  35. package/dist-node/pointer-hub.d.ts +22 -0
  36. package/dist-node/pointer-hub.d.ts.map +1 -0
  37. package/dist-node/pointer-hub.js +50 -0
  38. package/dist-node/pointer-hub.js.map +1 -0
  39. package/dist-node/publication.cli.d.ts +9 -0
  40. package/dist-node/publication.cli.d.ts.map +1 -0
  41. package/dist-node/publication.cli.js +17 -0
  42. package/dist-node/publication.cli.js.map +1 -0
  43. package/dist-node/publication.d.ts +23 -0
  44. package/dist-node/publication.d.ts.map +1 -0
  45. package/dist-node/publication.js +47 -0
  46. package/dist-node/publication.js.map +1 -0
  47. package/dist-node/reconcile.cli.d.ts +11 -0
  48. package/dist-node/reconcile.cli.d.ts.map +1 -0
  49. package/dist-node/reconcile.cli.js +39 -0
  50. package/dist-node/reconcile.cli.js.map +1 -0
  51. package/dist-node/reconcile.d.ts +30 -0
  52. package/dist-node/reconcile.d.ts.map +1 -0
  53. package/dist-node/reconcile.js +51 -0
  54. package/dist-node/reconcile.js.map +1 -0
  55. package/dist-node/ship/prompts/fix.md +22 -0
  56. package/dist-node/ship/prompts/implement.md +34 -0
  57. package/dist-node/ship/prompts/review.md +28 -0
  58. package/dist-node/ship/ship.agent.d.ts +22 -0
  59. package/dist-node/ship/ship.agent.d.ts.map +1 -0
  60. package/dist-node/ship/ship.agent.js +142 -0
  61. package/dist-node/ship/ship.agent.js.map +1 -0
  62. package/dist-node/ship/ship.args.d.ts +15 -0
  63. package/dist-node/ship/ship.args.d.ts.map +1 -0
  64. package/dist-node/ship/ship.args.js +93 -0
  65. package/dist-node/ship/ship.args.js.map +1 -0
  66. package/dist-node/ship/ship.context.d.ts +12 -0
  67. package/dist-node/ship/ship.context.d.ts.map +1 -0
  68. package/dist-node/ship/ship.context.js +3 -0
  69. package/dist-node/ship/ship.context.js.map +1 -0
  70. package/dist-node/ship/ship.d.ts +4 -0
  71. package/dist-node/ship/ship.d.ts.map +1 -0
  72. package/dist-node/ship/ship.js +221 -0
  73. package/dist-node/ship/ship.js.map +1 -0
  74. package/dist-node/ship/ship.mr.d.ts +9 -0
  75. package/dist-node/ship/ship.mr.d.ts.map +1 -0
  76. package/dist-node/ship/ship.mr.js +171 -0
  77. package/dist-node/ship/ship.mr.js.map +1 -0
  78. package/dist-node/ship/ship.output.d.ts +9 -0
  79. package/dist-node/ship/ship.output.d.ts.map +1 -0
  80. package/dist-node/ship/ship.output.js +21 -0
  81. package/dist-node/ship/ship.output.js.map +1 -0
  82. package/dist-node/ship/ship.tracker.d.ts +37 -0
  83. package/dist-node/ship/ship.tracker.d.ts.map +1 -0
  84. package/dist-node/ship/ship.tracker.js +220 -0
  85. package/dist-node/ship/ship.tracker.js.map +1 -0
  86. package/dist-node/version.cli.d.ts +9 -0
  87. package/dist-node/version.cli.d.ts.map +1 -0
  88. package/dist-node/version.cli.js +30 -0
  89. package/dist-node/version.cli.js.map +1 -0
  90. package/dist-node/version.d.ts +24 -0
  91. package/dist-node/version.d.ts.map +1 -0
  92. package/dist-node/version.js +76 -0
  93. package/dist-node/version.js.map +1 -0
  94. package/docs/adr/ADR-0000-proceso-y-formato-de-adrs.md +115 -0
  95. package/docs/adr/foundational/A-001-clean-architecture-4-capas.md +88 -0
  96. package/docs/adr/foundational/A-002-cqrs-semantico.md +58 -0
  97. package/docs/adr/foundational/A-003-tdd.md +97 -0
  98. package/docs/adr/foundational/A-005-either-return-custom-methods.md +80 -0
  99. package/docs/adr/foundational/A-006-feature-completion-criteria.md +47 -0
  100. package/docs/adr/foundational/A-007-data-contract-fail-fast.md +109 -0
  101. package/docs/adr/foundational/A-008-bounded-context-decomposition.md +130 -0
  102. package/docs/adr/foundational/A-009-use-case-taxonomy.md +99 -0
  103. package/docs/adr/foundational/A-010-propiedad-emision-eventos-dominio.md +95 -0
  104. package/docs/adr/foundational/A-011-characterization-first-refactoring.md +90 -0
  105. package/docs/adr/foundational/A-012-helm-mecanismo-primario-despliegue.md +67 -0
  106. package/docs/adr/foundational/A-013-contrato-settings-unica-fuente-configuracion.md +74 -0
  107. package/docs/adr/foundational/A-014-generacion-artefactos-despliegue-desde-contrato-settings.md +75 -0
  108. package/docs/adr/foundational/A-015-cloudevents-estandar-eventos.md +85 -0
  109. package/docs/adr/foundational/A-016-cicd-gitlab-pipeline-raiz-pipelines-modulares.md +92 -0
  110. package/docs/adr/foundational/A-017-taxonomia-repositorios-librerias-sistemas.md +93 -0
  111. package/docs/adr/foundational/A-018-practicas-build-imagenes-docker.md +85 -0
  112. package/docs/adr/foundational/A-019-convenciones-autoria-helm-charts.md +97 -0
  113. package/docs/adr/foundational/A-020-entorno-desarrollo-estandarizado-devcontainer.md +58 -0
  114. package/docs/adr/foundational/A-022-formato-reporte-tests-ctrf.md +55 -0
  115. package/docs/adr/foundational/A-023-composicion-bloques-prompt-agentes.md +65 -0
  116. package/docs/adr/foundational/A-024-features-slices-verticales-portables.md +41 -0
  117. package/docs/adr/foundational/A-025-anatomia-mecanica-feature-puertos-adaptadores-drivers.md +104 -0
  118. package/docs/adr/foundational/A-026-infraestructura-compartida.md +44 -0
  119. package/docs/adr/foundational/A-027-patron-wiring-entry-point.md +67 -0
  120. package/docs/adr/foundational/A-029-verbos-intencion-metodos-lado-negocio.md +54 -0
  121. package/docs/adr/foundational/A-030-global-development-tooling.md +52 -0
  122. package/docs/adr/foundational/A-031-ubiquitous-language.md +64 -0
  123. package/docs/adr/foundational/A-032-domain-artifact-documentation-format.md +128 -0
  124. package/docs/adr/foundational/A-033-estandar-mensajes-commit.md +127 -0
  125. package/docs/adr/foundational/A-034-canonical-skill-set.md +64 -0
  126. package/docs/adr/foundational/A-035-captura-ideas-reanclaje-candies.md +51 -0
  127. package/docs/adr/foundational/A-036-adopcion-wayfinder-ortogonalidad-labels.md +37 -0
  128. package/docs/adr/foundational/A-037-formalizacion-taxonomia-labels.md +98 -0
  129. package/docs/adr/foundational/A-038-issues-referencian-skills-nombre.md +54 -0
  130. package/docs/adr/foundational/A-039-pipeline-ship-issues-hijos-spec.md +64 -0
  131. package/docs/adr/foundational/A-040-punteros-configuracion-repo-skills.md +44 -0
  132. package/docs/adr/foundational/A-041-estrategia-de-ramas.md +45 -0
  133. package/docs/adr/foundational/A-042-formato-documentos-issue-tracker.md +78 -0
  134. package/docs/adr/foundational/A-043-vocabulario-etiquetas-adrs.md +42 -0
  135. package/docs/adr/foundational/A-044-composicion-funcional-procesos-operaciones.md +44 -0
  136. package/docs/adr/foundational/A-045-guardrails-aplicables-no-directrices.md +48 -0
  137. package/docs/adr/foundational/A-046-metricas-de-forma-y-legibilidad-del-codigo.md +50 -0
  138. package/docs/adr/foundational/A-047-dependencias-explicitas-sin-globales-ambientales.md +40 -0
  139. package/docs/adr/foundational/A-048-cobertura-minima-codigo.md +39 -0
  140. package/docs/adr/foundational/A-049-materializacion-bucle-guardrails-extension-nativa-pi.md +49 -0
  141. package/docs/adr/foundational/A-050-el-caso-de-uso-pide-al-dominio-no-lee-su-representacion.md +50 -0
  142. package/docs/adr/foundational/A-051-orden-imports-por-distancia.md +47 -0
  143. package/docs/adr/foundational/A-052-distribucion-metodologia-paquete-versionado.md +42 -0
  144. package/docs/adr/foundational/A-053-errores-compartidos-entre-features-dos-hogares-por-capa.md +43 -0
  145. package/docs/adr/foundational/A-054-contrato-costura-driver-port-raw-data-canal-error.md +65 -0
  146. package/docs/adr/foundational/index.md +98 -0
  147. package/docs/adr/index.md +19 -0
  148. package/docs/adr/python/P-001-dependencias-por-feature-fastapi-depends.md +63 -0
  149. package/docs/adr/python/P-002-inyeccion-token-por-request-sin-persistirlo.md +35 -0
  150. package/docs/adr/python/P-003-sqlalchemy-async-connection-pooling.md +59 -0
  151. package/docs/adr/python/P-004-serializer-json-custom-modelos-pydantic.md +47 -0
  152. package/docs/adr/python/P-005-patrones-pitfalls-nicegui.md +148 -0
  153. package/docs/adr/python/P-006-convenciones-pytest.md +96 -0
  154. package/docs/adr/python/P-007-ci-python-alineacion-poetry-version-unica.md +55 -0
  155. package/docs/adr/python/P-008-empaquetado-infraestructura-compartida-por-tecnologia.md +38 -0
  156. package/docs/adr/python/index.md +14 -0
  157. package/docs/adr/typescript/T-001-ddd-artifact-validation-zod-primitives.md +136 -0
  158. package/docs/adr/typescript/T-002-test-documentation-jsdoc.md +133 -0
  159. package/docs/adr/typescript/T-003-readme-formatting-domain-artifacts.md +51 -0
  160. package/docs/adr/typescript/T-005-input-optionality-use-case-not-entity.md +130 -0
  161. package/docs/adr/typescript/T-006-small-enums-inlined-zod-regex.md +77 -0
  162. package/docs/adr/typescript/T-007-controller-input-zod-schema.md +79 -0
  163. package/docs/adr/typescript/T-008-expressive-use-case-pipe.md +207 -0
  164. package/docs/adr/typescript/T-009-mdx-brace-escaping.md +80 -0
  165. package/docs/adr/typescript/T-010-stack-tooling-typescript-dominio.md +53 -0
  166. package/docs/adr/typescript/T-011-ultra-reporter-herramienta-canonica-documentacion.md +52 -0
  167. package/docs/adr/typescript/T-012-npm-workspaces-estructura-monorepo-clean-architecture.md +54 -0
  168. package/docs/adr/typescript/T-013-definicion-eventos-versionados-ultra-ddd.md +45 -0
  169. package/docs/adr/typescript/T-014-publicacion-distribucion-librerias-npm-semver.md +53 -0
  170. package/docs/adr/typescript/T-015-method-placement-definition.md +35 -0
  171. package/docs/adr/typescript/T-016-contrato-settings-zod.md +34 -0
  172. package/docs/adr/typescript/T-017-characterization-tests-golden-files.md +39 -0
  173. package/docs/adr/typescript/T-018-procedencia-campos-frontmatter.md +55 -0
  174. package/docs/adr/typescript/T-019-ultra-generator-herramienta-canonica-generacion.md +35 -0
  175. package/docs/adr/typescript/T-020-ultra-settings-herramienta-canonica-exportacion-despliegue.md +36 -0
  176. package/docs/adr/typescript/T-021-pipe-flujo-datos-operadores-semantica-contexto-inmutable.md +105 -0
  177. package/docs/adr/typescript/T-022-construction-is-execution-single-boundary.md +112 -0
  178. package/docs/adr/typescript/T-023-registry-cli-declara-dependencias-reales.md +84 -0
  179. package/docs/adr/typescript/T-024-paquete-ultra-agent-manifiesto-pi-peers-cli.md +39 -0
  180. package/docs/adr/typescript/T-025-publicacion-npm-trusted-publishing-procedencia.md +59 -0
  181. package/docs/adr/typescript/index.md +30 -0
  182. package/eslint/index.mjs +721 -0
  183. package/package.json +94 -0
  184. package/pi/extensions/handoff/README.md +92 -0
  185. package/pi/extensions/handoff/handoff-prompt.txt +9 -0
  186. package/pi/extensions/handoff/index.ts +296 -0
  187. package/pi/extensions/handoff/package.json +12 -0
  188. package/pi/extensions/qa-guardrail/README.md +108 -0
  189. package/pi/extensions/qa-guardrail/adapters/cli.ts +100 -0
  190. package/pi/extensions/qa-guardrail/adapters/eslint.ts +90 -0
  191. package/pi/extensions/qa-guardrail/adapters/registry.ts +45 -0
  192. package/pi/extensions/qa-guardrail/baseline.ts +74 -0
  193. package/pi/extensions/qa-guardrail/cerco.ts +134 -0
  194. package/pi/extensions/qa-guardrail/check.ts +185 -0
  195. package/pi/extensions/qa-guardrail/config.ts +189 -0
  196. package/pi/extensions/qa-guardrail/delta.ts +52 -0
  197. package/pi/extensions/qa-guardrail/feedback.ts +26 -0
  198. package/pi/extensions/qa-guardrail/index.ts +311 -0
  199. package/pi/extensions/qa-guardrail/nstop.ts +82 -0
  200. package/pi/extensions/qa-guardrail/package.json +12 -0
  201. package/pi/extensions/qa-guardrail/selection.ts +98 -0
  202. package/pi/extensions/qa-guardrail/types.ts +64 -0
  203. package/scripts/copy-assets.sh +10 -0
  204. package/scripts/reconcile-tags.sh +94 -0
  205. package/scripts/release-beta.sh +64 -0
  206. package/scripts/release-build.sh +53 -0
  207. package/scripts/release.sh +51 -0
  208. package/scripts/verify-adr-consistency.py +217 -0
  209. package/skills/code-review/SKILL.md +66 -0
  210. package/skills/codebase-design/SKILL.md +46 -0
  211. package/skills/configure-repo/SKILL.md +33 -0
  212. package/skills/diagnose-bug/SKILL.md +63 -0
  213. package/skills/domain-modeling/SKILL.md +56 -0
  214. package/skills/grill-me/SKILL.md +34 -0
  215. package/skills/i-have-an-idea/SKILL.md +166 -0
  216. package/skills/implement/SKILL.md +22 -0
  217. package/skills/research/SKILL.md +17 -0
  218. package/skills/ship/SKILL.md +26 -0
  219. package/skills/spike/SKILL.md +27 -0
  220. package/skills/tdd/SKILL.md +68 -0
  221. package/skills/tdd/references/deep-modules.md +53 -0
  222. package/skills/tdd/references/interface-design.md +74 -0
  223. package/skills/tdd/references/mocking.md +80 -0
  224. package/skills/tdd/references/refactoring.md +49 -0
  225. package/skills/tdd/references/tests.md +184 -0
  226. package/skills/to-specs/SKILL.md +141 -0
  227. package/skills/to-tasks/SKILL.md +143 -0
  228. package/skills/triage/SKILL.md +73 -0
  229. package/skills/ultra-generator/SKILL.md +263 -0
  230. package/skills/ultra-generator/references/aggregate-root.md +149 -0
  231. package/skills/ultra-generator/references/domain-event.md +174 -0
  232. package/skills/ultra-generator/references/entity.md +133 -0
  233. package/skills/ultra-generator/references/service.md +92 -0
  234. package/skills/ultra-generator/references/value-object.md +135 -0
  235. package/skills/wayfinder/SKILL.md +201 -0
  236. package/skills/workflow/SKILL.md +31 -0
  237. package/skills/writing-for-agents/SKILL-MECHANICS.md +32 -0
  238. package/skills/writing-for-agents/SKILL.md +86 -0
  239. package/skills/writing-for-agents/references/adrs.md +19 -0
@@ -0,0 +1,53 @@
1
+ # Deep Modules
2
+
3
+ Value-objects and aggregates are naturally **deep modules**: small public API, rich encapsulated behavior. The same lens applies to every code shape.
4
+
5
+ ## Deep = Value Object
6
+
7
+ ```
8
+ ┌───────────────────────┐
9
+ │ ProductName │
10
+ │ .of(payload) │ ← Small interface (1 factory + getters)
11
+ │ .getPrimitive() │
12
+ │ .getPayload() │
13
+ ├───────────────────────┤
14
+ │ Validation rules │
15
+ │ Length constraints │ ← Deep implementation (conditions, errors)
16
+ │ Type checking │
17
+ │ Error aggregation │
18
+ │ Zod transformations │
19
+ └───────────────────────┘
20
+ ```
21
+
22
+ ## Shallow = Primitive Obsession
23
+
24
+ ```
25
+ ┌─────────────────────────────────┐
26
+ │ createOrder(name, │
27
+ │ type, warehouse, productId, │
28
+ │ quantity, batch, state, ...) │ ← Large interface (many primitives)
29
+ ├─────────────────────────────────┤
30
+ │ if (name.length === 0) throw │ ← Thin implementation (inlined validation)
31
+ │ if (quantity <= 0) throw │
32
+ │ // ... │
33
+ └─────────────────────────────────┘
34
+ ```
35
+
36
+ ## When Designing Artifacts
37
+
38
+ Ask:
39
+ - Can I wrap this string/number in a value-object to encapsulate its rules?
40
+ - Can I reduce the number of conditions by composing value-objects?
41
+ - Can I hide the transformation logic inside the `Definition` class?
42
+ - Can the `.getPrimitive()` and `.getPayload()` methods provide clear, minimal interfaces?
43
+
44
+ ## Example: Deepen a Shallow Module
45
+
46
+ **Before (shallow)**: Order has `name: string`, `type: string`, `productId: string` — all inlined validation.
47
+
48
+ **After (deep)**: Order composes `OrderName`, `OrderType`, `ProductId` — each with its own encapsulated rules. The aggregate's interface shrinks but behavior becomes richer.
49
+
50
+ ## The Same Lens Across the Codebase
51
+
52
+ - **Use-cases are deep**: a small interface (input DTO → output DTO through the presenter) hiding rich orchestration. Keep the pipe steps internal; expose only `execute`.
53
+ - **Representations and adapters are shallow by nature**: read models are data + projection; adapters are translation. Depth is earned where behavior lives, not forced onto every module.
@@ -0,0 +1,74 @@
1
+ # Interface Design for Testability
2
+
3
+ These interface patterns make testing natural; they apply to every code shape:
4
+
5
+ ## 1. Factory Method Pattern (`of()`)
6
+
7
+ ```typescript
8
+ // The artifact's public API — already testable
9
+ const result = ProductName.of(payload);
10
+ if (result.isValid()) {
11
+ const name = result.getValue();
12
+ // use name
13
+ }
14
+ ```
15
+
16
+ The `Execution` monad (`of()` returns `Execution<Error, T>`) makes both success and failure paths testable without mocks.
17
+
18
+ ## 2. Return Results, Don't Produce Side Effects
19
+
20
+ ```typescript
21
+ // GOOD: Returns a new Execution — testable
22
+ ProductName.of('test') → Execution<ProductName>
23
+
24
+ // GOOD: getPrimitive returns a plain value — testable
25
+ productName.getPrimitive() → string
26
+ ```
27
+
28
+ ## 3. Values as Input, Values as Output
29
+
30
+ Domain artifacts are purely functional at the public API level:
31
+
32
+ - `of(payload)` — value in, Execution out
33
+ - `getPrimitive()` — value out
34
+ - `getPayload()` — value out
35
+ - `toJSON()` — value out
36
+
37
+ No internal state mutation exposed. This makes every test deterministic.
38
+
39
+ ## 4. Use-Case Interface (Ports as Seams)
40
+
41
+ A feature exposes a small, value-in/value-out interface too: `execute(input)` pushes an `InputDto` through the pipe and hands the result to a `PresenterPort`. The `QueryPort` (and any other port) is the seam where a test double plugs in:
42
+
43
+ ```typescript
44
+ // Input → execute → presenter result; query port is the seam
45
+ const useCase = new UseCase(presenter, new InMemoryQueryDriver());
46
+ const task = useCase.execute(input);
47
+ await task();
48
+ ```
49
+
50
+ The same rules as the factory: small surface, values in, values out, no hidden state.
51
+
52
+ ## 5. Dependency Injection for Services and Adapters
53
+
54
+ When a service or adapter needs external dependencies, use constructor injection:
55
+
56
+ ```typescript
57
+ class OrderService {
58
+ constructor(
59
+ private readonly policy: PricingPolicy,
60
+ private readonly orderRepo: OrderRepository,
61
+ ) {}
62
+ }
63
+ ```
64
+
65
+ This allows replacing `PricingPolicy` and `OrderRepository` with test doubles in tests.
66
+
67
+ ## 6. Small Surface Area per Artifact
68
+
69
+ Each artifact keeps a minimal public API — value-objects and aggregates with a factory + getters, use-cases with `execute` + DTOs:
70
+ - 1 factory (`of()`)
71
+ - 2-3 getters (`getPrimitive()`, `getPayload()`)
72
+ - Type exports (`Payload`, `Primitive`, `Errors`)
73
+
74
+ Fewer methods = fewer tests needed = simpler test setup.
@@ -0,0 +1,80 @@
1
+ # When to Mock
2
+
3
+ Mocks belong at system boundaries, never in domain or use-case logic. The same line applies to every code shape — domain artifacts, features, shared-infra, adapters.
4
+
5
+ ## Mock at System Boundaries Only
6
+
7
+ - External services (payment gateways, email, SMS)
8
+ - External context adapters (other bounded contexts)
9
+ - Database / EventStore (prefer in-memory or test instances)
10
+ - Time / randomness (`Date.now()`, `Math.random()`)
11
+ - File system
12
+
13
+ ## Don't Mock
14
+
15
+ - Value objects, entities, aggregates — use real instances
16
+ - `Definition` classes — test through the public artifact
17
+ - `conditions` — test through the value-object/aggregate's `of()`
18
+ - `Props`, `Payload`, `Primitive` types — just use the types
19
+ - Use-case orchestration / pipe steps — test through `execute(input)`
20
+ - Read-model mapping logic — test through the public query
21
+ - Internal collaborators within the domain
22
+
23
+ ## Domain Artifacts Are Naturally Testable
24
+
25
+ DDD value-objects and aggregates already follow good testing patterns:
26
+
27
+ ```typescript
28
+ // No mock needed — create real instances
29
+ const productName = ProductName.of('Valid Name').getValue();
30
+ const product = Product.of({ id: Uuid.generate(), version: 1 }).getValue();
31
+ ```
32
+
33
+ Because domain objects use the `Execution` pattern (`of()` returns `Execution<Error, Value>`), error cases are testable without mocking:
34
+
35
+ ```typescript
36
+ // Error path is testable without mocking
37
+ const result = ProductName.of('');
38
+ expect(result).toBeInvalid();
39
+ ```
40
+
41
+ ## Features / Use Cases
42
+
43
+ Mock the port behind the use-case, never the use-case itself. Drive it with an in-memory query/adapter and capture the presenter's result:
44
+
45
+ ```typescript
46
+ // In-memory driver implements QueryPort — no mock framework needed
47
+ const queryDriver = new InMemoryQueryDriver();
48
+ let captured: Execution<Errors, OutputDto[]> | null = null;
49
+ const presenter: PresenterPort = { setResult: (r) => { captured = r; } };
50
+
51
+ const useCase = new UseCase(presenter, queryDriver);
52
+ const task = useCase.execute({});
53
+ await task();
54
+ ```
55
+
56
+ ## Services / Adapters
57
+
58
+ For a service or adapter that calls an external dependency, use constructor injection and replace the dependency with a test double:
59
+
60
+ ```typescript
61
+ // Service accepts adapter as parameter (testable)
62
+ class OrderService {
63
+ constructor(private readonly orderRepo: OrderRepository) {}
64
+
65
+ async getTotal(orderId: OrderId): Promise<Quantity> {
66
+ const order = await this.orderRepo.getById(orderId);
67
+ return order.total;
68
+ }
69
+ }
70
+
71
+ // Test with test double
72
+ it('calculates the order total', async () => {
73
+ const mockRepo = {
74
+ getById: async () => ({ total: Quantity.of(10).getValue() }),
75
+ };
76
+ const service = new OrderService(mockRepo);
77
+ const result = await service.getTotal(orderId);
78
+ expect(result.getValue()).toBe(10);
79
+ });
80
+ ```
@@ -0,0 +1,49 @@
1
+ # Refactor Candidates
2
+
3
+ After a TDD cycle, look for these, by code shape:
4
+
5
+ ## Domain Artifacts
6
+
7
+ - **Duplicate conditions** → Extract into shared Zod schemas or value-objects
8
+ - **Long conditions** → Break complex `.refine()`, `.superRefine()` into named helpers
9
+ - **Primitive obsession** → If `string` appears with multiple distinct rules, introduce a value-object
10
+ - **Leaky errors** → Error messages should reference the domain field, not internal names
11
+ - **Feature envy in aggregates** → If an aggregate has many methods operating on a specific value-object's data, move the logic to the value-object
12
+ - **Unused error types** → Remove error classes that no conditions produce
13
+ - **Props duplication** → If multiple artifacts share the same computed property, extract to a shared module
14
+ - **Magic numbers** → Extract `MIN_LENGTH`, `MAX_LENGTH` constants
15
+ - **Repeated Zod chains** → Consider composing from smaller validators
16
+ - **Inline object shapes** → If multiple conditions share the same nested structure, create a named schema
17
+
18
+ ## Features / Use Cases
19
+
20
+ - **Long pipe** → Extract steps into named functions (T-008)
21
+ - **Fat use-case** → Move domain logic down to value-objects/aggregates; keep the use-case as orchestration (A-025)
22
+ - **Duplicated DTO mapping** → Extract a shared mapper instead of repeating input-DTO → domain translations in every use-case
23
+ - **Unused ports** → Remove ports no driver or adapter implements
24
+ - **Methods without intention verbs** → Rename per A-029
25
+
26
+ ## Representations
27
+
28
+ - **Duplicated projection logic** → Extract a shared projector instead of repeating the same shape across read models (A-002)
29
+ - **Fat read model** → Decompose a projection that mixes several queries into focused read models
30
+
31
+ ## Adapters / Drivers
32
+
33
+ - **Duplicated adapter logic** → If several features repeat the same external-call shape, extract a shared adapter
34
+ - **Wiring duplication** → Move repeated construction into the app factories instead of inlining it in drivers
35
+
36
+ ## Test Files
37
+
38
+ - **Duplicate test setup** → Extract payloads into `beforeEach` or helper functions
39
+ - **Overly specific error assertions** → Use `toHaveErrorLike()` with partial matching instead of full object comparison
40
+ - **Missing JSDoc** → Add `@name` and `@scenario` for test discoverability
41
+ - **Pending tests** → Use `todo()` and `it.todo()` for known gaps
42
+
43
+ ## Safety Rules
44
+
45
+ - [ ] Refactor only after GREEN (all tests passing)
46
+ - [ ] Run tests after each refactor step
47
+ - [ ] Don't change public API behavior during refactor
48
+ - [ ] If a refactor breaks tests, revert and try a smaller step
49
+ - [ ] New artifacts extracted during refactor get their own spec file
@@ -0,0 +1,184 @@
1
+ # Good and Bad Tests
2
+
3
+ The public-API-vs-internals line applies to every code shape — domain artifacts, features, shared-infra, adapters. Test the observable behavior through the public entry point, never the internals. The domain case is canonical; the same rule holds for the rest.
4
+
5
+ ## Domain Artifacts
6
+
7
+ ### Good Tests
8
+
9
+ **Test through the artifact's public API.**
10
+
11
+ ```typescript
12
+ // GOOD: Tests observable domain behavior
13
+ import { ProductName } from './value-object';
14
+ import { Payload } from './payload';
15
+
16
+ it('crear ProductName válido', () => {
17
+ const payload: Payload = 'Nombre de Empresa Valida';
18
+ const result = ProductName.of(payload);
19
+ expect(result).toBeValid();
20
+ expect(result.getValue()).toBeInstanceOf(ProductName);
21
+ });
22
+ ```
23
+
24
+ ```typescript
25
+ // GOOD: Tests domain rule violation
26
+ it('crear ProductName vacío', () => {
27
+ const payload: Payload = '';
28
+ const result = ProductName.of(payload);
29
+ expect(result).toBeInvalid();
30
+ expect(result).toBeInvalidWithError(TooSmallError);
31
+ });
32
+ ```
33
+
34
+ Characteristics:
35
+
36
+ - Tests domain rules users care about
37
+ - Uses the artifact's `of()` factory, `getValue()`, `getPrimitive()`
38
+ - Survives conditions refactors
39
+ - Verifies through the artifact's public API, not by importing conditions directly
40
+ - Uses the semantic matchers (A-003)
41
+
42
+ ### Bad Tests
43
+
44
+ **Implementation-detail tests**: Coupled to the `conditions` or `Definition` internals.
45
+
46
+ ```typescript
47
+ // BAD: Tests conditions directly instead of through the value-object
48
+ import { conditions } from './conditions';
49
+
50
+ it('validates string type', () => {
51
+ const result = conditions.safeParse('hello');
52
+ expect(result.success).toBe(true);
53
+ });
54
+ ```
55
+
56
+ ```typescript
57
+ // BAD: Tests the Definition class directly
58
+ import { Definition } from './definition';
59
+
60
+ it('definition getPrimitive works', () => {
61
+ const def = new Definition({ name: 'test' });
62
+ expect(def.getPrimitive()).toBe('test');
63
+ });
64
+ ```
65
+
66
+ ```typescript
67
+ // BAD: Bypasses the of() factory
68
+ it('creates with valid props', () => {
69
+ const vo = new ProductName({ name: 'Test' });
70
+ expect(vo.props.name).toBe('Test');
71
+ });
72
+ ```
73
+
74
+ Red flags:
75
+
76
+ - Importing `conditions.ts` directly in tests
77
+ - Testing `Definition` class directly
78
+ - Using `new ClassName()` instead of `ClassName.of()`
79
+ - Asserting on `props` internals
80
+ - Test breaks when you refactor conditions but behavior doesn't change
81
+ - Mocking `Definition` or `ValueObjectBase`
82
+
83
+ ### Correct Pattern: Test Through Public API
84
+
85
+ ```typescript
86
+ // GOOD: Full test of a value-object's domain rules
87
+ import '@imolko/ultra-result/test';
88
+ import { InvalidTypeError, TooBigError, TooSmallError } from '@imolko/ultra-v8n';
89
+ import { MAX_LENGTH } from './conditions';
90
+ import { ProductName } from './value-object';
91
+ import { Payload } from './payload';
92
+
93
+ describe('product-name', () => {
94
+ it('crear ProductName válido', () => {
95
+ const payload: Payload = 'Nombre de Empresa Valida';
96
+ const result = ProductName.of(payload);
97
+ expect(result).toBeValid();
98
+ });
99
+
100
+ it('Instancia de ProductName', () => {
101
+ const payload: Payload = 'Nombre de Empresa Valida';
102
+ const result = ProductName.of(payload);
103
+ expect(result.getValue()).toBeInstanceOf(ProductName);
104
+ });
105
+
106
+ it('crear ProductName con la cantidad maxima de caracteres', () => {
107
+ const payload: Payload = 'Nombre de Empresa larga exacta'.padEnd(MAX_LENGTH, 'o');
108
+ const result = ProductName.of(payload);
109
+ expect(result).toBeValid();
110
+ });
111
+
112
+ it('crear ProductName numérico', () => {
113
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
114
+ const payload: Payload = 3 as any;
115
+ const result = ProductName.of(payload);
116
+ expect(result).toBeInvalid();
117
+ expect(result).toBeInvalidWithError(InvalidTypeError);
118
+ expect(result.getErrors().length).toBe(1);
119
+ expect(result).toHaveErrorLike({
120
+ type: "http://journal/v8n/invalid_type",
121
+ title: "Invalid Type",
122
+ });
123
+ });
124
+
125
+ it('crear ProductName vacío', () => {
126
+ const payload: Payload = '';
127
+ const result = ProductName.of(payload);
128
+ expect(result).toBeInvalid();
129
+ expect(result).toBeInvalidWithError(TooSmallError);
130
+ });
131
+
132
+ it('Crear ProductName con mayor extensión de lo permitido', () => {
133
+ const payload: Payload = 'Nombre de Empresa muy larga'.padEnd(MAX_LENGTH + 1, 'o');
134
+ const result = ProductName.of(payload);
135
+ expect(result).toBeInvalid();
136
+ expect(result).toBeInvalidWithError(TooBigError);
137
+ });
138
+ });
139
+ ```
140
+
141
+ Note: `MAX_LENGTH` is imported from `conditions.ts` — this is acceptable because it's a constant used as test input, not testing conditions behavior. If the constant value changes, tests should adapt.
142
+
143
+ ## Features / Use Cases
144
+
145
+ The public entry point is `useCase.execute(input)`; the seams are the ports. Test through the entry point with in-memory ports and assert on the presenter's result.
146
+
147
+ ### Good
148
+
149
+ ```typescript
150
+ // GOOD: Tests the use-case through its public entry point
151
+ it('ejecuta el caso de uso y entrega el resultado al presentador', async () => {
152
+ const queryDriver = new InMemoryQueryDriver(); // implements QueryPort
153
+ let captured: Execution<Errors, OutputDto[]> | null = null;
154
+ const presenter: PresenterPort = { setResult: (r) => { captured = r; } };
155
+
156
+ const useCase = new UseCase(presenter, queryDriver);
157
+ const task = useCase.execute({});
158
+ await task();
159
+
160
+ expect(captured).toBeValid();
161
+ });
162
+ ```
163
+
164
+ ### Bad
165
+
166
+ ```typescript
167
+ // BAD: Tests a pipe step directly instead of through execute()
168
+ import { computeCutoffDate } from './use-case'; // internal helper
169
+
170
+ it('calcula la fecha de corte', () => {
171
+ expect(computeCutoffDate(10)).toBe(...);
172
+ });
173
+ ```
174
+
175
+ Red flags (features):
176
+
177
+ - Importing a pipe step or internal helper directly
178
+ - Calling a private method instead of `execute(input)`
179
+ - Asserting on the driver's internal state instead of the presenter's result
180
+ - Test breaks when you refactor the pipe but behavior doesn't change
181
+
182
+ ## Representations / Adapters
183
+
184
+ Same line: test the public query or port, not the internal mapping. A read model is tested through its public query and asserted on the returned shape; an adapter is tested through the port it implements, with the external side mocked.
@@ -0,0 +1,141 @@
1
+ ---
2
+ name: to-specs
3
+ description: Turn the current conversation into a spec and publish it as an issue — no interview, just synthesis.
4
+ disable-model-invocation: true
5
+ ---
6
+
7
+ # To-Specs
8
+
9
+ This skill takes the current conversation context and codebase understanding, produces a spec in the repo's issue language (`issue_language`) using the project's issue template, and publishes it to the issue tracker via `tracker_cli`.
10
+
11
+ Read `docs/agents/repo-config.md` for the repo pointers (`adr_dir`, `domain_dir`, `issue_language`, `tracker_cli`) — use them instead of hardcoded paths.
12
+
13
+ Reference: A-034 (its place in the canonical set), A-042 (spec structure), A-038 (spec `Skill recomendado` references skills by name), A-037 (labels), A-039 (spec → child issues).
14
+
15
+ Do NOT interview the user unnecessarily — synthesize what you already know from the conversation. Ask only when critical information is missing.
16
+
17
+ ## Process
18
+
19
+ ### 1. Explore the Codebase
20
+
21
+ Understand the current state of the repo. Use the project's domain glossary vocabulary throughout the spec, and respect any ADRs in the area the issue touches.
22
+
23
+ Look at:
24
+ - Existing artifacts in the relevant bounded context
25
+ - ADRs that may constrain the implementation
26
+ - Domain glossary terms that apply
27
+
28
+ ### 2. Sketch the seams
29
+
30
+ Identify the major modules that need to be built or modified, and the seams at which they'll be tested. Propose deep modules that can be tested in isolation — the `seam` / `deep` / `shallow` vocabulary is owned by `/skill:codebase-design`.
31
+
32
+ **Check with the user** that these seams match their expectations. Tests at those seams follow A-003 — every module gets tested, so there's nothing to select.
33
+
34
+ ### 3. Write the spec
35
+
36
+ Use the template below — the canonical spec structure is owned by A-042; this is its operational copy. Write in `issue_language` (default Spanish).
37
+
38
+ <spec-template>
39
+
40
+ # Skill recomendado
41
+
42
+ Name the skill(s) to use, by exact name (`/skill:<name>`) as the first instruction (A-038), and the next step in the flow. Example: `Para comenzar, usa /skill:tdd (→ /skill:ship).`
43
+
44
+ # Contexto
45
+
46
+ Explain the situation and general context for the issue. This is important to give the person that will work on the issue a general context and information to be productive really quickly.
47
+
48
+ # Alcance
49
+
50
+ Define the precise scope — short paragraphs on what is needed to close the issue.
51
+
52
+ # Criterio de cierre
53
+
54
+ Indicate in tasks what is the criteria for finishing this issue. Each task must have a clear focus and allows the worker to have a clear view of the goals required.
55
+
56
+ This section helps the person responsible for approving the issue (a different person from the implementer) to check if all the work has been done.
57
+
58
+ Use a task format for each point: one checkbox per criterion (checkbox-list format owned by A-042).
59
+
60
+ # Pruebas recomendadas
61
+
62
+ When the responsible for the issue completes the work, another person must check if it is OK.
63
+
64
+ In this section, provide a suggestion of minimum tests the reviewer must perform to verify the work. The reviewer can approve if all tests pass, or reject otherwise.
65
+
66
+ Each test should include a detailed list of steps and verifications to perform.
67
+
68
+ # Decisiones de implementación
69
+
70
+ A list of implementation decisions made. Include:
71
+
72
+ - The modules that will be built or modified
73
+ - The interfaces of those modules
74
+ - Technical clarifications
75
+ - Architectural decisions
76
+ - Schema changes
77
+ - API contracts
78
+ - Specific interactions
79
+
80
+ Record decisions in prose — it stays accurate longer than file paths or code snippets. A spike snippet that encodes a decision more precisely than prose (state machine, reducer, schema, type shape) may be inlined, noted as from a spike.
81
+
82
+ # Decisiones de testing
83
+
84
+ A description of the testing approach:
85
+
86
+ - Which seams will be tested and why
87
+ - What makes a good test for this issue — public API over internals (A-003)
88
+ - Prior art for the tests (similar types of tests in the codebase)
89
+
90
+ # Fuera de alcance
91
+
92
+ A description of things that are explicitly out of scope for this spec.
93
+
94
+ # Notas adicionales
95
+
96
+ Any further notes about the issue.
97
+
98
+ </spec-template>
99
+
100
+ ### 4. Publish to the issue tracker
101
+
102
+ Publish the spec as an issue using `tracker_cli` (default `glab`). Before publishing:
103
+
104
+ 1. Show a preview of the issue to the user
105
+ 2. Ask for confirmation
106
+ 3. On confirmation, create the issue:
107
+
108
+ ```bash
109
+ <tracker_cli> issue create \
110
+ --title "<TITULO_DEL_ISSUE>" \
111
+ --description "<CUERPO_DEL_ISSUE>" \
112
+ --label "state::ready"
113
+ ```
114
+
115
+ **Important:** The description body must be properly escaped for the shell. Use a temporary file approach for long descriptions:
116
+
117
+ ```bash
118
+ # Write the body to a temp file
119
+ cat > /tmp/issue-body.md << 'ISSUE_EOF'
120
+ <CUERPO_DEL_ISSUE>
121
+ ISSUE_EOF
122
+
123
+ # Create the issue from the file
124
+ <tracker_cli> issue create \
125
+ --title "<TITULO_DEL_ISSUE>" \
126
+ --description "$(cat /tmp/issue-body.md)" \
127
+ --label "state::ready"
128
+ ```
129
+
130
+ The issue title should be concise and descriptive in `issue_language`. Apply the `state::ready` label automatically (label taxonomy: A-037).
131
+
132
+ ### 5. Confirm
133
+
134
+ After creating the issue, show the user:
135
+ - The issue URL
136
+ - The issue number
137
+ - A summary of what was created
138
+
139
+ ## Rules
140
+
141
+ The spec's formatting rules are owned by A-042 — read and apply them from there, not restated here.