bug_bunny 4.8.1 → 4.9.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 35c04cd113b0a3056cab1dcfda6d389fa996e13bb44992d4791bdef29b93fc7d
4
- data.tar.gz: aa7d145fb3eb1d80f435c0541e1009df78a636019e6c6452be0e0a0515b1f1a8
3
+ metadata.gz: c70dd01d8d666fe56dbb95bc925ee431133cc0c342dc113a0775a4dddd4ce135
4
+ data.tar.gz: 2917d1105969a8173a8b9f70944d9b5839122a56185a5334db0e1c3a04594d2c
5
5
  SHA512:
6
- metadata.gz: 9efee5f3deb6a52c76a361230032e040c964e22a1b52a2419873cb3d1c06159a1c52a042f424974a697a591342e8116aea1e39dd57e0bf71aa617ed5245e0860
7
- data.tar.gz: 1f569f9e8b6fdfa2b48dd2270494a9a6b8f8aec2539ebadec5e20bc27f3ccd23407a340891b15c1567fe48ce593ee45a694b843987e5078852bb14d94ad37d70
6
+ metadata.gz: 241b7e0e684aa866d98940a2a3bc587ed13cf1da65c5708ff983c439078c40d823f13cac54ee8bf6d735dda138050c598c8cbf06759605523b8b2207de0e8e4d
7
+ data.tar.gz: 0b4f509d46dbe3ebd43bf8421bd9938bd2aa5d68ca23e639c3c1d232d5ad9d41b55cec0a4a0096cf8481e46c5d06163ce959d0f0167636d9084bf5d8aa3c9ba2
data/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.9.1] - 2026-04-06
4
+
5
+ ### Correcciones
6
+ - Corregir `ArgumentError` en `Controller#process` con ActiveSupport 8.1: `Rails.logger.tagged` ahora pasa el logger como argumento al bloque (`yield self`), lo que causa error de aridad en lambdas estrictos. Se reemplaza `lambda do` por `proc do` en `core_execution` para ignorar argumentos extra. Compatible con Rails 6, 7 y 8. — @Gabriel
7
+
8
+ ## [4.9.0] - 2026-04-05
9
+
10
+ ### ✨ New Features
11
+ * **OTel messaging semantic conventions:** BugBunny ahora emite los campos del estándar [OpenTelemetry semantic conventions for messaging](https://opentelemetry.io/docs/specs/otel/trace/semantic-conventions/messaging/) tanto en los headers AMQP de publish/reply como en los log events del consumer. Los campos emitidos son `messaging.system` (`"rabbitmq"`), `messaging.operation` (`"publish"` / `"process"`), `messaging.destination.name`, `messaging.rabbitmq.destination.routing_key` y `messaging.message.id` (cuando hay `correlation_id`). Permite que dashboards OTel-native (Tempo, Jaeger, Honeycomb) rendericen correctamente los spans de RabbitMQ y que ExisRay los consuma automáticamente desde `properties.headers`.
12
+ * **`BugBunny::OTel` module:** Nuevo módulo con las constantes de las claves OTel y el helper `messaging_headers` para construir el hash de campos. Los headers del usuario pueden sobrescribir valores OTel como escape hatch, pero `x-http-method` sigue siendo inmutable.
13
+
3
14
  ## [4.8.1] - 2026-04-04
4
15
 
5
16
  ### Mejoras internas
data/CLAUDE.md CHANGED
@@ -6,32 +6,38 @@ BugBunny es una gema Ruby que implementa una capa de enrutamiento RESTful sobre
6
6
 
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
+ ## Documentación
10
+
11
+ - **Para humanos**: `docs/` (5 archivos) + `README.md`. Ver README para índice.
12
+ - **Para agentes AI**: `skill/SKILL.md` + `skill/references/`. Es la skill empaquetada que otros proyectos consumen via `skill-manager sync`.
13
+ - **Nunca referenciar `skill/` desde `docs/` o `README.md`** — son audiencias distintas.
14
+
9
15
  ## Knowledge Base
10
16
  - Las skills en `.agents/skills/` incluyen conocimiento de dependencias.
11
17
  - Leer la skill de una dependencia ANTES de responder sobre ella.
12
18
  - Rebuild: `ruby .agents/skills/skill-manager/scripts/sync.rb`
13
19
 
14
- ## Entorno
20
+ ### Entorno
15
21
  - Versión de Ruby: leer `.ruby-version`
16
22
  - Versión de Rails y gemas: leer `Gemfile.lock`
17
23
  - Gestor de Ruby: chruby (no usar rvm ni rbenv)
18
24
  - Package manager: Bundler
19
25
 
20
- ## RuboCop
26
+ ### RuboCop
21
27
  - Usamos rubocop-rails-omakase como base.
22
28
  - Correr `bundle exec rubocop -a` antes de commitear.
23
29
  - No deshabilitar cops sin justificación en el PR.
24
30
 
25
- ## YARD
31
+ ### YARD
26
32
  - Documentación incremental: si tocás un método, documentalo con YARD.
27
33
  - Consultar la skill `yard` para tags y tipos correctos.
28
34
  - Verificar cobertura: `bundle exec yard stats --list-undoc`
29
35
 
30
- ## Testing
36
+ ### Testing
31
37
  - Framework: RSpec
32
38
  - Correr: `bundle exec rspec`
33
39
  - Todo código nuevo debe tener tests.
34
40
 
35
- ## Releases
36
- - Gemas: `/gem-release`
37
- - Servicios: `/service-release build` o `/service-release deploy`
41
+ ### Releases o Nuevas versiones
42
+ - Usar `/gem-release` para publicar nuevas versiones.
43
+ - El GitHub Action publica a RubyGems automáticamente al pushear un tag `v*`.
data/README.md CHANGED
@@ -214,15 +214,17 @@ BugBunny.consumer_middlewares.use TracingMiddleware
214
214
 
215
215
  ## Observability
216
216
 
217
- All internal events are emitted as structured key=value logs compatible with Datadog, CloudWatch, and ELK.
217
+ BugBunny implementa de forma nativa las [OpenTelemetry semantic conventions for messaging](https://opentelemetry.io/docs/specs/otel/trace/semantic-conventions/messaging/), inyectando automáticamente campos como `messaging_system`, `messaging_operation`, `messaging_destination_name` y `messaging_message_id` tanto en los headers AMQP como en los log events estructurados.
218
+
219
+ Todos los eventos internos se emiten como logs `key=value` compatibles con Datadog, CloudWatch, ELK y ExisRay.
218
220
 
219
221
  ```
220
- component=bug_bunny event=consumer.message_processed status=200 duration_s=0.012 controller=NodesController action=show
222
+ component=bug_bunny event=consumer.message_processed status=200 duration_s=0.012 messaging_operation=process controller=NodesController action=show
221
223
  component=bug_bunny event=consumer.execution_error error_class=RuntimeError error_message="..." duration_s=0.003
222
224
  component=bug_bunny event=consumer.connection_error attempt_count=2 retry_in_s=10 error_message="..."
223
225
  ```
224
226
 
225
- Sensitive keys (`password`, `token`, `secret`, `api_key`, `authorization`, etc.) are automatically filtered to `[FILTERED]` in all log output.
227
+ Las claves sensibles (`password`, `token`, `secret`, `api_key`, `authorization`, etc.) se filtran automáticamente a `[FILTERED]` en toda la salida de logs.
226
228
 
227
229
  ---
228
230
 
@@ -152,6 +152,15 @@ module BugBunny
152
152
  def process_message(delivery_info, properties, body)
153
153
  start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
154
154
 
155
+ # Campos OTel semantic conventions para los log events del consumer.
156
+ # Se mergean con ** en los safe_log de recepción y procesamiento.
157
+ otel_fields = BugBunny::OTel.messaging_headers(
158
+ operation: 'process',
159
+ destination: delivery_info.exchange,
160
+ routing_key: delivery_info.routing_key,
161
+ message_id: properties.correlation_id
162
+ )
163
+
155
164
  # 1. Validación de Headers (URL path)
156
165
  path = properties.type || (properties.headers && properties.headers['path'])
157
166
 
@@ -166,7 +175,7 @@ module BugBunny
166
175
  http_method = (headers_hash['x-http-method'] || headers_hash['method'] || 'GET').to_s.upcase
167
176
 
168
177
  safe_log(:info, 'consumer.message_received', method: http_method, path: path,
169
- routing_key: delivery_info.routing_key)
178
+ routing_key: delivery_info.routing_key, **otel_fields)
170
179
  safe_log(:debug, 'consumer.message_received_body', body: body.truncate(200))
171
180
 
172
181
  # ===================================================================
@@ -239,10 +248,11 @@ module BugBunny
239
248
  session.channel.ack(delivery_info.delivery_tag)
240
249
 
241
250
  safe_log(:info, 'consumer.message_processed',
242
- status: response_payload[:status],
251
+ response_status: response_payload[:status],
243
252
  duration_s: duration_s(start_time),
244
253
  controller: controller_class_name,
245
- action: route_info[:action])
254
+ action: route_info[:action],
255
+ **otel_fields)
246
256
  rescue StandardError => e
247
257
  safe_log(:error, 'consumer.execution_error', duration_s: duration_s(start_time), **exception_metadata(e))
248
258
  safe_log(:debug, 'consumer.execution_error_backtrace', backtrace: e.backtrace.first(5).join(' | '))
@@ -257,14 +267,20 @@ module BugBunny
257
267
  # @param correlation_id [String] ID para correlacionar la respuesta con la petición original.
258
268
  # @return [void]
259
269
  def reply(payload, reply_to, correlation_id)
260
- safe_log(:debug, 'consumer.rpc_reply', reply_to: reply_to, correlation_id: correlation_id)
270
+ safe_log(:debug, 'consumer.rpc_reply', reply_to: reply_to, messaging_message_id: correlation_id)
271
+ otel_headers = BugBunny::OTel.messaging_headers(
272
+ operation: 'publish',
273
+ destination: '',
274
+ routing_key: reply_to,
275
+ message_id: correlation_id
276
+ )
261
277
  extra_headers = BugBunny.configuration.rpc_reply_headers&.call || {}
262
278
  session.channel.default_exchange.publish(
263
279
  payload.to_json,
264
280
  routing_key: reply_to,
265
281
  correlation_id: correlation_id,
266
282
  content_type: 'application/json',
267
- headers: extra_headers
283
+ headers: otel_headers.transform_keys(&:to_s).merge(extra_headers)
268
284
  )
269
285
  end
270
286
 
@@ -165,7 +165,7 @@ module BugBunny
165
165
  current_arounds = resolve_callbacks(self.class.around_actions, action_name)
166
166
 
167
167
  # Definir el núcleo de ejecución
168
- core_execution = lambda do
168
+ core_execution = proc do
169
169
  return unless run_before_actions(action_name)
170
170
 
171
171
  raise NameError, "Action '#{action_name}' not found in #{self.class.name}" unless respond_to?(action_name)
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BugBunny
4
+ # Helpers para emitir campos siguiendo las OTel semantic conventions for messaging.
5
+ # https://opentelemetry.io/docs/specs/otel/trace/semantic-conventions/messaging/
6
+ #
7
+ # Se usa tanto en el lado publisher (inyección en headers AMQP) como en el consumer
8
+ # (enriquecimiento de log events estructurados). Centraliza las claves para evitar
9
+ # strings mágicos dispersos y facilitar los tests.
10
+ module OTel
11
+ # Clave: sistema de mensajería. Siempre `"rabbitmq"` en BugBunny.
12
+ # Flat-naming siguiendo el patrón de ExisRay (underscore sin dots).
13
+ SYSTEM = :messaging_system
14
+ # Clave: tipo de operación (`publish`, `receive`, `process`).
15
+ OPERATION = :messaging_operation
16
+ # Clave: nombre del exchange destino.
17
+ DESTINATION = :messaging_destination_name
18
+ # Clave: routing key del mensaje (específica de RabbitMQ).
19
+ ROUTING_KEY = :messaging_routing_key
20
+ # Clave: identificador único del mensaje. En BugBunny se mapea a `correlation_id`.
21
+ MESSAGE_ID = :messaging_message_id
22
+
23
+ # Valor constante para {SYSTEM}.
24
+ SYSTEM_VALUE = 'rabbitmq'
25
+
26
+ # Construye el hash de campos OTel para messaging.
27
+ #
28
+ # Los campos son aptos tanto para inyectar en headers AMQP como para mergear
29
+ # en kwargs de log events estructurados.
30
+ #
31
+ # @param operation [String] Una de: `"publish"`, `"receive"`, `"process"`.
32
+ # @param destination [String, nil] Nombre del exchange destino (puede ser `""` para default exchange).
33
+ # @param routing_key [String, nil] Routing key final del mensaje.
34
+ # @param message_id [String, nil] Identificador del mensaje. Se omite si es `nil`.
35
+ # @return [Hash{String=>String}] Hash con los campos OTel de messaging.
36
+ def self.messaging_headers(operation:, destination:, routing_key:, message_id: nil)
37
+ fields = {
38
+ SYSTEM => SYSTEM_VALUE,
39
+ OPERATION => operation,
40
+ DESTINATION => destination.to_s,
41
+ ROUTING_KEY => routing_key.to_s
42
+ }
43
+ fields[MESSAGE_ID] = message_id.to_s if message_id
44
+ fields
45
+ end
46
+ end
47
+ end
@@ -79,7 +79,7 @@ module BugBunny
79
79
  begin
80
80
  fire(request)
81
81
 
82
- safe_log(:debug, 'producer.rpc_waiting', correlation_id: cid, timeout_s: wait_timeout)
82
+ safe_log(:debug, 'producer.rpc_waiting', messaging_message_id: cid, timeout_s: wait_timeout)
83
83
 
84
84
  # Bloqueamos el hilo aquí hasta que llegue la respuesta o expire el timeout
85
85
  result = future.value(wait_timeout)
@@ -88,7 +88,8 @@ module BugBunny
88
88
 
89
89
  BugBunny.configuration.on_rpc_reply&.call(result[:headers])
90
90
 
91
- safe_log(:debug, 'producer.rpc_response_received', correlation_id: cid)
91
+ safe_log(:debug, 'producer.rpc_response_received',
92
+ messaging_system: 'rabbitmq', messaging_operation: 'receive', messaging_message_id: cid)
92
93
 
93
94
  parse_response(result[:body])
94
95
  ensure
@@ -109,13 +110,21 @@ module BugBunny
109
110
  rk = request.final_routing_key
110
111
  id = request.correlation_id
111
112
 
113
+ otel_fields = BugBunny::OTel.messaging_headers(
114
+ operation: 'publish',
115
+ destination: request.exchange,
116
+ routing_key: rk,
117
+ message_id: id
118
+ )
119
+
112
120
  # 📊 LOGGING DE OBSERVABILIDAD: Calculamos las opciones finales para mostrarlas en consola
113
121
  final_x_opts = BugBunny::Session::DEFAULT_EXCHANGE_OPTIONS
114
122
  .merge(BugBunny.configuration.exchange_options || {})
115
123
  .merge(request.exchange_options || {})
116
124
 
117
- safe_log(:info, 'producer.publish', method: verb, path: target, routing_key: rk, correlation_id: id)
118
- safe_log(:debug, 'producer.publish_detail', exchange: request.exchange, exchange_opts: final_x_opts)
125
+ safe_log(:info, 'producer.publish', method: verb, path: target, **otel_fields)
126
+ safe_log(:debug, 'producer.publish_detail', messaging_destination_name: request.exchange,
127
+ exchange_opts: final_x_opts)
119
128
  safe_log(:debug, 'producer.publish_payload', payload: payload.truncate(300)) if payload.is_a?(String)
120
129
  end
121
130
 
@@ -86,10 +86,22 @@ module BugBunny
86
86
  # **Importante:** Inyecta el verbo HTTP en los headers bajo la clave `x-http-method`.
87
87
  # Esto permite al Consumer enrutar correctamente a la acción del controlador.
88
88
  #
89
+ # También inyecta los campos de OTel semantic conventions for messaging
90
+ # (ver {BugBunny::OTel}) con `operation=publish`. Los headers del usuario
91
+ # pueden sobrescribir los valores OTel (escape hatch); `x-http-method`
92
+ # nunca se pisa porque es lo último en el merge.
93
+ #
89
94
  # @return [Hash] Opciones listas para pasar a `exchange.publish`.
90
95
  def amqp_options
91
- # Inyectamos el verbo HTTP en los headers para el Router del Consumer
92
- final_headers = headers.merge('x-http-method' => method.to_s.upcase)
96
+ otel_headers = BugBunny::OTel.messaging_headers(
97
+ operation: 'publish',
98
+ destination: exchange,
99
+ routing_key: final_routing_key,
100
+ message_id: correlation_id
101
+ )
102
+ # Orden del merge: OTel base -> headers del usuario -> x-http-method (inmutable)
103
+ # OTel keys son symbols internamente; los stringificamos para Bunny AMQP headers.
104
+ final_headers = otel_headers.transform_keys(&:to_s).merge(headers).merge('x-http-method' => method.to_s.upcase)
93
105
 
94
106
  {
95
107
  type: final_type,
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module BugBunny
4
- VERSION = '4.8.1'
4
+ VERSION = '4.9.1'
5
5
  end
data/lib/bug_bunny.rb CHANGED
@@ -6,6 +6,7 @@ require_relative 'bug_bunny/version'
6
6
  require_relative 'bug_bunny/exception'
7
7
  require_relative 'bug_bunny/configuration'
8
8
  require_relative 'bug_bunny/observability'
9
+ require_relative 'bug_bunny/otel'
9
10
  require_relative 'bug_bunny/routing/route_set'
10
11
  require_relative 'bug_bunny/middleware/base'
11
12
  require_relative 'bug_bunny/middleware/stack'
data/skill/SKILL.md CHANGED
@@ -68,8 +68,31 @@ Skill de conocimiento completo sobre BugBunny. Consultame para cualquier pregunt
68
68
  | `BugBunny::Routing::RouteSet` | DSL de rutas: `resources`, `namespace`, `member`, `collection`. |
69
69
  | `BugBunny::Observability` | Mixin de logging estructurado. `safe_log` nunca lanza excepciones. Filtra keys sensibles. |
70
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). |
71
+ | BugBunny::Request | Value object del mensaje saliente con metadata AMQP completa. |
72
+ | BugBunny::OTel | Helpers para emitir campos siguiendo las OTel semantic conventions for messaging. |
73
+ | BugBunny::Railtie | Integración Rails: autoload de `app/rabbit`, fork safety (Puma, Spring). |
74
+
75
+ ---
76
+
77
+ ## Observability: OpenTelemetry
78
+
79
+ BugBunny implementa las [OpenTelemetry semantic conventions for messaging](https://opentelemetry.io/docs/specs/otel/trace/semantic-conventions/messaging/) de forma nativa para garantizar la trazabilidad entre servicios en entornos distribuidos.
80
+
81
+ ### Campos Estándar (Flat-naming)
82
+
83
+ | Campo | Valor / Origen | Propósito |
84
+ |---|---|---|
85
+ | `messaging_system` | `"rabbitmq"` | Identifica el broker. |
86
+ | `messaging_operation` | `"publish"`, `"receive"`, `"process"` | Tipo de interacción. |
87
+ | `messaging_destination_name` | `exchange_name` | Exchange destino (o `""` para default). |
88
+ | `messaging_routing_key` | `routing_key` | Clave de ruteo final. |
89
+ | `messaging_message_id` | `correlation_id` | ID único para correlación y traza. |
90
+
91
+ ### Inyección y Extracción
92
+
93
+ - **Publisher:** Inyecta estos campos en los headers AMQP bajo el prefijo `messaging_`. El usuario puede sobrescribirlos como *escape hatch* desde `headers`.
94
+ - **Consumer:** Extrae los campos de los logs estructurados sin mutar los headers originales. Los eventos `consumer.message_received` y `consumer.message_processed` incluyen estos campos automáticamente.
95
+ - **RPC Reply:** El consumer inyecta los mismos campos en el reply para cerrar el ciclo de traza del lado del cliente.
73
96
 
74
97
  ---
75
98
 
@@ -107,6 +107,23 @@ El método `call` del `Base` invoca `on_request`, delega a `@app.call`, y luego
107
107
 
108
108
  **JsonResponse** — Auto-parsea `response['body']` de String a Hash/Array. Aplica `HashWithIndifferentAccess` si disponible.
109
109
 
110
+ ## OpenTelemetry: Publisher Injection
111
+
112
+ El `Producer` (vía `Request#amqp_options`) inyecta automáticamente los campos de OTel semantic conventions en los headers AMQP del mensaje saliente.
113
+
114
+ ```ruby
115
+ # Headers inyectados automáticamente
116
+ {
117
+ 'messaging_system' => 'rabbitmq',
118
+ 'messaging_operation' => 'publish',
119
+ 'messaging_destination_name' => 'exchange_name',
120
+ 'messaging_routing_key' => 'rk',
121
+ 'messaging_message_id' => 'uuid'
122
+ }
123
+ ```
124
+
125
+ El orden de merge es: **OTel base** → **headers del usuario** → **x-http-method**. Esto permite al desarrollador sobrescribir valores de OTel si es necesario, pero garantiza que el ruteo interno (`x-http-method`) se mantenga íntegro.
126
+
110
127
  ## Request Object
111
128
 
112
129
  Value object con toda la metadata AMQP:
@@ -18,13 +18,31 @@ consumer = BugBunny::Consumer.subscribe(
18
18
  ## Flujo de Procesamiento
19
19
 
20
20
  1. Escucha en la queue con `manual_ack: true`.
21
- 2. Valida que el mensaje tenga header `type` (path).
22
- 3. Parsea el método HTTP de headers (`x-http-method` o `method`).
23
- 4. Reconoce la ruta con `BugBunny.routes.recognize(method, path)`.
24
- 5. Resuelve el controlador validando herencia de `BugBunny::Controller`.
25
- 6. Ejecuta consumer middlewares controller callbacks → acción.
26
- 7. Responde via `reply_to` si está presente (RPC).
27
- 8. Hace `ack` del mensaje. En caso de error, `reject`.
21
+ 2. Extrae campos **OTel messaging** del mensaje para logs estructurados (sin mutar headers).
22
+ 3. Valida que el mensaje tenga header `type` (path).
23
+ 4. Parsea el método HTTP de headers (`x-http-method` o `method`).
24
+ 5. Emite log `consumer.message_received` con campos OTel (`messaging_operation: 'process'`).
25
+ 6. Reconoce la ruta con `BugBunny.routes.recognize(method, path)`.
26
+ 7. Resuelve el controlador validando herencia de `BugBunny::Controller`.
27
+ 8. Ejecuta consumer middlewares controller callbacks acción.
28
+ 9. Responde via `reply_to` si está presente (RPC), inyectando campos OTel (`messaging_operation: 'publish'`).
29
+ 10. Emite log `consumer.message_processed` con campos OTel y duraciones.
30
+ 11. Hace `ack` del mensaje. En caso de error, `reject`.
31
+
32
+ ## Observability: OTel Fields
33
+
34
+ El consumer construye automáticamente el hash de campos OTel al inicio de `process_message`:
35
+
36
+ ```ruby
37
+ otel_fields = BugBunny::OTel.messaging_headers(
38
+ operation: 'process',
39
+ destination: delivery_info.exchange,
40
+ routing_key: delivery_info.routing_key,
41
+ message_id: properties.correlation_id
42
+ )
43
+ ```
44
+
45
+ Estos campos se mergean en todos los eventos de log del ciclo de vida del mensaje, permitiendo que ExisRay los rastree sin necesidad de propagarlos manualmente en los headers del usuario.
28
46
 
29
47
  ## Lifecycle
30
48
 
data/skills.lock CHANGED
@@ -1,9 +1,18 @@
1
1
  ---
2
- synced_at: '2026-04-04 12:24:37'
2
+ synced_at: '2026-04-06 14:08:54'
3
3
  skills:
4
+ - name: agent-review
5
+ scope: local
6
+ path: "/Users/gabriel/src/gems/bug_bunny/.agents/skills/agent-review"
7
+ - name: ai-reports
8
+ scope: local
9
+ path: "/Users/gabriel/src/gems/bug_bunny/.agents/skills/ai-reports"
4
10
  - name: documentation-writer
5
11
  scope: local
6
12
  path: "/Users/gabriel/src/gems/bug_bunny/.agents/skills/documentation-writer"
13
+ - name: find-skills
14
+ scope: local
15
+ path: "/Users/gabriel/src/gems/bug_bunny/.agents/skills/find-skills"
7
16
  - name: gem-release
8
17
  scope: local
9
18
  path: "/Users/gabriel/src/gems/bug_bunny/.agents/skills/gem-release"
data/skills.yml CHANGED
@@ -1,19 +1,44 @@
1
1
  mcps:
2
2
  - github
3
+ - clickup
3
4
  skills:
4
- - name: skill-manager
5
+ skill-manager:
5
6
  repo: sequre/ai_knowledge
6
- - name: yard
7
+ scope: local
8
+ yard:
7
9
  repo: sequre/ai_knowledge
8
- - name: quality-code
10
+ scope: local
11
+ quality-code:
9
12
  repo: sequre/ai_knowledge
10
- - name: gem-release
13
+ scope: local
14
+ gem-release:
11
15
  repo: sequre/ai_knowledge
12
- - name: skill-builder
16
+ scope: local
17
+ skill-builder:
13
18
  repo: sequre/ai_knowledge
14
- - name: rabbitmq-expert
15
- repo: martinholovsky/claude-skills-generator
16
- path: skills/rabbitmq-expert
17
- - name: documentation-writer
19
+ scope: local
20
+ ai-reports:
21
+ repo: sequre/ai_knowledge
22
+ scope: local
23
+ environment:
24
+ space_id: "${AI_REPORTS_SPACE_ID}"
25
+ bug_reports_list_id: "${AI_REPORTS_BUG_REPORTS_LIST_ID}"
26
+ improvements_list_id: "${AI_REPORTS_IMPROVEMENTS_LIST_ID}"
27
+ agent-review:
28
+ repo: sequre/ai_knowledge
29
+ scope: local
30
+ environment:
31
+ space_id: "${AGENT_REVIEW_SAPCE_ID}"
32
+ list_id: "${AGENT_LIST_ID}"
33
+ documentation-writer:
18
34
  repo: github/awesome-copilot
19
35
  path: skills/documentation-writer
36
+ scope: local
37
+ find-skills:
38
+ repo: vercel-labs/skills
39
+ path: skills/find-skills
40
+ scope: local
41
+ rabbitmq-expert:
42
+ repo: martinholovsky/claude-skills-generator
43
+ path: skills/rabbitmq-expert
44
+ scope: local
@@ -24,7 +24,7 @@ class TrackingMiddleware < BugBunny::ConsumerMiddleware::Base
24
24
  def call(delivery_info, properties, body)
25
25
  self.class.calls << {
26
26
  routing_key: delivery_info.routing_key,
27
- headers: properties.headers
27
+ headers: properties.headers
28
28
  }
29
29
  @app.call(delivery_info, properties, body)
30
30
  end
@@ -45,7 +45,7 @@ RSpec.describe 'Consumer Middleware Stack', :integration do
45
45
  BugBunny.configure { |c| c.controller_namespace = 'BugBunny::Controllers' }
46
46
  # Limpiamos el middleware para no afectar otros specs
47
47
  BugBunny.configuration.instance_variable_set(:@consumer_middlewares,
48
- BugBunny::ConsumerMiddleware::Stack.new)
48
+ BugBunny::ConsumerMiddleware::Stack.new)
49
49
  end
50
50
 
51
51
  it 'ejecuta el middleware antes de process_message' do
@@ -82,5 +82,26 @@ RSpec.describe 'Consumer Middleware Stack', :integration do
82
82
  BugBunny.configuration.rpc_reply_headers = nil
83
83
  BugBunny.configuration.on_rpc_reply = nil
84
84
  end
85
+
86
+ it 'incluye los campos OTel semantic conventions en el reply' do
87
+ received_headers = nil
88
+
89
+ BugBunny.configuration.rpc_reply_headers = -> { { 'X-Test-Header' => 'from-consumer' } }
90
+ BugBunny.configuration.on_rpc_reply = ->(headers) { received_headers = headers }
91
+
92
+ with_running_worker(queue: queue, exchange: exchange, routing_key: 'ping') do
93
+ client.request('ping', method: :get, exchange: exchange, exchange_type: 'topic', routing_key: 'ping')
94
+ end
95
+
96
+ expect(received_headers).to include(
97
+ 'messaging_system' => 'rabbitmq',
98
+ 'messaging_operation' => 'publish',
99
+ 'X-Test-Header' => 'from-consumer'
100
+ )
101
+ expect(received_headers['messaging_message_id']).not_to be_nil
102
+ ensure
103
+ BugBunny.configuration.rpc_reply_headers = nil
104
+ BugBunny.configuration.on_rpc_reply = nil
105
+ end
85
106
  end
86
107
  end