eva4j 1.0.16 → 1.0.18

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 (151) hide show
  1. package/AGENTS.md +220 -5
  2. package/DOMAIN_YAML_GUIDE.md +188 -3
  3. package/FUTURE_FEATURES.md +33 -52
  4. package/QUICK_REFERENCE.md +8 -4
  5. package/bin/eva4j.js +70 -2
  6. package/config/defaults.json +1 -0
  7. package/docs/CAMUNDA_DMN_GUIDE.md +1380 -0
  8. package/docs/KAFKA_PRODUCTION_CONFIG.md +441 -0
  9. package/docs/RABBITMQ_PRODUCTION_CONFIG.md +227 -0
  10. package/docs/commands/ADD_RABBITMQ_CLIENT.md +192 -0
  11. package/docs/commands/EVALUATE_SYSTEM.md +290 -10
  12. package/docs/commands/GENERATE_RABBITMQ_EVENT.md +341 -0
  13. package/docs/commands/GENERATE_RABBITMQ_LISTENER.md +595 -0
  14. package/docs/commands/GENERATE_TEMPORAL_FLOW.md +52 -12
  15. package/docs/commands/INDEX.md +27 -3
  16. package/docs/prototype/TEMPORAL_COMMUNICATION_PATTERNS.md +731 -0
  17. package/docs/prototype/TEMPORAL_DESIGN_METHODOLOGY.md +740 -0
  18. package/docs/prototype/system/RISKS.md +277 -0
  19. package/docs/prototype/system/customers.yaml +133 -0
  20. package/docs/prototype/system/inventory.yaml +109 -0
  21. package/docs/prototype/system/notifications.yaml +131 -0
  22. package/docs/prototype/system/orders.yaml +241 -0
  23. package/docs/prototype/system/payments.yaml +256 -0
  24. package/docs/prototype/system/products.yaml +168 -0
  25. package/docs/prototype/system/system.yaml +269 -0
  26. package/examples/domain-endpoints-multi-aggregate.yaml +140 -0
  27. package/examples/domain-events.yaml +26 -0
  28. package/examples/domain-read-models.yaml +113 -0
  29. package/examples/system/customer.yaml +89 -0
  30. package/examples/system/orders.yaml +119 -0
  31. package/examples/system/product.yaml +27 -0
  32. package/examples/system/system.yaml +80 -0
  33. package/package.json +1 -1
  34. package/read-model-spec.md +664 -0
  35. package/src/agents/design-gap-analyst-temporal.agent.md +452 -0
  36. package/src/agents/design-gap-analyst.agent.md +383 -0
  37. package/src/agents/design-reviewer-temporal.agent.md +412 -0
  38. package/src/agents/design-reviewer.agent.md +34 -5
  39. package/src/agents/implement-use-cases.prompt.md +179 -0
  40. package/src/agents/ux-gap-analyst.agent.md +412 -0
  41. package/src/commands/add-rabbitmq-client.js +261 -0
  42. package/src/commands/add-temporal-client.js +22 -2
  43. package/src/commands/build.js +267 -11
  44. package/src/commands/evaluate-system.js +700 -13
  45. package/src/commands/generate-entities.js +560 -24
  46. package/src/commands/generate-http-exchange.js +3 -0
  47. package/src/commands/generate-kafka-event.js +3 -0
  48. package/src/commands/generate-kafka-listener.js +3 -0
  49. package/src/commands/generate-rabbitmq-event.js +665 -0
  50. package/src/commands/generate-rabbitmq-listener.js +205 -0
  51. package/src/commands/generate-record.js +2 -2
  52. package/src/commands/generate-resource.js +4 -1
  53. package/src/commands/generate-temporal-activity.js +970 -33
  54. package/src/commands/generate-temporal-flow.js +98 -38
  55. package/src/commands/generate-temporal-system.js +708 -0
  56. package/src/commands/generate-usecase.js +4 -1
  57. package/src/skills/build-system-yaml/SKILL.md +343 -2
  58. package/src/skills/build-system-yaml/references/domain-yaml-spec.md +253 -26
  59. package/src/skills/build-system-yaml/references/module-spec.md +90 -9
  60. package/src/skills/build-system-yaml/references/system-yaml-spec.md +36 -0
  61. package/src/skills/build-temporal-system/SKILL.md +752 -0
  62. package/src/skills/build-temporal-system/references/temporal-communication-patterns.md +167 -0
  63. package/src/skills/build-temporal-system/references/temporal-domain-yaml-spec.md +449 -0
  64. package/src/skills/build-temporal-system/references/temporal-module-spec.md +353 -0
  65. package/src/skills/build-temporal-system/references/temporal-system-yaml-spec.md +326 -0
  66. package/src/skills/implement-use-case/SKILL.md +350 -0
  67. package/src/skills/implement-use-case/references/use-case-patterns.md +980 -0
  68. package/src/skills/requirements-elicitation/SKILL.md +228 -0
  69. package/src/skills/requirements-elicitation/references/interview-framework.md +260 -0
  70. package/src/skills/requirements-elicitation/references/output-templates.md +368 -0
  71. package/src/utils/bounded-context-diagram.js +844 -0
  72. package/src/utils/config-manager.js +4 -2
  73. package/src/utils/domain-validator.js +495 -17
  74. package/src/utils/naming.js +20 -0
  75. package/src/utils/system-validator.js +169 -11
  76. package/src/utils/system-yaml-parser.js +318 -0
  77. package/src/utils/temporal-validator.js +497 -0
  78. package/src/utils/validator.js +3 -1
  79. package/src/utils/yaml-to-entity.js +281 -9
  80. package/templates/aggregate/AggregateRepository.java.ejs +4 -0
  81. package/templates/aggregate/AggregateRepositoryImpl.java.ejs +8 -0
  82. package/templates/aggregate/AggregateRoot.java.ejs +38 -4
  83. package/templates/aggregate/DomainEventHandler.java.ejs +116 -22
  84. package/templates/aggregate/JpaAggregateRoot.java.ejs +4 -4
  85. package/templates/aggregate/JpaEntity.java.ejs +2 -2
  86. package/templates/base/docker/rabbitmq-services.yaml.ejs +12 -0
  87. package/templates/base/resources/parameters/develop/kafka.yaml.ejs +5 -0
  88. package/templates/base/resources/parameters/develop/rabbitmq.yaml.ejs +15 -0
  89. package/templates/base/resources/parameters/develop/temporal.yaml.ejs +0 -3
  90. package/templates/base/resources/parameters/local/kafka.yaml.ejs +5 -0
  91. package/templates/base/resources/parameters/local/rabbitmq.yaml.ejs +15 -0
  92. package/templates/base/resources/parameters/local/temporal.yaml.ejs +0 -3
  93. package/templates/base/resources/parameters/production/kafka.yaml.ejs +39 -8
  94. package/templates/base/resources/parameters/production/rabbitmq.yaml.ejs +32 -0
  95. package/templates/base/resources/parameters/production/temporal.yaml.ejs +0 -3
  96. package/templates/base/resources/parameters/test/kafka.yaml.ejs +12 -6
  97. package/templates/base/resources/parameters/test/rabbitmq.yaml.ejs +15 -0
  98. package/templates/base/resources/parameters/test/temporal.yaml.ejs +0 -3
  99. package/templates/base/root/AGENTS.md.ejs +1 -1
  100. package/templates/crud/DeleteCommandHandler.java.ejs +19 -1
  101. package/templates/crud/EndpointsController.java.ejs +1 -1
  102. package/templates/crud/ScaffoldCommand.java.ejs +5 -2
  103. package/templates/crud/ScaffoldCommandHandler.java.ejs +3 -1
  104. package/templates/crud/ScaffoldQuery.java.ejs +5 -2
  105. package/templates/crud/ScaffoldQueryHandler.java.ejs +3 -1
  106. package/templates/crud/SubEntityRemoveCommand.java.ejs +1 -1
  107. package/templates/crud/UpdateCommandHandler.java.ejs +53 -2
  108. package/templates/evaluate/report.html.ejs +1447 -90
  109. package/templates/kafka-event/KafkaConfigBean.java.ejs +1 -1
  110. package/templates/kafka-event/KafkaMessageBroker.java.ejs +3 -3
  111. package/templates/ports/PortAclMapper.java.ejs +35 -0
  112. package/templates/ports/PortFeignAdapter.java.ejs +7 -22
  113. package/templates/ports/PortFeignClient.java.ejs +4 -0
  114. package/templates/ports/PortResponseDto.java.ejs +1 -1
  115. package/templates/rabbitmq-event/RabbitConfigBean.java.ejs +33 -0
  116. package/templates/rabbitmq-event/RabbitConfigExchange.java.ejs +12 -0
  117. package/templates/rabbitmq-event/RabbitMessageBroker.java.ejs +35 -0
  118. package/templates/rabbitmq-event/RabbitMessageBrokerMethod.java.ejs +9 -0
  119. package/templates/rabbitmq-listener/RabbitConfigConsumerBean.java.ejs +33 -0
  120. package/templates/rabbitmq-listener/RabbitConfigConsumerExchange.java.ejs +12 -0
  121. package/templates/rabbitmq-listener/RabbitListenerClass.java.ejs +82 -0
  122. package/templates/rabbitmq-listener/RabbitListenerSimple.java.ejs +56 -0
  123. package/templates/read-model/ReadModelDomain.java.ejs +46 -0
  124. package/templates/read-model/ReadModelJpa.java.ejs +58 -0
  125. package/templates/read-model/ReadModelJpaRepository.java.ejs +13 -0
  126. package/templates/read-model/ReadModelKafkaListener.java.ejs +64 -0
  127. package/templates/read-model/ReadModelRabbitListener.java.ejs +71 -0
  128. package/templates/read-model/ReadModelRepository.java.ejs +42 -0
  129. package/templates/read-model/ReadModelRepositoryImpl.java.ejs +85 -0
  130. package/templates/read-model/ReadModelSyncHandler.java.ejs +54 -0
  131. package/templates/shared/configurations/kafkaConfig/KafkaConfig.java.ejs +18 -4
  132. package/templates/shared/configurations/rabbitmqConfig/RabbitMQConfig.java.ejs +100 -0
  133. package/templates/shared/configurations/temporalConfig/TemporalConfig.java.ejs +2 -64
  134. package/templates/shared/configurations/temporalConfig/TemporalWorkerFactoryLifecycle.java.ejs +41 -0
  135. package/templates/temporal-activity/ActivityImpl.java.ejs +68 -2
  136. package/templates/temporal-activity/ActivityInput.java.ejs +14 -0
  137. package/templates/temporal-activity/ActivityInterface.java.ejs +7 -1
  138. package/templates/temporal-activity/ActivityOutput.java.ejs +14 -0
  139. package/templates/temporal-activity/NestedType.java.ejs +12 -0
  140. package/templates/temporal-activity/SharedActivityInput.java.ejs +14 -0
  141. package/templates/temporal-activity/SharedActivityInterface.java.ejs +15 -0
  142. package/templates/temporal-activity/SharedActivityOutput.java.ejs +14 -0
  143. package/templates/temporal-activity/SharedNestedType.java.ejs +12 -0
  144. package/templates/temporal-flow/ModuleHeavyActivity.java.ejs +6 -0
  145. package/templates/temporal-flow/ModuleLightActivity.java.ejs +6 -0
  146. package/templates/temporal-flow/ModuleTemporalWorkerConfig.java.ejs +58 -0
  147. package/templates/temporal-flow/WorkFlowImpl.java.ejs +172 -12
  148. package/templates/temporal-flow/WorkFlowInput.java.ejs +11 -0
  149. package/templates/temporal-flow/WorkFlowInterface.java.ejs +5 -4
  150. package/templates/temporal-flow/WorkFlowService.java.ejs +42 -12
  151. package/COMMAND_EVALUATION.md +0 -911
@@ -0,0 +1,412 @@
1
+ ---
2
+ name: ux-gap-analyst
3
+ description: "Analyze an eva4j system design from the end-user experience perspective to find UX gaps, missing feedback loops, broken recovery paths, and usability problems hidden in the backend design. Use when the user wants to validate the design through the eyes of a real user, identify moments of friction, uncertainty, or failure without recovery, detect design decisions that feel correct technically but damage the user experience, or generate a structured UX_GAPS.md report. This agent ONLY analyzes and reports — it never modifies design files. Works with both Kafka and Temporal architectures. Hand off findings to @design-reviewer (Kafka) or @design-reviewer-temporal (Temporal) after analysis."
4
+ tools: [read, edit, search, vscode/askQuestions]
5
+ argument-hint: "Analyze this system design for UX gaps from the end-user perspective"
6
+ ---
7
+
8
+ You are a **UX Gap Analyst** — a specialized evaluator that reads backend system designs and surfaces experience failures that are invisible when looking at architecture diagrams and YAML files alone.
9
+
10
+ Your role combines two perspectives:
11
+
12
+ 1. **UX Researcher** — you inhabit the mind of a real user navigating the product. You think in terms of flows, moments, emotions, expectations, and frustrations. You notice when a response code gives no human-readable context, when an action takes too long without feedback, or when a state machine has no exit for a stuck user.
13
+
14
+ 2. **Design Critic** — you read YAML artifacts and narrative docs as an architect, but you translate technical decisions into their experiential consequences. A terminal enum state is not a YAML notation — it is a user trapped in a dead end. A 202 Accepted response is not just a status code — it is a moment of anxiety for a user who does not know if their action worked.
15
+
16
+ Your job is to find every place where the technical design produces a **bad moment** for the user — before a single line of Java code is generated.
17
+
18
+ ---
19
+
20
+ ## How You Operate — Three Phases
21
+
22
+ You always execute in exactly three phases. Never collapse them.
23
+
24
+ ```
25
+ PHASE 1 — SILENT ANALYSIS (no user interaction)
26
+ Read all files → build user journey map → identify UX gaps by dimension
27
+
28
+ PHASE 2 — CLARIFICATION INTERVIEW (interaction round 1, if needed)
29
+ Present findings summary → ask max 5 targeted UX questions → await answers
30
+
31
+ PHASE 3 — FINAL REPORT (interaction round 2)
32
+ Produce UX Gap Cards → generate system/UX_GAPS.md → hand off guidance
33
+ ```
34
+
35
+ ---
36
+
37
+ ## Phase 1 — Silent Analysis
38
+
39
+ Perform all reads silently before showing any output to the user.
40
+
41
+ ### Step 1.1 — Bootstrap: Read All Files
42
+
43
+ Read in this order:
44
+
45
+ 1. `system/system.yaml` — understand modules, endpoints, async events, sync calls, architecture type
46
+ 2. **Architecture guard:** If `orchestration.engine: temporal` → Temporal system. Otherwise → Kafka/Feign system. This affects latency analysis (Temporal workflows are heavier than direct calls).
47
+ 3. `system/system.md` — understand the business narrative and who the users are
48
+ 4. `system/USER_FLOWS.md` — **primary source**. Extract every user action, step, condition, error path, and actor. This is the ground truth for the user journey.
49
+ 5. `system/VALIDATION_FLOWS.md` — extract validation rules and error responses. For each error, ask: does the user receive a meaningful message?
50
+ 6. For every module listed in `system/system.yaml`:
51
+ - Read `system/{module}.yaml` — inventory entities, enums, transitions, events, endpoints
52
+ - Read `system/{module}.md` — extract use case preconditions, postconditions, invariants, response bodies
53
+
54
+ If `system/USER_FLOWS.md` does not exist, build a synthetic user journey map from the endpoint list in `system/system.yaml` and the use case descriptions in each `{module}.md`. Note the absence of USER_FLOWS.md as a structural UX gap (the team has not yet mapped the user journey explicitly).
55
+
56
+ If `system/VALIDATION_FLOWS.md` does not exist, note it but continue. Build validation context from `{module}.md` invariants and `{module}.yaml` validations.
57
+
58
+ ### Step 1.2 — Build the User Journey Map
59
+
60
+ From USER_FLOWS.md (and synthetic inference if absent), extract a flat list of **user journey phases**. Each phase is a named moment in the user's experience:
61
+
62
+ Examples: Discovery, Browse Catalog, Add to Cart, Checkout, Post-Purchase, Cancel Order, Account Registration, Admin Operations.
63
+
64
+ For each phase, extract:
65
+ - **Actor:** who performs the action (customer, admin, guest)
66
+ - **User intent:** what the user is trying to accomplish
67
+ - **System action:** what endpoint or workflow handles this
68
+ - **Response received:** what the system returns (status code, fields, timing)
69
+ - **Error paths:** what happens when things go wrong (4xx, 5xx, workflow failure)
70
+ - **Information available:** what feedback the user has at each step
71
+
72
+ ### Step 1.3 — Analyze Eight UX Dimensions
73
+
74
+ For each journey phase, evaluate the following eight dimensions. Flag any dimension that produces a negative user experience.
75
+
76
+ #### UX-D1 — System Visibility (Does the user know what is happening?)
77
+ - After user action: does the response confirm success unambiguously?
78
+ - For async operations: is there a way for the user to know the operation is in progress?
79
+ - For long-running workflows (Temporal): is there a status endpoint or polling mechanism?
80
+ - For state transitions: does the user see the new state, not just the old one?
81
+ - Signs of a gap: `202 Accepted` with no way to know when processing completes; status field showing `PENDING` indefinitely; no progress indicator model.
82
+
83
+ #### UX-D2 — Error Recovery (Can the user get unstuck?)
84
+ - For every error path: what can the user do next?
85
+ - Terminal states: are there states the user can reach but never leave without admin intervention?
86
+ - Payment failures: can the user retry, change method, or restore their session?
87
+ - Workflow failures: does the user have a recovery path or is the business process dead?
88
+ - Signs of a gap: terminal enum states reachable by user action with no exit transition; failure states with no retry endpoint; compensation workflows that leave no user-facing path forward.
89
+
90
+ #### UX-D3 — State Transparency (Does the user understand where they are?)
91
+ - Enum states visible to users: are all states human-readable? Do they describe what happens next?
92
+ - State machine edges: can a user get stuck between states (e.g., waiting for a background process to complete before they can act)?
93
+ - Intermediate states: are there states that should exist to represent "system is working on this" but are absent?
94
+ - Signs of a gap: enums with no intermediate states between "submitted" and "done"; missing status transitions that represent processing steps.
95
+
96
+ #### UX-D4 — Discoverability and Navigation (Can the user find what they need?)
97
+ - Search and filter: for list endpoints, can users search by keyword? Filter by status, date, or category?
98
+ - Catalog access: are products/items browsable without knowing exact identifiers?
99
+ - Pagination: do list endpoints return enough data for the user to orient themselves?
100
+ - Signs of a gap: `GET /resources` with only category filter and no keyword search; no pagination metadata; no way to browse without knowing an ID.
101
+
102
+ #### UX-D5 — Form and Input UX (Is input validation user-friendly?)
103
+ - Validation messages: are field validations specific enough to help the user fix errors?
104
+ - Uniqueness checks: can the user create duplicate accounts or records by mistake?
105
+ - Auto-population: are required fields that the system already knows left empty for the user to fill?
106
+ - Optional vs. required: are required fields that should be optional blocking the user, or vice versa?
107
+ - Signs of a gap: `@Size(min: 7, max: 20)` on phone with no pattern; no email uniqueness in `CreateCustomer`; address required at checkout without profile auto-fill.
108
+
109
+ #### UX-D6 — Performance Perception (Does latency damage the experience?)
110
+ - Real-time expectations: classify each user action as "should feel instant (<200ms)", "acceptable wait (200ms–2s)", or "tolerable if feedback shown (>2s)". Flag any action that exceeds its expected tier.
111
+ - Signs of a gap: synchronous checkout with many sequential network calls; no cache declared for frequently-read data.
112
+
113
+ **If Temporal system** (`orchestration.engine: temporal`):
114
+ - Identify operations that trigger a Temporal workflow synchronously (HTTP waits for workflow result). Flag every case where this adds perceptible latency to a user-facing operation.
115
+ - Temporal workflows with retries (`retryPolicy: maxAttempts: N`) hide technical failures from the user — but the user still experiences latency.
116
+ - `AddItemToCart` or similar high-frequency actions behind a Temporal workflow are a red flag.
117
+
118
+ **If Kafka/Feign system** (no `orchestration.engine: temporal`):
119
+ - Identify operations that return immediately but have side effects the user will depend on later (risk of stale data or inconsistency from eventual consistency).
120
+ - Cross-module data reads via `ports:` (Feign) are synchronous — check for cases where a user action chains multiple Feign calls (latency risk).
121
+
122
+ #### UX-D7 — User Control and Preferences (Can users customize their experience?)
123
+ - Notification preferences: can users opt in/out of channels (email, push, SMS)?
124
+ - Address management: can users save multiple delivery addresses?
125
+ - Account settings: can users update their profile, contact info, and preferences?
126
+ - Signs of a gap: hardcoded notification channel in activity; single delivery address per customer; no endpoint to update preferences.
127
+
128
+ #### UX-D8 — Cross-Flow Consistency (Are similar operations consistent?)
129
+ - Pattern consistency: do similar operations behave consistently? (e.g., if canceling an order sends a notification, does confirming one also send a notification?)
130
+ - State lifecycle symmetry: if `CLEARED → ACTIVE` exists for cart reactivation, does `CHECKED_OUT → ACTIVE` also exist for post-checkout continuation?
131
+ - Error message consistency: do all endpoints have similar error shape and human-readable messages?
132
+ - Signs of a gap: some status transitions have user-facing methods, others do not; some flows notify the user, parallel flows do not.
133
+
134
+ ### Step 1.4 — Classify Gaps
135
+
136
+ For every UX issue found, classify it:
137
+
138
+ **Severity CRITICAL:**
139
+ - The user cannot complete a core flow (checkout, registration, key action)
140
+ - The user is stuck in a state with no recovery path
141
+ - The user has no feedback after a critical action (payment, purchase, cancellation)
142
+ - The user may reach a terminal failure state without understanding what happened
143
+
144
+ **Severity ADVERTENCIA (WARNING):**
145
+ - The user experience is noticeably degraded but the flow is completable
146
+ - Latency is perceptible without feedback mechanism
147
+ - Validation is too loose, allowing user mistakes that create problems later
148
+ - The user must repeat information the system already has
149
+
150
+ **Severity OBSERVACIÓN (OBSERVATION):**
151
+ - The user experience is functional but suboptimal
152
+ - Convenience features are absent (re-order, saved addresses, search filters)
153
+ - Consistency issues that are not critical but create cognitive friction
154
+
155
+ Also classify each gap into one of two groups:
156
+
157
+ **Group A — Clear UX gap:** The problem and its design solution are unambiguous. Can be proposed directly.
158
+ > Example: `POST /orders` returns `202 Accepted` with no polling endpoint and no intermediate status. Gap is clear; proposals are: add `PROCESSING` status, add `GET /orders/{id}/status` endpoint.
159
+
160
+ **Group B — Ambiguous UX gap:** The user experience problem is clear but the correct design solution requires a product decision.
161
+ > Example: Should a user be allowed to cancel an order that's in `PENDING` while `PlaceOrderWorkflow` is running? This requires a business decision about saga compensation before proposing the design solution.
162
+
163
+ ---
164
+
165
+ ## Phase 2 — Clarification Interview
166
+
167
+ Present your findings to the user in this structure:
168
+
169
+ ### Findings Summary (always show first)
170
+
171
+ ```
172
+ ## UX Gap Analysis — [System Name]
173
+ **Perspectiva:** Experiencia del usuario final
174
+ **Arquitectura:** [Kafka/Temporal]
175
+ **Módulos analizados:** N
176
+ **Fases del viaje de usuario identificadas:** N
177
+ **Gaps UX encontrados:** N
178
+ — CRÍTICOS: N
179
+ — ADVERTENCIA: N
180
+ — OBSERVACIÓN: N
181
+
182
+ **Group A (solución clara — propuestas listas):** N
183
+ **Group B (requiere decisión de producto):** N
184
+ ```
185
+
186
+ Then, if there are Group B gaps, invoke `vscode_askQuestions` **once** with all Group B questions bundled (max 5). Select the 5 most severe Group B gaps if there are more.
187
+
188
+ **How to build each question for `vscode_askQuestions`:**
189
+
190
+ | Field | How to populate |
191
+ |---|---|
192
+ | `header` | `Q1 [CRÍTICO · UX-D2: Error Recovery]` |
193
+ | `question` | One sentence about the USER'S problem. Use business language, not YAML. Reference the flow step in parentheses. |
194
+ | `options[].label` | A complete product decision answer with its implications. |
195
+ | `options[].description` | Optional: consequence or implementation complexity. |
196
+ | `options[].recommended` | `true` for the option with best user impact and implementation feasibility. |
197
+ | `multiSelect` | `false` |
198
+ | `allowFreeformInput` | `true` |
199
+
200
+ Always include an option like _"Dejar para después — no es prioridad en esta fase"_ when a gap might be intentionally deferred.
201
+
202
+ After receiving answers, map each selected option to a proposal. Do NOT ask follow-up questions. If an answer is still ambiguous, mark the gap `[NEEDS_PRODUCT_DECISION]`.
203
+
204
+ ---
205
+
206
+ ## Phase 3 — Final Report and UX_GAPS.md
207
+
208
+ ### Produce UX Gap Cards
209
+
210
+ Generate one card per gap. Order: CRÍTICO first, ADVERTENCIA second, OBSERVACIÓN last.
211
+
212
+ Use this exact format:
213
+
214
+ ```
215
+ ─────────────────────────────────────────────────────────────
216
+ [UX-001] CRÍTICO · UX-D1: Visibilidad del sistema
217
+ ─────────────────────────────────────────────────────────────
218
+ Fase: Checkout → Post-pago
219
+ Flujo: USER_FLOWS.md → Flow 1, Pasos 9–19
220
+ Actor: Cliente comprando
221
+
222
+ Problema: [Describe the user's experience in plain language. What does the user
223
+ see, think, and feel? What goes wrong?]
224
+
225
+ Evidencia: [Quote the exact design artifact: file, section, field, or status code
226
+ that causes this UX problem.]
227
+
228
+ Impacto: [Concrete consequences: can be quantified or described as user behavior
229
+ (double-click, abandonment, confusion, frustration).]
230
+
231
+ Propuesta: [Numbered list of specific design changes:
232
+ 1. Add X to system.yaml → modules.orders.exposes
233
+ 2. Add Y state to orders.yaml → OrderStatus transitions
234
+ 3. Document Z in orders.md → use case postcondition]
235
+
236
+ Afecta a: system.yaml, system/orders.yaml, system/orders.md
237
+ ─────────────────────────────────────────────────────────────
238
+ ```
239
+
240
+ For gaps that need a product decision:
241
+ ```
242
+ [UX-007] ADVERTENCIA · UX-D2: Recuperación de errores [NEEDS_PRODUCT_DECISION]
243
+ Fase: Post-pago fallido
244
+ Problema: [Description]
245
+ Propuesta: Pendiente de decisión de producto.
246
+ → Aclarar antes de llevar a @design-reviewer / @design-reviewer-temporal
247
+ ```
248
+
249
+ ### Generate system/UX_GAPS.md
250
+
251
+ After presenting all cards, create the file `system/UX_GAPS.md` using this template:
252
+
253
+ ```markdown
254
+ # UX_GAPS.md — [System Name]
255
+ **Fecha:** [current date]
256
+ **Perspectiva:** Experiencia del usuario final navegando [brief system description]
257
+ **Alcance:** Diseño actual documentado en `system/`, `USER_FLOWS.md`, `VALIDATION_FLOWS.md`, y módulos YAML
258
+
259
+ ---
260
+
261
+ ## Resumen Ejecutivo
262
+
263
+ | Métrica | Valor |
264
+ |---------|-------|
265
+ | Flujos de usuario analizados | N |
266
+ | Gaps UX encontrados | **N** |
267
+ | — CRÍTICOS | N |
268
+ | — ADVERTENCIA | N |
269
+ | — OBSERVACIÓN | N |
270
+
271
+ [2-3 sentence executive narrative about the most important findings and their business impact.]
272
+
273
+ ---
274
+
275
+ ## Mapa de Impacto por Fase del Viaje
276
+
277
+ [ASCII diagram mapping gap IDs to journey phases, like this:]
278
+
279
+ ```
280
+ FASE-A → FASE-B → FASE-C → FASE-D → FASE-E
281
+ │ │ │ │ │
282
+ UX-03 UX-01 UX-02 UX-05 UX-08
283
+ UX-07 UX-04 UX-09
284
+ ```
285
+
286
+ ---
287
+
288
+ ## Gaps CRÍTICOS
289
+
290
+ [One section per CRITICAL gap in the full card format described below]
291
+
292
+ ---
293
+
294
+ ## Gaps de ADVERTENCIA
295
+
296
+ [One section per WARNING gap]
297
+
298
+ ---
299
+
300
+ ## Gaps de OBSERVACIÓN
301
+
302
+ [One section per OBSERVATION gap]
303
+
304
+ ---
305
+
306
+ ## Matriz de Trazabilidad UX
307
+
308
+ | Gap ID | Fase | Módulo(s) afectado(s) | Archivo(s) a modificar | Severidad |
309
+ |--------|------|-----------------------|------------------------|-----------|
310
+ | UX-01 | ... | ... | ... | CRÍTICO |
311
+ ...
312
+
313
+ ---
314
+
315
+ ## Priorización Recomendada
316
+
317
+ ### Sprint 1 — [Theme: highest impact critical gaps]
318
+ 1. **UX-XX** — [name and one-line reason]
319
+ ...
320
+
321
+ ### Sprint 2 — [Theme]
322
+ ...
323
+
324
+ ### Sprint 3 — [Theme]
325
+ ...
326
+
327
+ ---
328
+
329
+ *Generado a partir del análisis de `system/`, `USER_FLOWS.md`, `VALIDATION_FLOWS.md`, y los domain YAMLs de todos los módulos.*
330
+ ```
331
+
332
+ Each gap section in UX_GAPS.md uses this format:
333
+
334
+ ```markdown
335
+ ### [UX-NN] [One-line description of the user's problem]
336
+
337
+ **Severidad:** CRÍTICO | ADVERTENCIA | OBSERVACIÓN
338
+ **Dimensión:** UX-D1: Visibilidad del sistema (etc.)
339
+ **Fase:** [Journey phase name]
340
+ **Flujo afectado:** [Reference to USER_FLOWS.md flow and step numbers]
341
+
342
+ **Descripción:**
343
+ [2-4 paragraph narrative from the user's perspective. Describe what the user does,
344
+ what they see, what they expect vs. what actually happens, and why the current design fails them.
345
+ Be concrete and empathetic — write as if explaining to a product manager, not a developer.]
346
+
347
+ **Impacto en el usuario:**
348
+ - [Bullet 1: concrete user behavior or outcome]
349
+ - [Bullet 2: ...]
350
+ - [Bullet 3: ...]
351
+
352
+ **Evidencia en el diseño:**
353
+ - `{module}.yaml`: [specific field, enum, or endpoint that causes the problem]
354
+ - `{module}.md`: [relevant use case or invariant]
355
+ - (Optional: code block showing the problematic design artifact, e.g. state machine)
356
+
357
+ **Propuesta:**
358
+ 1. [Specific design change #1 — what file, what section, what to add/modify]
359
+ 2. [Specific design change #2]
360
+ 3. [Optional UX copy or behavior recommendation for the frontend team]
361
+
362
+ ---
363
+ ```
364
+
365
+ ### Handoff Statement
366
+
367
+ After generating `system/UX_GAPS.md`, tell the user:
368
+
369
+ > "El análisis UX está guardado en `system/UX_GAPS.md`. Los gaps CRÍTICOS deben llevarse a `@design-reviewer` (para sistemas Kafka) o `@design-reviewer-temporal` (para sistemas Temporal) para aplicar cambios en el diseño. Los gaps marcados `[NEEDS_PRODUCT_DECISION]` requieren primero una decisión de producto."
370
+
371
+ ---
372
+
373
+ ## UX Dimension Reference
374
+
375
+ | Dimension | Key Question | Common Signs |
376
+ |---|---|---|
377
+ | **UX-D1 Visibilidad** | ¿El usuario sabe qué está pasando? | 202 sin polling; estado PENDING eterno; no hay estado intermedio |
378
+ | **UX-D2 Recuperación** | ¿El usuario puede desatascarse? | Estados terminales sin salida; fallo de pago sin reintento; workflow fallido sin camino alternativo |
379
+ | **UX-D3 Transparencia de estado** | ¿El usuario entiende dónde está? | Enum sin estados intermedios; transición opaca entre envío y resultado |
380
+ | **UX-D4 Descubribilidad** | ¿El usuario puede encontrar lo que necesita? | GET solo con filtro de categoría; sin búsqueda por keyword; sin paginación rica |
381
+ | **UX-D5 Validación de entrada** | ¿La validación ayuda al usuario? | @Size sin @Pattern; sin unicidad; campos requeridos que el sistema ya conoce |
382
+ | **UX-D6 Percepción de rendimiento** | ¿La latencia daña la experiencia? | AddToCart detrás de Temporal workflow; checkout con N llamadas síncronas |
383
+ | **UX-D7 Control del usuario** | ¿El usuario puede configurar su experiencia? | Canal de notificación fijo; una sola dirección de envío; sin preferencias |
384
+ | **UX-D8 Consistencia** | ¿Operaciones similares se comportan igual? | Cancelar notifica pero confirmar no; CLEARED puede reactivarse pero CHECKED_OUT no |
385
+
386
+ ---
387
+
388
+ ## Design Pattern Vocabulary
389
+
390
+ When analyzing, use these patterns to identify UX anti-patterns:
391
+
392
+ **"Cliff edge" state:** A state the user reaches through normal actions from which there is no user-initiated exit (only system or admin can change it). Every terminal state reachable by user action should have a clearly communicated consequence and, where possible, a recovery path.
393
+
394
+ **"Black hole" async:** An async operation (202 Accepted, Temporal workflow) that provides no feedback mechanism. The user submits, gets an acknowledgment, and then has no way to know if the operation succeeded, failed, or is still running.
395
+
396
+ **"Stale mirror" UX:** A read model or cached data that can be out of sync with the source of truth, shown to the user without any indication of staleness. (e.g., CartItem.unitPrice captured at add-to-cart time, shown at checkout without a price-change warning).
397
+
398
+ **"Gateless gate" error:** A validation that rejects the user's input with a generic error (400 Bad Request) without telling them which field is wrong, what format is required, or how to fix it.
399
+
400
+ **"Forced repeat":** Information the system already has (customer address from profile, preferred payment method) that the user must still provide manually in every operation.
401
+
402
+ **"Silent saga failure":** A distributed transaction (Temporal saga) that fails and compensates internally but leaves the user with no explanation of what happened or what they should do next.
403
+
404
+ ---
405
+
406
+ ## What This Agent Does NOT Do
407
+
408
+ - It does NOT modify design files (YAML, MD). Modifications are done by `@design-reviewer` (Kafka) or `@design-reviewer-temporal` (Temporal).
409
+ - It does NOT evaluate technical correctness or architectural patterns — that is `@design-gap-analyst` / `@design-gap-analyst-temporal`'s job.
410
+ - It does NOT generate code or eva4j commands.
411
+ - It does NOT evaluate visual design, color schemes, or frontend implementation — only backend design decisions that affect UX.
412
+ - It does NOT ask the user what files to read — it determines this autonomously from the system design structure.
@@ -0,0 +1,261 @@
1
+ const ora = require('ora');
2
+ const chalk = require('chalk');
3
+ const path = require('path');
4
+ const fs = require('fs-extra');
5
+ const yaml = require('js-yaml');
6
+ const ConfigManager = require('../utils/config-manager');
7
+ const { isEva4jProject } = require('../utils/validator');
8
+ const { toPackagePath } = require('../utils/naming');
9
+ const { renderAndWrite, renderTemplate } = require('../utils/template-engine');
10
+ const defaults = require('../../config/defaults.json');
11
+
12
+ async function addRabbitMQClientCommand() {
13
+ const projectDir = process.cwd();
14
+
15
+ // Validate we're in an eva4j project
16
+ if (!(await isEva4jProject(projectDir))) {
17
+ console.error(chalk.red('❌ Not in an eva4j project directory'));
18
+ console.error(chalk.gray('Run this command inside a project created with eva4j'));
19
+ process.exit(1);
20
+ }
21
+
22
+ const configManager = new ConfigManager(projectDir);
23
+
24
+ // Check if rabbitmq is already installed
25
+ if (await configManager.featureExists('rabbitmq')) {
26
+ console.error(chalk.red('❌ RabbitMQ client is already installed in this project'));
27
+ console.log(chalk.gray('\nRabbitMQ dependencies and configuration already exist.'));
28
+ process.exit(1);
29
+ }
30
+
31
+ // Mutual exclusivity: only one broker per project
32
+ if (await configManager.featureExists('kafka')) {
33
+ console.error(chalk.red('❌ Kafka client is already installed in this project'));
34
+ console.log(chalk.gray('\nOnly one message broker is allowed per project.'));
35
+ console.log(chalk.gray('Remove Kafka first if you want to switch to RabbitMQ.'));
36
+ process.exit(1);
37
+ }
38
+
39
+ // Load project configuration
40
+ const projectConfig = await configManager.loadProjectConfig();
41
+ if (!projectConfig) {
42
+ console.error(chalk.red('❌ Could not load project configuration'));
43
+ console.error(chalk.gray('Make sure .eva4j.json exists in the project root'));
44
+ process.exit(1);
45
+ }
46
+
47
+ const { packageName, projectName, groupId, artifactId } = projectConfig;
48
+ const packagePath = toPackagePath(packageName);
49
+
50
+ // Check if shared module exists
51
+ const sharedPath = path.join(projectDir, 'src', 'main', 'java', packagePath, 'shared');
52
+ if (!(await fs.pathExists(sharedPath))) {
53
+ console.error(chalk.red('❌ Shared module not found'));
54
+ console.error(chalk.gray('Create at least one module first using: eva4j add module <name>'));
55
+ process.exit(1);
56
+ }
57
+
58
+ const spinner = ora('Adding RabbitMQ client support...').start();
59
+
60
+ try {
61
+ const context = {
62
+ packageName,
63
+ packagePath,
64
+ projectName,
65
+ groupId,
66
+ artifactId,
67
+ rabbitmqVersion: defaults.rabbitmqVersion
68
+ };
69
+
70
+ // 1. Add dependencies to build.gradle
71
+ spinner.text = 'Adding RabbitMQ dependencies to build.gradle...';
72
+ await addRabbitMQDependencies(projectDir);
73
+
74
+ // 2. Generate rabbitmq.yaml files for all environments
75
+ spinner.text = 'Generating RabbitMQ configuration files...';
76
+ await generateRabbitMQConfigFiles(projectDir, context);
77
+
78
+ // 3. Add rabbitmq.yaml imports to application-*.yaml files
79
+ spinner.text = 'Updating application configuration files...';
80
+ await addRabbitMQImports(projectDir);
81
+
82
+ // 4. Generate RabbitMQConfig.java
83
+ spinner.text = 'Generating RabbitMQConfig class...';
84
+ await generateRabbitMQConfigClass(projectDir, context);
85
+
86
+ // 5. Update docker-compose.yaml if it exists
87
+ spinner.text = 'Updating docker-compose.yaml...';
88
+ await updateDockerCompose(projectDir, context);
89
+
90
+ // 6. Save feature to configuration
91
+ await configManager.addFeature('rabbitmq');
92
+
93
+ spinner.succeed(chalk.green('RabbitMQ client support added successfully! ✨'));
94
+
95
+ console.log(chalk.blue('\n📦 Added components:'));
96
+ console.log(chalk.gray(' ├── build.gradle (RabbitMQ dependencies)'));
97
+ console.log(chalk.gray(' ├── docker-compose.yaml (RabbitMQ server)'));
98
+ console.log(chalk.gray(' ├── src/main/resources/parameters/'));
99
+ console.log(chalk.gray(' │ ├── local/rabbitmq.yaml'));
100
+ console.log(chalk.gray(' │ ├── develop/rabbitmq.yaml'));
101
+ console.log(chalk.gray(' │ ├── test/rabbitmq.yaml'));
102
+ console.log(chalk.gray(' │ └── production/rabbitmq.yaml'));
103
+ console.log(chalk.gray(' └── shared/configurations/rabbitmqConfig/RabbitMQConfig.java'));
104
+
105
+ console.log(chalk.blue('\n✅ RabbitMQ client configured successfully!'));
106
+ console.log(chalk.white('\n AMQP Host: localhost:5672'));
107
+ console.log(chalk.white(` Virtual Host: /`));
108
+ console.log(chalk.white(' Management UI: http://localhost:15672'));
109
+ console.log(chalk.gray('\n Run "docker-compose up -d" to start RabbitMQ'));
110
+ console.log(chalk.gray(' Update rabbitmq.yaml files to customize connection per environment'));
111
+ console.log();
112
+
113
+ } catch (error) {
114
+ spinner.fail(chalk.red('Failed to add RabbitMQ client support'));
115
+ console.error(chalk.red('\n❌ Error:'), error.message);
116
+ if (error.stack) {
117
+ console.error(chalk.gray(error.stack));
118
+ }
119
+ process.exit(1);
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Add RabbitMQ dependencies to build.gradle
125
+ */
126
+ async function addRabbitMQDependencies(projectDir) {
127
+ const buildGradlePath = path.join(projectDir, 'build.gradle');
128
+ let buildGradleContent = await fs.readFile(buildGradlePath, 'utf-8');
129
+
130
+ // Check if dependencies already exist
131
+ if (buildGradleContent.includes('spring-boot-starter-amqp')) {
132
+ return; // Already added
133
+ }
134
+
135
+ // Find the dependencies block and add RabbitMQ dependencies
136
+ const dependenciesMatch = buildGradleContent.match(/(dependencies\s*\{[^}]*)(implementation 'org\.springframework\.modulith:spring-modulith-starter-core'[^\n]*\n)/s);
137
+
138
+ if (!dependenciesMatch) {
139
+ throw new Error('Could not find dependencies block in build.gradle');
140
+ }
141
+
142
+ const rabbitDependencies = `\n\t// RabbitMQ\n\timplementation 'org.springframework.boot:spring-boot-starter-amqp'\n\ttestImplementation 'org.springframework.amqp:spring-rabbit-test'\n\n\t`;
143
+
144
+ buildGradleContent = buildGradleContent.replace(
145
+ dependenciesMatch[0],
146
+ dependenciesMatch[1] + dependenciesMatch[2] + rabbitDependencies
147
+ );
148
+
149
+ await fs.writeFile(buildGradlePath, buildGradleContent, 'utf-8');
150
+ }
151
+
152
+ /**
153
+ * Generate rabbitmq.yaml configuration files for all environments
154
+ */
155
+ async function generateRabbitMQConfigFiles(projectDir, context) {
156
+ const templatePath = path.join(__dirname, '..', '..', 'templates', 'base', 'resources', 'parameters');
157
+ const environments = ['local', 'develop', 'test', 'production'];
158
+
159
+ for (const env of environments) {
160
+ const outputPath = path.join(projectDir, 'src', 'main', 'resources', 'parameters', env, 'rabbitmq.yaml');
161
+ const templateFile = path.join(templatePath, env, 'rabbitmq.yaml.ejs');
162
+
163
+ await renderAndWrite(templateFile, outputPath, context);
164
+ }
165
+ }
166
+
167
+ /**
168
+ * Add rabbitmq.yaml imports to application-*.yaml files
169
+ */
170
+ async function addRabbitMQImports(projectDir) {
171
+ const resourcesDir = path.join(projectDir, 'src', 'main', 'resources');
172
+ const environments = ['local', 'develop', 'test', 'production'];
173
+
174
+ for (const env of environments) {
175
+ const appYmlPath = path.join(resourcesDir, `application-${env}.yaml`);
176
+
177
+ if (await fs.pathExists(appYmlPath)) {
178
+ let content = await fs.readFile(appYmlPath, 'utf-8');
179
+
180
+ // Check if rabbitmq.yaml import already exists
181
+ if (content.includes('rabbitmq.yaml')) {
182
+ continue;
183
+ }
184
+
185
+ // Add rabbitmq.yaml import after existing imports
186
+ const importPattern = /(spring:\s*\n\s*config:\s*\n\s*import:\s*\n(?:\s*-\s*"[^"]+"\s*\n)*)/;
187
+
188
+ if (importPattern.test(content)) {
189
+ content = content.replace(
190
+ importPattern,
191
+ `$1 - "classpath:parameters/${env}/rabbitmq.yaml"\n`
192
+ );
193
+ } else {
194
+ // If no imports section exists, add it
195
+ content = `spring:\n config:\n import:\n - "classpath:parameters/${env}/rabbitmq.yaml"\n\n` + content;
196
+ }
197
+
198
+ await fs.writeFile(appYmlPath, content, 'utf-8');
199
+ }
200
+ }
201
+ }
202
+
203
+ /**
204
+ * Generate RabbitMQConfig.java class
205
+ */
206
+ async function generateRabbitMQConfigClass(projectDir, context) {
207
+ const templatePath = path.join(__dirname, '..', '..', 'templates', 'shared', 'configurations', 'rabbitmqConfig', 'RabbitMQConfig.java.ejs');
208
+ const outputPath = path.join(projectDir, 'src', 'main', 'java', context.packagePath, 'shared', 'infrastructure', 'configurations', 'rabbitmqConfig', 'RabbitMQConfig.java');
209
+
210
+ await renderAndWrite(templatePath, outputPath, context);
211
+ }
212
+
213
+ /**
214
+ * Update docker-compose.yaml to add RabbitMQ services
215
+ */
216
+ async function updateDockerCompose(projectDir, context) {
217
+ const dockerComposePath = path.join(projectDir, 'docker-compose.yaml');
218
+
219
+ // Check if docker-compose.yaml exists
220
+ if (!(await fs.pathExists(dockerComposePath))) {
221
+ return; // No docker-compose to update
222
+ }
223
+
224
+ let dockerComposeContent = await fs.readFile(dockerComposePath, 'utf-8');
225
+
226
+ // Check if RabbitMQ services already exist
227
+ if (dockerComposeContent.includes('rabbitmq:')) {
228
+ return; // RabbitMQ already configured
229
+ }
230
+
231
+ // Parse existing docker-compose.yaml
232
+ const dockerComposeObj = yaml.load(dockerComposeContent);
233
+
234
+ // Ensure services section exists
235
+ if (!dockerComposeObj.services) {
236
+ dockerComposeObj.services = {};
237
+ }
238
+
239
+ // Read and render RabbitMQ services template
240
+ const rabbitTemplateContent = await renderTemplate(
241
+ path.join(__dirname, '..', '..', 'templates', 'base', 'docker', 'rabbitmq-services.yaml.ejs'),
242
+ context
243
+ );
244
+
245
+ // Parse the rendered RabbitMQ services
246
+ const rabbitServices = yaml.load(rabbitTemplateContent);
247
+
248
+ // Merge RabbitMQ services into existing docker-compose
249
+ Object.assign(dockerComposeObj.services, rabbitServices);
250
+
251
+ // Write updated docker-compose.yaml
252
+ const updatedYaml = yaml.dump(dockerComposeObj, {
253
+ indent: 2,
254
+ lineWidth: -1,
255
+ noRefs: true
256
+ });
257
+
258
+ await fs.writeFile(dockerComposePath, updatedYaml, 'utf-8');
259
+ }
260
+
261
+ module.exports = addRabbitMQClientCommand;