bug_bunny 4.8.0 → 4.8.1

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 (51) hide show
  1. checksums.yaml +4 -4
  2. data/.agents/skills/documentation-writer/SKILL.md +45 -0
  3. data/.agents/skills/gem-release/SKILL.md +114 -0
  4. data/.agents/skills/quality-code/SKILL.md +51 -0
  5. data/.agents/skills/sentry/SKILL.md +135 -0
  6. data/.agents/skills/sentry/references/api-endpoints.md +147 -0
  7. data/.agents/skills/sentry/scripts/sentry.rb +194 -0
  8. data/.agents/skills/skill-builder/SKILL.md +232 -0
  9. data/.agents/skills/skill-manager/SKILL.md +172 -0
  10. data/.agents/skills/skill-manager/scripts/sync.rb +310 -0
  11. data/.agents/skills/yard/SKILL.md +311 -0
  12. data/.agents/skills/yard/references/tipos.md +144 -0
  13. data/CHANGELOG.md +8 -0
  14. data/CLAUDE.md +28 -231
  15. data/lib/bug_bunny/version.rb +1 -1
  16. data/skill/SKILL.md +230 -0
  17. data/skill/references/client-middleware.md +144 -0
  18. data/skill/references/consumer.md +104 -0
  19. data/skill/references/controller.md +105 -0
  20. data/skill/references/errores.md +97 -0
  21. data/skill/references/resource.md +116 -0
  22. data/skill/references/routing.md +82 -0
  23. data/skill/references/testing.md +138 -0
  24. data/skills.lock +24 -0
  25. data/skills.yml +19 -0
  26. metadata +24 -28
  27. data/.claude/commands/gem-ai-setup.md +0 -174
  28. data/.claude/commands/pr.md +0 -53
  29. data/.claude/commands/release.md +0 -52
  30. data/.claude/commands/rubocop.md +0 -22
  31. data/.claude/commands/service-ai-setup.md +0 -168
  32. data/.claude/commands/test.md +0 -28
  33. data/.claude/commands/yard.md +0 -46
  34. data/docs/_index.md +0 -50
  35. data/docs/ai/_index.md +0 -56
  36. data/docs/ai/antipatterns.md +0 -166
  37. data/docs/ai/api.md +0 -251
  38. data/docs/ai/architecture.md +0 -92
  39. data/docs/ai/errors.md +0 -158
  40. data/docs/ai/faq_external.md +0 -133
  41. data/docs/ai/faq_internal.md +0 -86
  42. data/docs/ai/glossary.md +0 -45
  43. data/docs/concepts.md +0 -140
  44. data/docs/howto/controller.md +0 -194
  45. data/docs/howto/middleware_client.md +0 -119
  46. data/docs/howto/middleware_consumer.md +0 -127
  47. data/docs/howto/rails.md +0 -214
  48. data/docs/howto/resource.md +0 -200
  49. data/docs/howto/routing.md +0 -133
  50. data/docs/howto/testing.md +0 -259
  51. data/docs/howto/tracing.md +0 -119
@@ -0,0 +1,144 @@
1
+ # YARD — Catálogo completo de Type Specifications
2
+
3
+ Los tipos se especifican entre corchetes `[Type]` en tags como `@param`, `@return`, `@yield`, etc.
4
+
5
+ ## Tipos básicos
6
+
7
+ | Tipo | Descripción |
8
+ |---|---|
9
+ | `String` | Clase Ruby estándar |
10
+ | `Integer` | Clase Ruby estándar |
11
+ | `Float` | Clase Ruby estándar |
12
+ | `Symbol` | Clase Ruby estándar |
13
+ | `Boolean` | Convención YARD: `TrueClass` o `FalseClass` |
14
+ | `nil` | Valor nil literal |
15
+ | `void` | Sin valor de retorno significativo |
16
+ | `self` | Retorna self (métodos encadenables) |
17
+ | `true` | Literal true |
18
+ | `false` | Literal false |
19
+
20
+ ## Union types (múltiples tipos)
21
+
22
+ Separados por comas dentro de los corchetes:
23
+
24
+ ```
25
+ [String, Symbol] → String o Symbol
26
+ [Integer, nil] → Integer o nil
27
+ [String, Symbol, nil] → cualquiera de los tres
28
+ [Boolean, nil] → true, false o nil
29
+ ```
30
+
31
+ ## Parametrized types (generics)
32
+
33
+ Sintaxis: `Collection<ElementType>`
34
+
35
+ ```
36
+ [Array<String>] → array de strings
37
+ [Array<String, Symbol>] → array de strings y/o symbols
38
+ [Set<Integer>] → set de integers
39
+ [Hash<Symbol, String>] → hash con keys symbol, values string
40
+ [Hash<Symbol, Array<Integer>>] → hash con values que son arrays de integers
41
+ [Enumerator<User>] → enumerator de users
42
+ ```
43
+
44
+ ## Hash con estructura explícita
45
+
46
+ Sintaxis: `Hash{KeyType => ValueType}`
47
+
48
+ ```
49
+ [Hash{Symbol => String}] → hash con keys symbol, values string
50
+ [Hash{String => Object}] → hash con keys string, values cualquiera
51
+ [Hash{Symbol => Array<String>}] → hash con values que son arrays de strings
52
+ [Hash{String, Symbol => Integer}] → keys string o symbol, values integer
53
+ ```
54
+
55
+ ## Duck types
56
+
57
+ Prefijo `#` indica que el objeto responde a ese método:
58
+
59
+ ```
60
+ [#read] → cualquier objeto con método #read
61
+ [#call] → cualquier callable (Proc, Lambda, etc.)
62
+ [#to_s] → cualquier objeto convertible a string
63
+ [#read, #close] → debe responder a ambos métodos
64
+ [#each] → cualquier enumerable
65
+ ```
66
+
67
+ ## Order-dependent lists
68
+
69
+ Sintaxis: `Collection(Type1, Type2, ...)` — exactamente esos tipos en ese orden:
70
+
71
+ ```
72
+ [Array(String, Integer)] → array de exactamente 2 elementos: [string, integer]
73
+ [Array(String, Integer, Hash)] → array de exactamente 3 elementos en ese orden
74
+ ```
75
+
76
+ ## Combinaciones comunes
77
+
78
+ ```
79
+ [String, nil] → string nullable
80
+ [Array<String>] → array de strings
81
+ [Hash{Symbol => Object}] → options hash
82
+ [Boolean] → true/false
83
+ [void] → sin retorno
84
+ [self] → chainable
85
+ [#read, #write] → IO-like
86
+ [Integer, Float] → numeric
87
+ [String, Symbol] → string-like identifier
88
+ [Array<Hash{Symbol => String}>] → array de hashes
89
+ [Hash{Symbol => String, nil}] → hash con values nullable
90
+ ```
91
+
92
+ ## Patrones por contexto
93
+
94
+ ### Métodos de búsqueda
95
+ ```ruby
96
+ # @return [User, nil] el usuario o nil si no existe
97
+ def find(id); end
98
+
99
+ # @return [User] el usuario
100
+ # @raise [RecordNotFound] si no existe
101
+ def find!(id); end
102
+ ```
103
+
104
+ ### Métodos booleanos
105
+ ```ruby
106
+ # @return [Boolean] true si el usuario es admin
107
+ def admin?; end
108
+ ```
109
+
110
+ ### Métodos de mutación
111
+ ```ruby
112
+ # @return [void]
113
+ def save!; end
114
+
115
+ # @return [self] para encadenar
116
+ def where(conditions); end
117
+ ```
118
+
119
+ ### Métodos de colección
120
+ ```ruby
121
+ # @return [Array<User>] lista de usuarios
122
+ def all; end
123
+
124
+ # @yield [user] itera sobre cada usuario
125
+ # @yieldparam user [User]
126
+ # @return [Enumerator<User>] si no se pasa bloque
127
+ def each(&block); end
128
+ ```
129
+
130
+ ### Métodos con opciones
131
+ ```ruby
132
+ # @param opts [Hash{Symbol => Object}] opciones
133
+ # @option opts [Integer] :limit (10) máximo de resultados
134
+ # @option opts [Integer] :offset (0) desde dónde empezar
135
+ # @option opts [Symbol] :order (:asc) dirección del ordenamiento
136
+ def search(query, **opts); end
137
+ ```
138
+
139
+ ### Callbacks y Procs
140
+ ```ruby
141
+ # @param callback [Proc, #call] bloque a ejecutar
142
+ # @param filter [Proc<User, Boolean>] filtro que recibe user y retorna boolean
143
+ def on_create(callback); end
144
+ ```
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.8.1] - 2026-04-04
4
+
5
+ ### Mejoras internas
6
+ * **Skills System:** Migración completa del sistema de documentación AI de `docs/` y `.claude/commands/` al nuevo estándar de skills. La documentación AI ahora se distribuye como `skill/SKILL.md` empaquetada en la gema, con 7 archivos de referencia detallados en `skill/references/`.
7
+ * **CLAUDE.md simplificado:** Se eliminaron ~230 líneas de instrucciones hardcodeadas. `CLAUDE.md` ahora delega el conocimiento a las skills en `.agents/skills/` y `skill/`.
8
+ * **Gemspec:** `documentation_uri` actualizado de `docs` a `skill/` para apuntar a la ubicación correcta de la documentación.
9
+ * **Skills de desarrollo:** Se incorporan 7 skills locales en `.agents/skills/` (documentation-writer, gem-release, quality-code, sentry, skill-builder, skill-manager, yard) con `skills.yml` como manifiesto de dependencias.
10
+
3
11
  ## [4.8.0] - 2026-04-02
4
12
 
5
13
  ### ✨ AI Documentation Standard (v4.3)
data/CLAUDE.md CHANGED
@@ -7,234 +7,31 @@ BugBunny es una gema Ruby que implementa una capa de enrutamiento RESTful sobre
7
7
  **Problema que resuelve:** Eliminar el acoplamiento directo entre microservicios via HTTP, usando RabbitMQ como bus de mensajes con la misma ergonomía de un framework web.
8
8
 
9
9
  ## Knowledge Base
10
-
11
- - **Docs AI:** `docs/ai/` conocimiento estructurado para agentes
12
- - **Index:** `docs/ai/_index.md` — manifest con versión, audiencias y archivos
13
- - **Docs humanos:** `docs/_index.md` — manifest de toda la documentación (`howto/`, `concepts.md`)
14
-
15
- ## Skills disponibles
16
-
17
- - `.agents/skills/rabbitmq-expert/` arquitectura AMQP, exchanges, quorum queues, DLX, HA, clustering
18
-
19
- ---
20
-
21
- ## Arquitectura
22
-
23
- ```
24
- Publisher (Client/Resource)
25
- └─ Producer → Session → Bunny → RabbitMQ Exchange
26
-
27
- RabbitMQ Queue
28
-
29
- Consumer (subscribe loop)
30
- └─ ConsumerMiddleware::Stack
31
- └─ process_message
32
- └─ Router Controller → Action
33
- └─ reply (RPC)
34
- ```
35
-
36
- ### Componentes clave
37
-
38
- | Clase | Responsabilidad |
39
- |---|---|
40
- | `BugBunny::Session` | Wrapper de canal Bunny. Declara exchanges y queues. |
41
- | `BugBunny::Consumer` | Subscribe loop. Rutea mensajes a controladores via `BugBunny.routes`. |
42
- | `BugBunny::ConsumerMiddleware::Stack` | Pipeline de middlewares antes de `process_message`. |
43
- | `BugBunny::Producer` | Publica mensajes. Implementa RPC con `Concurrent::IVar`. |
44
- | `BugBunny::Client` | API de alto nivel para el publicador. Pool de conexiones. |
45
- | `BugBunny::Controller` | Base class tipo Rails. `around_action`, `before_action`, `render`. |
46
- | `BugBunny::Resource` | ActiveRecord-like sobre AMQP. `find`, `where`, `create`, etc. |
47
- | `BugBunny::Request` | Value object del mensaje saliente (path, method, params, headers). |
48
- | `BugBunny::Observability` | Mixin de logging estructurado. `safe_log`, `exception_metadata`. |
49
- | `BugBunny::Configuration` | Configuración global. Logger, timeouts, middleware hooks. |
50
-
51
- ### Flujo RPC completo
52
-
53
- 1. `Resource.find(id)` → `Client#request` → `Producer#rpc`
54
- 2. Producer publica en exchange con `reply_to: 'amq.rabbitmq.reply-to'`
55
- 3. `Concurrent::IVar` bloquea el thread principal (`future.value(timeout)`)
56
- 4. Consumer recibe → middleware stack → controller → `reply(response)`
57
- 5. Reply listener thread setea `future.set({ body:, headers: })`
58
- 6. Thread principal: `on_rpc_reply&.call(headers)` → `parse_response(body)`
59
-
60
- ## Hooks de extensión
61
-
62
- ```ruby
63
- # Middleware antes de process_message (ej: tracing, auth)
64
- BugBunny.consumer_middlewares.use MyMiddleware
65
-
66
- # Headers a inyectar en el reply RPC (ej: trace context actualizado)
67
- config.rpc_reply_headers = -> { { 'X-Amzn-Trace-Id' => Tracer.header } }
68
-
69
- # Callback en el thread principal al recibir el reply (ej: hidratar tracer)
70
- config.on_rpc_reply = ->(headers) { Tracer.hydrate(headers['X-Amzn-Trace-Id']) }
71
- ```
72
-
73
- ---
74
-
75
- ## Dominio y Expertise
76
-
77
- Al trabajar en esta gema aplicá expertise en:
78
-
79
- - **Ruby idiomático**: módulos, mixins, metaprogramación, `class_attribute`, `Concurrent::*`
80
- - **RabbitMQ / AMQP**: exchanges (direct/topic/fanout), queues, bindings, `reply_to`, `correlation_id`, `properties.headers`, publisher confirms, manual ack
81
- - **Bunny**: la gema Ruby que wrappea AMQP. `channel`, `basic_consume`, `basic_publish`, `IVar`
82
- - **Rails patterns**: `ActiveModel`, `ActiveSupport`, `class_attribute`, `concerns`, `constantize`
83
- - **Rack**: `Rack::Utils.parse_nested_query`, `build_nested_query`
84
-
85
- ---
86
-
87
- ## Observability — Estándar de Logging
88
-
89
- Esta gema implementa su propio patrón de observability via `BugBunny::Observability`.
90
-
91
- ### Reglas fundamentales
92
-
93
- - **Formato**: `component=x event=clase.evento [key=value ...]` — todo en una línea
94
- - **Nunca** llamar al logger directamente. Siempre usar `safe_log`
95
- - **Nunca** `Kernel#warn`, `$stderr`, `puts`
96
- - **Niveles**: `ERROR`=excepción, `WARN`=inesperado+continuó, `INFO`=normal, `DEBUG`=detalle
97
- - `DEBUG` siempre en bloque: `logger.debug { "k=#{v}" }` — `safe_log` lo maneja internamente
98
- - Duraciones: `Process.clock_gettime(Process::CLOCK_MONOTONIC)`, nunca `Time.now`
99
- - Logger failures **nunca** interrumpen el flujo — `safe_log` tiene `rescue StandardError`
100
-
101
- ### Uso en clases nuevas
102
-
103
- ```ruby
104
- class BugBunny::MiClase
105
- include BugBunny::Observability
106
-
107
- def initialize
108
- @logger = BugBunny.configuration.logger
109
- end
110
-
111
- def mi_metodo
112
- start = monotonic_now
113
- # ...
114
- safe_log(:info, "mi_clase.mi_evento", campo: valor, duration_s: duration_s(start))
115
- rescue StandardError => e
116
- safe_log(:error, "mi_clase.error", **exception_metadata(e))
117
- end
118
- end
119
- ```
120
-
121
- ### Naming de eventos
122
-
123
- Formato estricto: `"clase.evento"` (string, nunca symbol)
124
-
125
- | Evento | Nivel | Cuándo |
126
- |---|---|---|
127
- | `consumer.start` | INFO | Consumer inicia subscribe |
128
- | `consumer.bound` | INFO | Queue bindeada al exchange |
129
- | `consumer.message_received` | INFO | Mensaje recibido, antes del routing |
130
- | `consumer.route_matched` | DEBUG | Ruta encontrada |
131
- | `consumer.message_processed` | INFO | Procesamiento exitoso con duración |
132
- | `consumer.execution_error` | ERROR | Excepción en el procesamiento |
133
- | `producer.publish` | INFO | Mensaje publicado |
134
- | `producer.rpc_waiting` | DEBUG | Bloqueando esperando respuesta |
135
- | `producer.rpc_response_received` | DEBUG | Reply recibido (thread principal) |
136
-
137
- ### Campos estándar
138
-
139
- ```ruby
140
- safe_log(:error, "clase.error", **exception_metadata(e))
141
- # => error_class: "RuntimeError", error_message: "..."
142
-
143
- safe_log(:info, "clase.evento", duration_s: duration_s(start_time))
144
- # => duration_s: 0.001234
145
-
146
- # Valores sensibles se filtran automáticamente:
147
- # password, token, secret, api_key, auth → [FILTERED]
148
- ```
149
-
150
- ---
151
-
152
- ## Standards de Código
153
-
154
- ### RuboCop
155
-
156
- Esta gema usa **rubocop-rails-omakase**. Todo código nuevo o modificado debe cumplir.
157
-
158
- ```bash
159
- source /opt/homebrew/opt/chruby/share/chruby/chruby.sh && chruby ruby-3.3.8
160
- bundle exec rubocop
161
- bundle exec rubocop -a # autocorrect
162
- ```
163
-
164
- **No corregir código existente no tocado.** Solo el código nuevo o modificado en el PR.
165
-
166
- ### YARD
167
-
168
- Todo método público nuevo o modificado lleva documentación YARD:
169
-
170
- ```ruby
171
- # Descripción breve.
172
- #
173
- # Descripción extendida si es necesario.
174
- #
175
- # @param name [Type] Descripción
176
- # @return [Type] Descripción
177
- # @raise [ErrorClass] Cuándo se lanza
178
- # @example
179
- # resultado = mi_metodo(arg)
180
- def mi_metodo(name)
181
- ```
182
-
183
- ```bash
184
- bundle exec yard doc
185
- bundle exec yard stats --list-undoc
186
- ```
187
-
188
- ### RSpec
189
-
190
- Tests en `spec/`. Sin mocks de dependencias externas reales (RabbitMQ se mockea con doubles de Bunny).
191
-
192
- ```bash
193
- source /opt/homebrew/opt/chruby/share/chruby/chruby.sh && chruby ruby-3.3.8
194
- bundle exec rspec
195
- bundle exec rspec spec/bug_bunny/consumer_spec.rb # archivo específico
196
- ```
197
-
198
- ---
199
-
200
- ## Entorno de Desarrollo
201
-
202
- ### Ruby
203
-
204
- ```bash
205
- source /opt/homebrew/opt/chruby/share/chruby/chruby.sh && chruby ruby-3.3.8
206
- ```
207
-
208
- Nunca usar `bundle exec ruby` con el Ruby del sistema (2.6). Siempre sourcear chruby primero.
209
-
210
- ### Worktrees
211
-
212
- - **Main**: `/Users/gabriel/src/gems/bug_bunny` (rama `main`)
213
- - **Work**: `/Users/gabriel/src/gems/worktrees/current-5n3` (ramas de feature)
214
- - `main` está checkeado en otro worktree — no se puede hacer `git checkout main` desde el worktree de trabajo
215
-
216
- ### Push a remoto
217
-
218
- SSH está roto en este entorno. Para push siempre:
219
-
220
- ```bash
221
- git remote set-url origin https://github.com/gedera/bug_bunny.git
222
- git push origin main
223
- git remote set-url origin git@github.com:gedera/bug_bunny.git # restaurar
224
- ```
225
-
226
- ---
227
-
228
- ## Release Workflow
229
-
230
- Usá el comando `/release` para el flujo completo. Manualmente:
231
-
232
- 1. Determinar tipo: `patch`=bugfix, `minor`=feature nueva, `major`=breaking change
233
- 2. Actualizar `lib/bug_bunny/version.rb`
234
- 3. Agregar entrada al tope de `CHANGELOG.md`
235
- 4. Commit con mensaje convencional
236
- 5. Desde `/Users/gabriel/src/gems/bug_bunny`: `git merge --ff-only <branch>`
237
- 6. Push via HTTPS + restaurar SSH
238
- 7. `git tag vX.Y.Z && git push origin vX.Y.Z`
239
-
240
- **Nunca commitear ni pushear sin permiso explícito del usuario.**
10
+ - Las skills en `.agents/skills/` incluyen conocimiento de dependencias.
11
+ - Leer la skill de una dependencia ANTES de responder sobre ella.
12
+ - Rebuild: `ruby .agents/skills/skill-manager/scripts/sync.rb`
13
+
14
+ ## Entorno
15
+ - Versión de Ruby: leer `.ruby-version`
16
+ - Versión de Rails y gemas: leer `Gemfile.lock`
17
+ - Gestor de Ruby: chruby (no usar rvm ni rbenv)
18
+ - Package manager: Bundler
19
+
20
+ ## RuboCop
21
+ - Usamos rubocop-rails-omakase como base.
22
+ - Correr `bundle exec rubocop -a` antes de commitear.
23
+ - No deshabilitar cops sin justificación en el PR.
24
+
25
+ ## YARD
26
+ - Documentación incremental: si tocás un método, documentalo con YARD.
27
+ - Consultar la skill `yard` para tags y tipos correctos.
28
+ - Verificar cobertura: `bundle exec yard stats --list-undoc`
29
+
30
+ ## Testing
31
+ - Framework: RSpec
32
+ - Correr: `bundle exec rspec`
33
+ - Todo código nuevo debe tener tests.
34
+
35
+ ## Releases
36
+ - Gemas: `/gem-release`
37
+ - Servicios: `/service-release build` o `/service-release deploy`
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module BugBunny
4
- VERSION = '4.8.0'
4
+ VERSION = '4.8.1'
5
5
  end
data/skill/SKILL.md ADDED
@@ -0,0 +1,230 @@
1
+ # BugBunny Expert
2
+
3
+ Skill de conocimiento completo sobre BugBunny. Consultame para cualquier pregunta sobre integración, arquitectura, API, errores y antipatrones.
4
+
5
+ ---
6
+
7
+ ## Glosario
8
+
9
+ **AMQP** — Advanced Message Queuing Protocol. Protocolo binario que implementa RabbitMQ.
10
+ **Bunny** — Cliente Ruby para AMQP. BugBunny lo usa internamente para conexiones, canales y publicación.
11
+ **Exchange** — Recibe mensajes del producer y los enruta a queues según reglas. Tipos: `direct` (match exacto), `topic` (wildcards), `fanout` (broadcast).
12
+ **Queue** — Almacena mensajes hasta que un consumer los consume. Las queues durables sobreviven reinicios del broker.
13
+ **Routing Key** — String que el producer adjunta al mensaje. El exchange lo usa para decidir a qué queues enrutar.
14
+ **Binding** — Enlace entre un exchange y una queue, opcionalmente con un patrón de routing key.
15
+ **Session** — `BugBunny::Session` envuelve canales de Bunny con thread-safety y double-checked locking.
16
+ **RPC** — Patrón síncrono que usa la pseudo-cola `amq.rabbitmq.reply-to` para respuestas sin crear queues temporales.
17
+ **Fire-and-Forget** — Patrón asíncrono donde el producer publica y continúa sin esperar respuesta. Retorna `{ 'status' => 202 }`.
18
+ **Resource** — ORM tipo ActiveRecord que mapea operaciones CRUD a llamadas AMQP.
19
+ **Consumer** — Worker bloqueante que despacha mensajes a controladores mediante un Router.
20
+ **Connection Pool** — Pool de conexiones (`connection_pool` gem) que comparte sessions entre threads. Cada slot cachea su `Session` y `Producer`.
21
+
22
+ ---
23
+
24
+ ## Arquitectura: Flujo RPC
25
+
26
+ ```
27
+ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
28
+ │ Resource │────>│ Client │────>│ Middleware│────>│ Producer │
29
+ └──────────┘ └──────────┘ │ Stack │ └────┬─────┘
30
+ └──────────┘ │
31
+
32
+ ┌──────────┐
33
+ │ Exchange │
34
+ └────┬─────┘
35
+
36
+
37
+ ┌──────────┐
38
+ │ Queue │
39
+ └────┬─────┘
40
+
41
+
42
+ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
43
+ │ Reply │<────│Controller│<────│ Router │<────│ Consumer │
44
+ └──────────┘ └──────────┘ └──────────┘ └──────────┘
45
+ ```
46
+
47
+ 1. El `Client` pasa la petición por una pila de middlewares client-side.
48
+ 2. El `Producer` publica en el exchange con `correlation_id`, `reply_to` y el path en el header `type`.
49
+ 3. El hilo emisor se bloquea en un `Concurrent::IVar` esperando la respuesta.
50
+ 4. El `Consumer` recibe, ejecuta consumer middlewares, rutea al controller.
51
+ 5. El controller ejecuta callbacks y la acción, luego responde via `reply_to`.
52
+ 6. Se aplican ganchos de traza (`on_rpc_reply`) y se devuelve el objeto hidratado.
53
+
54
+ ---
55
+
56
+ ## Arquitectura: Componentes Clave
57
+
58
+ | Clase | Responsabilidad |
59
+ |---|---|
60
+ | `BugBunny::Configuration` | Configuración global. Valida campos requeridos en `BugBunny.configure`. |
61
+ | `BugBunny::Session` | Wrapper de canal Bunny. Declara exchanges y queues. Thread-safe con double-checked locking. |
62
+ | `BugBunny::Producer` | Publica mensajes. Implementa RPC con `Concurrent::IVar` y direct reply-to. |
63
+ | `BugBunny::Client` | API de alto nivel. Pool de conexiones y middleware stack (onion architecture). |
64
+ | `BugBunny::Consumer` | Subscribe loop con health check. Rutea mensajes via `BugBunny.routes`. |
65
+ | `BugBunny::ConsumerMiddleware::Stack` | Pipeline de middlewares antes de `process_message`. Thread-safe. |
66
+ | `BugBunny::Controller` | Base class tipo Rails. `before_action`, `around_action`, `after_action`, `rescue_from`, `render`. |
67
+ | `BugBunny::Resource` | ORM sobre AMQP. `find`, `where`, `create`, `save`, `destroy`. ActiveModel validations y callbacks. |
68
+ | `BugBunny::Routing::RouteSet` | DSL de rutas: `resources`, `namespace`, `member`, `collection`. |
69
+ | `BugBunny::Observability` | Mixin de logging estructurado. `safe_log` nunca lanza excepciones. Filtra keys sensibles. |
70
+ | `BugBunny::Middleware::Stack` | Builder de middlewares client-side (onion architecture tipo Faraday). |
71
+ | `BugBunny::Request` | Value object del mensaje saliente con metadata AMQP completa. |
72
+ | `BugBunny::Railtie` | Integración Rails: autoload de `app/rabbit`, fork safety (Puma, Spring). |
73
+
74
+ ---
75
+
76
+ ## API: Configuración Global
77
+
78
+ ```ruby
79
+ BugBunny.configure do |config|
80
+ # Conexión
81
+ config.host = 'localhost' # default: '127.0.0.1'
82
+ config.port = 5672 # default: 5672
83
+ config.username = 'guest' # default: 'guest'
84
+ config.password = 'guest' # default: 'guest'
85
+ config.vhost = '/' # default: '/'
86
+
87
+ # Resiliencia
88
+ config.automatically_recover = true
89
+ config.network_recovery_interval = 5
90
+ config.max_reconnect_attempts = nil # nil = infinito
91
+ config.connection_timeout = 10
92
+ config.heartbeat = 15
93
+
94
+ # Performance
95
+ config.channel_prefetch = 1
96
+ config.rpc_timeout = 10
97
+
98
+ # Logging
99
+ config.logger = Rails.logger
100
+ config.log_tags = [:uuid]
101
+
102
+ # Propagación de trazas
103
+ config.rpc_reply_headers = -> { { 'X-Trace-Id' => Tracer.id } }
104
+ config.on_rpc_reply = ->(h) { Tracer.hydrate(h['X-Trace-Id']) }
105
+
106
+ # Infraestructura (cascade level 2)
107
+ config.exchange_options = { durable: true }
108
+ config.queue_options = { auto_delete: false }
109
+
110
+ # Health check
111
+ config.health_check_interval = 60
112
+ config.health_check_file = 'tmp/bb_health'
113
+
114
+ # Routing
115
+ config.controller_namespace = 'BugBunny::Controllers'
116
+ end
117
+ ```
118
+
119
+ La validación es automática tras el bloque; lanza `ConfigurationError` si faltan campos requeridos o los valores están fuera de rango.
120
+
121
+ ---
122
+
123
+ ## API: Routing DSL
124
+
125
+ ```ruby
126
+ BugBunny.routes.draw do
127
+ resources :users
128
+ resources :orders, only: [:index, :show]
129
+ resources :products, except: [:destroy]
130
+
131
+ namespace :admin do
132
+ resources :reports
133
+ resources :nodes do
134
+ member do
135
+ put :drain # PUT nodes/:id/drain
136
+ end
137
+ collection do
138
+ get :stats # GET nodes/stats
139
+ end
140
+ end
141
+ end
142
+ end
143
+ ```
144
+
145
+ Genera rutas REST estándar (index, show, create, update, destroy) mapeadas a controladores. El `namespace` añade prefijo al path y busca controladores dentro del módulo correspondiente.
146
+
147
+ ---
148
+
149
+ ## API: RPC vs Fire-and-Forget
150
+
151
+ **RPC síncrono** — Bloquea hasta respuesta. Usa `amq.rabbitmq.reply-to`. Timeout configurable.
152
+ ```ruby
153
+ response = client.request('users/42', method: :get)
154
+ # → { 'status' => 200, 'body' => { 'id' => 42, 'name' => 'John' } }
155
+ ```
156
+
157
+ **Fire-and-Forget** — Publica y continúa. Sin confirmación.
158
+ ```ruby
159
+ client.publish('events', method: :post, body: { type: 'order.placed' })
160
+ # → { 'status' => 202, 'body' => nil }
161
+ ```
162
+
163
+ ---
164
+
165
+ ## FAQ
166
+
167
+ ### ¿Cómo se integra con Rails?
168
+ `rails generate bug_bunny:install` genera el inicializador, crea `app/bug_bunny/controllers/` y actualiza `CLAUDE.md`. El pool se define en el inicializador y se asigna a `BugBunny::Resource.connection_pool`.
169
+
170
+ ### ¿Cómo funciona el Health Check?
171
+ El consumer ejecuta un check periódico (default 60s) que verifica la conexión AMQP con un `queue.declare(passive: true)`. Si `health_check_file` está configurado, actualiza su mtime. En Kubernetes, usar un `livenessProbe` tipo `exec` que verifique recencia del archivo.
172
+
173
+ ### ¿Cómo funciona el Connection Pool?
174
+ Cada slot del pool cachea su `Session` y `Producer` durante su vida útil. Esto evita recrear canales AMQP (costoso) y previene el error de doble `basic_consume`. Thread-safety garantizada por `ConnectionPool`.
175
+
176
+ ### ¿Cómo funciona la cascada de configuración?
177
+ 3 niveles: Gem defaults → Global config (`BugBunny.configure`) → Per-request (args en `client.request` o `Resource.with`). Se mergean con `merge`.
178
+
179
+ ### ¿Cómo funciona fork safety?
180
+ `BugBunny::Railtie` registra hooks en `ActiveSupport::ForkTracker` (Rails 7.1+), `Puma.events.on_worker_boot` y `Spring.after_fork` para llamar `BugBunny.disconnect` y evitar sockets TCP heredados.
181
+
182
+ ---
183
+
184
+ ## Antipatrones
185
+
186
+ ### Consumer en Puma
187
+ No ejecutar el `Consumer` dentro de hilos de Puma. Es un bucle bloqueante que saturará el servidor web. Usar un proceso worker dedicado o una tarea Rake separada.
188
+
189
+ ### Reasignación de Pool en Runtime
190
+ No asignar `Resource.connection_pool` dentro de controllers o models durante una petición. Es un ajuste global que causa condiciones de carrera y fugas de conexiones.
191
+
192
+ ### Abuso de .with persistente
193
+ No guardar el resultado de `Order.with(...)` en una variable para múltiples llamadas. Lanzará error tras la primera ejecución. Para múltiples llamadas, usar siempre la forma de bloque.
194
+
195
+ ### Registrar middleware durante call()
196
+ No registrar consumer middlewares durante la ejecución de `call()`. El stack toma un snapshot al inicio; los registros concurrentes no afectan la ejecución actual.
197
+
198
+ ---
199
+
200
+ ## Errores Comunes
201
+
202
+ ### BugBunny::RequestTimeout (408)
203
+ **Causa:** No hubo respuesta en `config.rpc_timeout` segundos.
204
+ **Resolución:** Verificar que el worker esté activo y que el controlador remoto no lance excepciones silenciosas.
205
+
206
+ ### BugBunny::SecurityError
207
+ **Causa:** El mensaje intenta ejecutar un controlador que no hereda de `BugBunny::Controller`.
208
+ **Resolución:** Verificar la jerarquía de controladores y que `config.controller_namespace` coincida.
209
+
210
+ ### BugBunny::UnprocessableEntity (422)
211
+ **Causa:** Fallo de validación en el servicio remoto.
212
+ **Resolución:** `resource.save` devuelve `false`. Acceder a `resource.errors` o `rescue` con `e.error_messages`.
213
+
214
+ ### BugBunny::CommunicationError
215
+ **Causa:** Fallo de conexión o reconexión agotada.
216
+ **Resolución:** Verificar conectividad a RabbitMQ. Revisar `max_reconnect_attempts` y logs de reconexión.
217
+
218
+ Ver catálogo completo en [Errores](references/errores.md).
219
+
220
+ ---
221
+
222
+ ## Referencias
223
+
224
+ - [Routing](references/routing.md) — DSL de rutas, bindings, namespaces, member y collection
225
+ - [Controllers](references/controller.md) — Acciones, callbacks, render, rescue_from y log tags
226
+ - [Resources](references/resource.md) — CRUD sobre AMQP, .with, callbacks y change tracking
227
+ - [Client y Middleware](references/client-middleware.md) — Client, Producer, middleware stack onion
228
+ - [Consumer](references/consumer.md) — Subscribe loop, consumer middleware, health check
229
+ - [Catálogo de Errores](references/errores.md) — Jerarquía completa de excepciones con resolución
230
+ - [Testing](references/testing.md) — RSpec helpers, mocks, patrones de integración