@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,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.
|