e11y 0.2.0 → 1.1.0

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 (288) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +130 -10
  3. data/CHANGELOG.md +80 -1
  4. data/CLAUDE.md +168 -0
  5. data/CONTRIBUTING.md +640 -0
  6. data/README.md +165 -701
  7. data/RELEASE.md +41 -12
  8. data/Rakefile +249 -57
  9. data/config/README.md +1 -1
  10. data/config/loki-local-config.yaml +12 -0
  11. data/config/otel-collector-config.yaml +44 -0
  12. data/cucumber.yml +1 -0
  13. data/docker-compose.yml +18 -2
  14. data/docs/ADAPTERS.md +76 -0
  15. data/docs/ADAPTIVE_SAMPLING.md +59 -0
  16. data/docs/COMPARISON.md +104 -0
  17. data/docs/CONFIGURATION.md +52 -0
  18. data/docs/DISTRIBUTED_TRACING.md +44 -0
  19. data/docs/LIMITATIONS.md +13 -0
  20. data/docs/METRICS_DSL.md +84 -0
  21. data/docs/PERFORMANCE.md +60 -0
  22. data/docs/PII_FILTERING.md +40 -0
  23. data/docs/PRESETS.md +65 -0
  24. data/docs/QUICK-START.md +546 -587
  25. data/docs/RAILS_INTEGRATION.md +79 -0
  26. data/docs/SCHEMA_VALIDATION.md +63 -0
  27. data/docs/SLO-PROMQL-ALERTS.md +161 -0
  28. data/docs/TESTING.md +69 -0
  29. data/docs/{ADR-001-architecture.md → architecture/ADR-001-architecture.md} +36 -65
  30. data/docs/{ADR-002-metrics-yabeda.md → architecture/ADR-002-metrics-yabeda.md} +62 -236
  31. data/docs/architecture/ADR-003-slo-observability.md +1402 -0
  32. data/docs/{ADR-004-adapter-architecture.md → architecture/ADR-004-adapter-architecture.md} +163 -146
  33. data/docs/{ADR-005-tracing-context.md → architecture/ADR-005-tracing-context.md} +10 -9
  34. data/docs/{ADR-006-security-compliance.md → architecture/ADR-006-security-compliance.md} +184 -191
  35. data/docs/{ADR-007-opentelemetry-integration.md → architecture/ADR-007-opentelemetry-integration.md} +3 -21
  36. data/docs/{ADR-008-rails-integration.md → architecture/ADR-008-rails-integration.md} +182 -743
  37. data/docs/{ADR-009-cost-optimization.md → architecture/ADR-009-cost-optimization.md} +45 -54
  38. data/docs/architecture/ADR-010-developer-experience.md +522 -0
  39. data/docs/{ADR-011-testing-strategy.md → architecture/ADR-011-testing-strategy.md} +44 -86
  40. data/docs/{ADR-012-event-evolution.md → architecture/ADR-012-event-evolution.md} +11 -11
  41. data/docs/{ADR-013-reliability-error-handling.md → architecture/ADR-013-reliability-error-handling.md} +37 -12
  42. data/docs/{ADR-014-event-driven-slo.md → architecture/ADR-014-event-driven-slo.md} +12 -24
  43. data/docs/{ADR-015-middleware-order.md → architecture/ADR-015-middleware-order.md} +43 -59
  44. data/docs/{ADR-016-self-monitoring-slo.md → architecture/ADR-016-self-monitoring-slo.md} +58 -355
  45. data/docs/{ADR-017-multi-rails-compatibility.md → architecture/ADR-017-multi-rails-compatibility.md} +4 -11
  46. data/docs/architecture/ADR-018-memory-optimization.md +366 -0
  47. data/docs/{ADR-INDEX.md → architecture/ADR-INDEX.md} +11 -6
  48. data/docs/plans/2026-03-20-browser-overlay-svelte.md +281 -0
  49. data/docs/{00-ICP-AND-TIMELINE.md → prd/00-ICP-AND-TIMELINE.md} +6 -6
  50. data/docs/{01-SCALE-REQUIREMENTS.md → prd/01-SCALE-REQUIREMENTS.md} +6 -6
  51. data/docs/prd/01-overview-vision.md +19 -14
  52. data/docs/use_cases/README.md +22 -23
  53. data/docs/use_cases/UC-001-request-scoped-debug-buffering.md +50 -44
  54. data/docs/use_cases/UC-002-business-event-tracking.md +26 -95
  55. data/docs/use_cases/UC-003-event-metrics.md +66 -0
  56. data/docs/use_cases/UC-004-zero-config-slo-tracking.md +33 -684
  57. data/docs/use_cases/UC-005-sentry-integration.md +13 -15
  58. data/docs/use_cases/UC-006-trace-context-management.md +30 -28
  59. data/docs/use_cases/UC-007-pii-filtering.md +35 -87
  60. data/docs/use_cases/UC-008-opentelemetry-integration.md +51 -89
  61. data/docs/use_cases/UC-009-multi-service-tracing.md +30 -178
  62. data/docs/use_cases/UC-010-background-job-tracking.md +24 -91
  63. data/docs/use_cases/UC-011-rate-limiting.md +95 -168
  64. data/docs/use_cases/UC-012-audit-trail.md +21 -46
  65. data/docs/use_cases/UC-013-high-cardinality-protection.md +29 -167
  66. data/docs/use_cases/UC-014-adaptive-sampling.md +2 -2
  67. data/docs/use_cases/UC-015-cost-optimization.md +46 -99
  68. data/docs/use_cases/UC-016-rails-logger-migration.md +39 -213
  69. data/docs/use_cases/UC-017-local-development.md +203 -777
  70. data/docs/use_cases/UC-018-testing-events.md +3 -3
  71. data/docs/use_cases/UC-019-retention-based-routing.md +53 -106
  72. data/docs/use_cases/UC-020-event-versioning.md +8 -9
  73. data/docs/use_cases/UC-021-error-handling-retry-dlq.md +18 -22
  74. data/docs/use_cases/UC-022-event-registry.md +15 -21
  75. data/docs/use_cases/backlog.md +119 -87
  76. data/e11y.gemspec +2 -2
  77. data/gems/e11y-devtools/README.md +158 -0
  78. data/gems/e11y-devtools/config/routes.rb +15 -0
  79. data/gems/e11y-devtools/e11y-devtools.gemspec +25 -0
  80. data/gems/e11y-devtools/exe/e11y +34 -0
  81. data/gems/e11y-devtools/frontend/.gitignore +24 -0
  82. data/gems/e11y-devtools/frontend/README.md +51 -0
  83. data/gems/e11y-devtools/frontend/index.html +14 -0
  84. data/gems/e11y-devtools/frontend/package-lock.json +3707 -0
  85. data/gems/e11y-devtools/frontend/package.json +28 -0
  86. data/gems/e11y-devtools/frontend/public/mocks/v1/events/recent.json +4205 -0
  87. data/gems/e11y-devtools/frontend/public/mocks/v1/interactions.json +194 -0
  88. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/0a2e04027cfa22d014bc22e8b27cd913/events.json +86 -0
  89. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/0e1543af6a630fb3af6b52283154b3e0/events.json +169 -0
  90. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/1838b691faa49564f97db8592ff3978d/events.json +78 -0
  91. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/29f198f6588dacffb687777eb5f8f118/events.json +197 -0
  92. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/34bc3c9c0097de28a7a6f99b90a8e7bc/events.json +194 -0
  93. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/3ba6c20d068ab9cee00e51b180e66444/events.json +184 -0
  94. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/435bfd8f17b9009146a79812d7c3726d/events.json +144 -0
  95. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/4c7676e3fe668e99edb2b94d7d5678a9/events.json +222 -0
  96. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/6daf0d47974bedfc55d5de7004a3ea9f/events.json +194 -0
  97. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/8a81ada42834d15f287bb40010043605/events.json +194 -0
  98. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/8c0a98900edaae105469df8daedccf02/events.json +198 -0
  99. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/8e4f645180f8a7d1dce426b07380466b/events.json +222 -0
  100. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/93db346fa5d44a032605a13b627f4b80/events.json +128 -0
  101. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/98ff6146faf7bd9be8bd03a8275817ba/events.json +223 -0
  102. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/9997ddd0247bc7e25f2ca7a5c415c93d/events.json +197 -0
  103. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/99e35f8ef3baedd798cc4fd085980ad9/events.json +194 -0
  104. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/b4f3095c1909924cbc98889a86c83d6d/events.json +131 -0
  105. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/b54b7fc32b7575a7110de809d11ccda0/events.json +128 -0
  106. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/c0b48033fa06746bcc5886745e053cff/events.json +169 -0
  107. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/c44649ac76701b4558927cd2305ab535/events.json +169 -0
  108. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/d601ae3320057580a39dbdac2edfdf4a/events.json +248 -0
  109. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/e67e724bab422d2b52eeb49635e512e1/events.json +194 -0
  110. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/e6c72765a28f158a8485b35fa63f73da/events.json +194 -0
  111. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/f541b87405c9a54819b18ebe529f6419/events.json +194 -0
  112. data/gems/e11y-devtools/frontend/scripts/generate_mocks.rb +397 -0
  113. data/gems/e11y-devtools/frontend/src/App.svelte +827 -0
  114. data/gems/e11y-devtools/frontend/src/components/Fab.svelte +19 -0
  115. data/gems/e11y-devtools/frontend/src/components/FilterBar.svelte +38 -0
  116. data/gems/e11y-devtools/frontend/src/components/FullscreenPanel.svelte +82 -0
  117. data/gems/e11y-devtools/frontend/src/components/InteractionsTimeline.svelte +264 -0
  118. data/gems/e11y-devtools/frontend/src/components/RecentHistogram.svelte +354 -0
  119. data/gems/e11y-devtools/frontend/src/lib/api.ts +37 -0
  120. data/gems/e11y-devtools/frontend/src/lib/eventIdentity.ts +12 -0
  121. data/gems/e11y-devtools/frontend/src/lib/format.ts +37 -0
  122. data/gems/e11y-devtools/frontend/src/lib/listFilter.ts +43 -0
  123. data/gems/e11y-devtools/frontend/src/lib/recentVolume.ts +80 -0
  124. data/gems/e11y-devtools/frontend/src/lib/router.ts +12 -0
  125. data/gems/e11y-devtools/frontend/src/lib/transitions.ts +34 -0
  126. data/gems/e11y-devtools/frontend/src/lib/viewportOrigin.ts +25 -0
  127. data/gems/e11y-devtools/frontend/src/main.ts +8 -0
  128. data/gems/e11y-devtools/frontend/src/overlay-entry.ts +24 -0
  129. data/gems/e11y-devtools/frontend/src/overlay.css +1080 -0
  130. data/gems/e11y-devtools/frontend/svelte.config.js +2 -0
  131. data/gems/e11y-devtools/frontend/test_puppeteer.js +41 -0
  132. data/gems/e11y-devtools/frontend/test_scale.js +3 -0
  133. data/gems/e11y-devtools/frontend/tsconfig.app.json +21 -0
  134. data/gems/e11y-devtools/frontend/tsconfig.json +7 -0
  135. data/gems/e11y-devtools/frontend/tsconfig.node.json +26 -0
  136. data/gems/e11y-devtools/frontend/vite.config.ts +36 -0
  137. data/gems/e11y-devtools/lib/e11y/devtools/mcp/server.rb +96 -0
  138. data/gems/e11y-devtools/lib/e11y/devtools/mcp/tool_base.rb +25 -0
  139. data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/clear.rb +31 -0
  140. data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/errors.rb +35 -0
  141. data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/event_detail.rb +33 -0
  142. data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/events_by_trace.rb +33 -0
  143. data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/interactions.rb +40 -0
  144. data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/recent_events.rb +34 -0
  145. data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/search.rb +34 -0
  146. data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/stats.rb +30 -0
  147. data/gems/e11y-devtools/lib/e11y/devtools/overlay/assets/overlay.js +20 -0
  148. data/gems/e11y-devtools/lib/e11y/devtools/overlay/controller.rb +94 -0
  149. data/gems/e11y-devtools/lib/e11y/devtools/overlay/engine.rb +26 -0
  150. data/gems/e11y-devtools/lib/e11y/devtools/overlay/middleware.rb +80 -0
  151. data/gems/e11y-devtools/lib/e11y/devtools/overlay/rails_controller.rb +67 -0
  152. data/gems/e11y-devtools/lib/e11y/devtools/tui/app.rb +262 -0
  153. data/gems/e11y-devtools/lib/e11y/devtools/tui/grouping.rb +66 -0
  154. data/gems/e11y-devtools/lib/e11y/devtools/tui/widgets/event_detail.rb +62 -0
  155. data/gems/e11y-devtools/lib/e11y/devtools/tui/widgets/event_list.rb +70 -0
  156. data/gems/e11y-devtools/lib/e11y/devtools/tui/widgets/interaction_list.rb +47 -0
  157. data/gems/e11y-devtools/lib/e11y/devtools/version.rb +8 -0
  158. data/gems/e11y-devtools/lib/e11y/devtools.rb +13 -0
  159. data/gems/e11y-devtools/spec/e11y/devtools/mcp/tools_spec.rb +107 -0
  160. data/gems/e11y-devtools/spec/e11y/devtools/overlay/controller_spec.rb +91 -0
  161. data/gems/e11y-devtools/spec/e11y/devtools/overlay/middleware_spec.rb +46 -0
  162. data/gems/e11y-devtools/spec/e11y/devtools/tui/app_spec.rb +85 -0
  163. data/gems/e11y-devtools/spec/e11y/devtools/tui/grouping_spec.rb +64 -0
  164. data/gems/e11y-devtools/spec/spec_helper.rb +5 -0
  165. data/gems/e11y-devtools/spec/tui/widgets/event_list_spec.rb +44 -0
  166. data/gems/e11y-devtools/spec/tui/widgets/interaction_list_spec.rb +62 -0
  167. data/lib/e11y/adapters/audit_encrypted.rb +53 -11
  168. data/lib/e11y/adapters/base.rb +33 -34
  169. data/lib/e11y/adapters/dev_log/file_store.rb +143 -0
  170. data/lib/e11y/adapters/dev_log/query.rb +219 -0
  171. data/lib/e11y/adapters/dev_log.rb +118 -0
  172. data/lib/e11y/adapters/file.rb +3 -6
  173. data/lib/e11y/adapters/in_memory.rb +52 -5
  174. data/lib/e11y/adapters/in_memory_test.rb +29 -0
  175. data/lib/e11y/adapters/loki.rb +58 -23
  176. data/lib/e11y/adapters/null.rb +82 -0
  177. data/lib/e11y/adapters/opentelemetry_collector.rb +183 -0
  178. data/lib/e11y/adapters/otel_logs.rb +136 -23
  179. data/lib/e11y/adapters/sentry.rb +4 -7
  180. data/lib/e11y/adapters/stdout.rb +73 -7
  181. data/lib/e11y/adapters/yabeda.rb +153 -29
  182. data/lib/e11y/buffers/adaptive_buffer.rb +3 -17
  183. data/lib/e11y/buffers/{request_scoped_buffer.rb → ephemeral_buffer.rb} +72 -58
  184. data/lib/e11y/buffers/ring_buffer.rb +3 -16
  185. data/lib/e11y/configuration.rb +272 -0
  186. data/lib/e11y/console.rb +10 -17
  187. data/lib/e11y/current.rb +53 -1
  188. data/lib/e11y/debug/pipeline_inspector.rb +96 -0
  189. data/lib/e11y/documentation/generator.rb +48 -0
  190. data/lib/e11y/event/base.rb +176 -82
  191. data/lib/e11y/event/value_sampling_config.rb +1 -5
  192. data/lib/e11y/events/rails/database/query.rb +1 -4
  193. data/lib/e11y/events/rails/job/failed.rb +2 -0
  194. data/lib/e11y/instruments/active_job.rb +44 -12
  195. data/lib/e11y/instruments/rails_instrumentation.rb +49 -24
  196. data/lib/e11y/instruments/sidekiq.rb +135 -31
  197. data/lib/e11y/linters/base.rb +11 -0
  198. data/lib/e11y/linters/pii/pii_declaration_linter.rb +120 -0
  199. data/lib/e11y/linters/slo/config_consistency_linter.rb +76 -0
  200. data/lib/e11y/linters/slo/explicit_declaration_linter.rb +36 -0
  201. data/lib/e11y/linters/slo/slo_status_from_linter.rb +41 -0
  202. data/lib/e11y/logger/bridge.rb +26 -7
  203. data/lib/e11y/metrics/cardinality_protection.rb +10 -15
  204. data/lib/e11y/metrics/cardinality_tracker.rb +16 -6
  205. data/lib/e11y/metrics/registry.rb +3 -5
  206. data/lib/e11y/metrics/test_backend.rb +62 -0
  207. data/lib/e11y/metrics.rb +56 -10
  208. data/lib/e11y/middleware/adapter_resolver.rb +40 -0
  209. data/lib/e11y/middleware/audit_signing.rb +43 -6
  210. data/lib/e11y/middleware/baggage_protection.rb +75 -0
  211. data/lib/e11y/middleware/dev_log_source.rb +24 -0
  212. data/lib/e11y/middleware/event_slo.rb +23 -9
  213. data/lib/e11y/middleware/otel_span.rb +23 -0
  214. data/lib/e11y/middleware/pii_filter.rb +104 -75
  215. data/lib/e11y/middleware/rate_limiting.rb +54 -27
  216. data/lib/e11y/middleware/request.rb +70 -23
  217. data/lib/e11y/middleware/routing.rb +78 -21
  218. data/lib/e11y/middleware/sampling.rb +66 -17
  219. data/lib/e11y/middleware/self_monitoring_emit.rb +39 -0
  220. data/lib/e11y/middleware/trace_context.rb +45 -10
  221. data/lib/e11y/middleware/track_latency.rb +34 -0
  222. data/lib/e11y/middleware/validation.rb +7 -16
  223. data/lib/e11y/middleware/versioning.rb +26 -22
  224. data/lib/e11y/opentelemetry/semantic_conventions.rb +109 -0
  225. data/lib/e11y/opentelemetry/span_creator.rb +142 -0
  226. data/lib/e11y/pii/patterns.rb +12 -1
  227. data/lib/e11y/pipeline/builder.rb +4 -4
  228. data/lib/e11y/presets/audit_event.rb +13 -2
  229. data/lib/e11y/railtie.rb +52 -14
  230. data/lib/e11y/registry.rb +306 -0
  231. data/lib/e11y/reliability/circuit_breaker.rb +19 -21
  232. data/lib/e11y/reliability/dlq/base.rb +71 -0
  233. data/lib/e11y/reliability/dlq/file_adapter.rb +301 -0
  234. data/lib/e11y/reliability/dlq/file_storage.rb +63 -34
  235. data/lib/e11y/reliability/dlq/filter.rb +37 -54
  236. data/lib/e11y/reliability/retry_handler.rb +26 -29
  237. data/lib/e11y/reliability/retry_rate_limiter.rb +3 -11
  238. data/lib/e11y/sampling/error_spike_detector.rb +0 -2
  239. data/lib/e11y/sampling/load_monitor.rb +5 -9
  240. data/lib/e11y/sampling/stratified_tracker.rb +18 -0
  241. data/lib/e11y/self_monitoring/buffer_monitor.rb +2 -0
  242. data/lib/e11y/self_monitoring/performance_monitor.rb +19 -61
  243. data/lib/e11y/self_monitoring/reliability_monitor.rb +4 -74
  244. data/lib/e11y/slo/config_loader.rb +40 -0
  245. data/lib/e11y/slo/config_validator.rb +58 -0
  246. data/lib/e11y/slo/dashboard_generator.rb +122 -0
  247. data/lib/e11y/slo/event_driven.rb +8 -0
  248. data/lib/e11y/slo/tracker.rb +31 -4
  249. data/lib/e11y/testing/have_tracked_event_matcher.rb +190 -0
  250. data/lib/e11y/testing/rspec_matchers.rb +21 -0
  251. data/lib/e11y/testing/snapshot_matcher.rb +86 -0
  252. data/lib/e11y/trace_context/sampler.rb +35 -0
  253. data/lib/e11y/tracing/faraday_middleware.rb +31 -0
  254. data/lib/e11y/tracing/net_http_patch.rb +33 -0
  255. data/lib/e11y/tracing/propagator.rb +144 -0
  256. data/lib/e11y/tracing.rb +47 -0
  257. data/lib/e11y/version.rb +1 -1
  258. data/lib/e11y/versioning/version_extractor.rb +32 -0
  259. data/lib/e11y.rb +123 -266
  260. data/lib/generators/e11y/event/event_generator.rb +22 -0
  261. data/lib/generators/e11y/event/templates/event.rb.tt +16 -0
  262. data/lib/generators/e11y/grafana_dashboard/grafana_dashboard_generator.rb +30 -0
  263. data/lib/generators/e11y/grafana_dashboard/templates/e11y_dashboard.json +81 -0
  264. data/lib/generators/e11y/install/install_generator.rb +34 -0
  265. data/lib/generators/e11y/install/templates/e11y.rb +239 -0
  266. data/lib/generators/e11y/prometheus_alerts/prometheus_alerts_generator.rb +29 -0
  267. data/lib/generators/e11y/prometheus_alerts/templates/e11y_alerts.yml +28 -0
  268. data/lib/tasks/e11y_docs.rake +30 -0
  269. data/lib/tasks/e11y_events.rake +71 -0
  270. data/lib/tasks/e11y_lint.rake +91 -0
  271. data/lib/tasks/e11y_slo.rake +29 -0
  272. metadata +186 -39
  273. data/docs/ADR-003-slo-observability.md +0 -3337
  274. data/docs/ADR-010-developer-experience.md +0 -2166
  275. data/docs/API-REFERENCE-L28.md +0 -914
  276. data/docs/COMPREHENSIVE-CONFIGURATION.md +0 -2366
  277. data/docs/CONTRIBUTING.md +0 -312
  278. data/docs/IMPLEMENTATION_NOTES.md +0 -2804
  279. data/docs/IMPLEMENTATION_PLAN.md +0 -1971
  280. data/docs/IMPLEMENTATION_PLAN_ARCHITECTURE.md +0 -586
  281. data/docs/PLAN.md +0 -148
  282. data/docs/README.md +0 -296
  283. data/docs/design/00-memory-optimization.md +0 -593
  284. data/docs/guides/MIGRATION-L27-L28.md +0 -692
  285. data/docs/guides/PERFORMANCE-BENCHMARKS.md +0 -434
  286. data/docs/guides/README.md +0 -44
  287. data/docs/use_cases/UC-003-pattern-based-metrics.md +0 -1627
  288. data/lib/e11y/adapters/registry.rb +0 -141
data/RELEASE.md CHANGED
@@ -20,14 +20,29 @@ For more control, see [Step-by-Step Release](#step-by-step-release) below.
20
20
 
21
21
  ## Pre-Release Checklist
22
22
 
23
+ ### Core
23
24
  - [ ] All changes documented in CHANGELOG.md under `[Unreleased]`
24
25
  - [ ] Version bumped: `rake release:bump`
25
- - [ ] All tests passing: `rake spec`
26
+ - [ ] All tests passing: `rake spec:all` (unit + integration)
27
+ - [ ] RuboCop clean: `bundle exec rubocop`
26
28
  - [ ] Changes committed
27
29
  - [ ] Git tag created
28
30
  - [ ] Published to RubyGems.org
29
31
  - [ ] GitHub release created
30
32
 
33
+ ### Documentation & Links
34
+ - [ ] All doc links valid (ADRs in `docs/architecture/`, use cases in `docs/use_cases/`)
35
+ - [ ] No broken references to deleted files (e.g. `docs/analysis/`, `docs/IMPLEMENTATION_PLAN.md`)
36
+ - [ ] README, CONTRIBUTING, CLAUDE.md reference correct paths
37
+ - [ ] All user-facing text in English (no Russian in docs/code comments)
38
+
39
+ ### Production Readiness
40
+ - [ ] SECURITY.md present (if handling sensitive data)
41
+ - [ ] LICENSE file present and correct
42
+ - [ ] No TODO/FIXME in critical paths
43
+ - [ ] Deprecation warnings documented (if any)
44
+ - [ ] Breaking changes clearly called out in CHANGELOG
45
+
31
46
  ## Step-by-Step Release
32
47
 
33
48
  ### Step 0: Bump Version
@@ -53,7 +68,7 @@ git commit -m "Bump version to 0.2.0"
53
68
 
54
69
  ### Step 1: Prepare Release
55
70
 
56
- Run tests, build gem, create tag:
71
+ Run tests, build both gems, create tag:
57
72
 
58
73
  ```bash
59
74
  rake release:prep
@@ -62,8 +77,14 @@ rake release:prep
62
77
  This will:
63
78
  - ✅ Check git status (fails if uncommitted changes)
64
79
  - ✅ Run full test suite
65
- - ✅ Build gem file
66
- - ✅ Create annotated git tag
80
+ - ✅ Build **e11y** and **e11y-devtools** `.gem` files (devtools is built under `gems/e11y-devtools/`)
81
+ - ✅ Create annotated git tag `v<e11y-version>` (tag follows the core gem only)
82
+
83
+ Build gems without tests (e.g. after a failed spec run you already trust):
84
+
85
+ ```bash
86
+ rake release:build_gems
87
+ ```
67
88
 
68
89
  Or manually:
69
90
 
@@ -71,8 +92,9 @@ Or manually:
71
92
  # Run all tests
72
93
  bundle exec rspec
73
94
 
74
- # Build gem
95
+ # Build gems
75
96
  gem build e11y.gemspec
97
+ (cd gems/e11y-devtools && gem build e11y-devtools.gemspec)
76
98
 
77
99
  # Create and push tag
78
100
  git tag -a v0.2.0 -m "Release v0.2.0"
@@ -99,10 +121,16 @@ rake release:gem_push
99
121
  ```
100
122
 
101
123
  This will:
102
- - ✅ Verify gem file exists
103
- - ✅ Prompt for confirmation
104
- - ✅ Push gem to RubyGems (requires MFA)
105
- - ✅ Show verification URL
124
+ - ✅ Verify both `.gem` files exist (e11y in repo root, e11y-devtools under `gems/e11y-devtools/`)
125
+ - ✅ Prompt once for confirmation
126
+ - ✅ Push **e11y** first, then **e11y-devtools** (each `gem push` may ask for MFA)
127
+
128
+ Push only one gem if needed:
129
+
130
+ ```bash
131
+ rake release:rubygems:push_core # e11y only
132
+ rake release:rubygems:push_devtools # e11y-devtools only
133
+ ```
106
134
 
107
135
  Or manually:
108
136
 
@@ -118,8 +146,9 @@ Or manually:
118
146
  # Sign in to RubyGems (one-time setup)
119
147
  gem signin
120
148
 
121
- # Push the gem (requires MFA)
149
+ # Push the gems (requires MFA; e11y first — devtools depends on it)
122
150
  gem push e11y-0.1.0.gem
151
+ gem push gems/e11y-devtools/e11y-devtools-0.1.0.gem
123
152
  ```
124
153
 
125
154
  Expected output:
@@ -181,7 +210,7 @@ bundle install
181
210
  ## 📚 Documentation
182
211
 
183
212
  - **Quick Start**: [README.md](https://github.com/arturseletskiy/e11y#quick-start)
184
- - **Architecture**: [docs/ADR-INDEX.md](https://github.com/arturseletskiy/e11y/blob/main/docs/ADR-INDEX.md)
213
+ - **Architecture**: [docs/architecture/ADR-INDEX.md](https://github.com/arturseletskiy/e11y/blob/main/docs/architecture/ADR-INDEX.md)
185
214
  - **Benchmarks**: [benchmarks/README.md](https://github.com/arturseletskiy/e11y/blob/main/benchmarks/README.md)
186
215
 
187
216
  ## 🔥 What's New
@@ -192,7 +221,7 @@ See [CHANGELOG.md](https://github.com/arturseletskiy/e11y/blob/main/CHANGELOG.md
192
221
  - Event System with dry-schema validation
193
222
  - Pipeline Architecture (middleware-based)
194
223
  - 7 Adapters: Stdout, File, InMemory, Loki, Sentry, OpenTelemetry, Yabeda
195
- - 3 Buffer Types: RingBuffer, RequestScopedBuffer, AdaptiveBuffer
224
+ - 3 Buffer Types: RingBuffer, EphemeralBuffer, AdaptiveBuffer
196
225
  - Reliability: Retry, Circuit Breaker, Dead Letter Queue
197
226
 
198
227
  ### SLO Tracking (Phase 3)
data/Rakefile CHANGED
@@ -9,7 +9,8 @@
9
9
  # rake release:full # Complete release workflow (prep + git_push + gem_push)
10
10
  # rake release:prep # Run tests, build gem, create tag
11
11
  # rake release:git_push # Push to GitHub
12
- # rake release:gem_push # Publish to RubyGems
12
+ # rake release:gem_push # Publish e11y + e11y-devtools to RubyGems
13
+ # rake release:build_gems # Build both .gem packages (no tests)
13
14
  # rake spec:all # Run all test suites
14
15
  # rake spec:unit # Run unit tests only (fast)
15
16
  # rake spec:integration # Run integration tests
@@ -20,6 +21,15 @@ require "bundler/gem_tasks"
20
21
  require "rspec/core/rake_task"
21
22
  require "rubocop/rake_task"
22
23
 
24
+ def e11y_devtools_specs_available?
25
+ # Devtools specs live in monorepo; run them when the directory exists
26
+ # (no need for gem in bundle — spec_helper loads lib via path)
27
+ File.directory?(File.join(__dir__, "gems/e11y-devtools/spec"))
28
+ end
29
+
30
+ # Built with: (cd gems/e11y-devtools && gem build …) — .gem stays in this directory
31
+ E11Y_DEVTOOLS_GEM_DIR = File.expand_path("gems/e11y-devtools", __dir__).freeze
32
+
23
33
  RSpec::Core::RakeTask.new(:spec)
24
34
 
25
35
  RuboCop::RakeTask.new
@@ -36,7 +46,6 @@ namespace :spec do
36
46
  desc "Run integration tests (requires Rails, bundle install --with integration)"
37
47
  task :integration do
38
48
  # Run integration tests with explicit file patterns to avoid loading all specs
39
- # This prevents test pollution from unit test files
40
49
  sh "INTEGRATION=true bundle exec rspec " \
41
50
  "spec/integration/*.rb " \
42
51
  "spec/e11y/adapters/*_spec.rb " \
@@ -49,13 +58,27 @@ namespace :spec do
49
58
  sh "bundle exec rspec spec/e11y/railtie_integration_spec.rb --tag railtie_integration"
50
59
  end
51
60
 
52
- desc "Run all tests (unit + integration + railtie, ~1729 examples)"
61
+ desc "Run all tests (unit + memory + integration + railtie + cucumber)"
53
62
  task :all do
54
63
  puts "\n#{'=' * 80}"
55
64
  puts "Running UNIT tests (spec/e11y + top-level specs)..."
56
65
  puts "#{'=' * 80}\n"
57
66
  Rake::Task["spec:unit"].invoke
58
67
 
68
+ if e11y_devtools_specs_available?
69
+ puts "\n#{'=' * 80}"
70
+ puts "Running E11Y-DEVTOOLS unit tests (gems/e11y-devtools/spec/)..."
71
+ puts "#{'=' * 80}\n"
72
+ Rake::Task["spec:devtools"].invoke
73
+ else
74
+ puts "\n⏭️ Skipping e11y-devtools specs (gems/e11y-devtools/spec/ not found)"
75
+ end
76
+
77
+ puts "\n#{'=' * 80}"
78
+ puts "Running MEMORY tests (allocations, leaks, consumption)..."
79
+ puts "#{'=' * 80}\n"
80
+ Rake::Task["spec:memory"].invoke
81
+
59
82
  puts "\n#{'=' * 80}"
60
83
  puts "Running INTEGRATION tests (spec/integration)..."
61
84
  puts "#{'=' * 80}\n"
@@ -66,6 +89,15 @@ namespace :spec do
66
89
  puts "#{'=' * 80}\n"
67
90
  Rake::Task["spec:railtie"].invoke
68
91
 
92
+ if Rake::Task.task_defined?("cucumber:passing")
93
+ puts "\n#{'=' * 80}"
94
+ puts "Running CUCUMBER tests (features/, exclude @wip)..."
95
+ puts "#{'=' * 80}\n"
96
+ Rake::Task["cucumber:passing"].invoke
97
+ else
98
+ puts "\n⚠️ Skipping Cucumber (bundle install --with development)"
99
+ end
100
+
69
101
  puts "\n#{'=' * 80}"
70
102
  puts "✅ All test suites completed!"
71
103
  puts "#{'=' * 80}\n"
@@ -86,15 +118,31 @@ namespace :spec do
86
118
  sh "bundle exec rspec spec/e11y --tag benchmark"
87
119
  end
88
120
 
89
- desc "Run ALL tests including benchmarks (very slow)"
121
+ desc "Run memory profiling specs (allocations, leaks, consumption)"
122
+ task :memory do
123
+ sh "bundle exec rspec " \
124
+ "spec/e11y/memory_spec.rb " \
125
+ "spec/e11y/event/base_benchmark_spec.rb " \
126
+ "--tag memory --format documentation"
127
+ end
128
+
129
+ desc "Run e11y-devtools unit tests (gems/e11y-devtools/spec/)"
130
+ task :devtools do
131
+ sh "bundle exec rspec gems/e11y-devtools/spec/ --tag ~integration --format progress"
132
+ end
133
+
134
+ desc "Run ALL tests including benchmarks and cucumber (very slow)"
90
135
  task :everything do
91
136
  puts "\n#{'=' * 80}"
92
- puts "Running ALL tests (unit + integration + railtie + benchmarks)"
137
+ puts "Running ALL tests (unit + integration + railtie + cucumber + benchmarks)"
93
138
  puts "#{'=' * 80}\n"
94
139
  Rake::Task["spec:unit"].invoke
140
+ Rake::Task["spec:devtools"].invoke if e11y_devtools_specs_available?
95
141
  Rake::Task["spec:integration"].invoke
96
142
  Rake::Task["spec:railtie"].invoke
143
+ Rake::Task["cucumber:passing"].invoke if Rake::Task.task_defined?("cucumber:passing")
97
144
  Rake::Task["spec:benchmark"].invoke
145
+ Rake::Task["spec:memory"].invoke
98
146
 
99
147
  puts "\n#{'=' * 80}"
100
148
  puts "✅ All test suites including benchmarks completed!"
@@ -136,32 +184,32 @@ namespace :release do
136
184
  task :bump do
137
185
  require_relative "lib/e11y/version"
138
186
  current_version = E11y::VERSION
139
-
187
+
140
188
  puts "\n#{'=' * 80}"
141
189
  puts "📝 Version Bump"
142
190
  puts "#{'=' * 80}\n"
143
191
  puts "Current version: #{current_version}"
144
192
  puts "\nEnter new version (e.g., 0.2.0, 1.0.0):"
145
-
193
+
146
194
  new_version = $stdin.gets.chomp.strip
147
-
195
+
148
196
  if new_version.empty?
149
197
  puts "❌ Error: Version cannot be empty"
150
198
  exit 1
151
199
  end
152
-
200
+
153
201
  unless new_version.match?(/^\d+\.\d+\.\d+$/)
154
202
  puts "❌ Error: Invalid version format. Use semantic versioning (e.g., 0.2.0)"
155
203
  exit 1
156
204
  end
157
-
205
+
158
206
  if new_version == current_version
159
207
  puts "⚠️ Warning: New version is the same as current version"
160
208
  puts "Continue anyway? (y/N)"
161
209
  response = $stdin.gets.chomp.downcase
162
- exit 0 unless response == "y" || response == "yes"
210
+ exit 0 unless %w[y yes].include?(response)
163
211
  end
164
-
212
+
165
213
  puts "\n[1/3] Updating lib/e11y/version.rb..."
166
214
  version_file = "lib/e11y/version.rb"
167
215
  version_content = File.read(version_file)
@@ -171,58 +219,60 @@ namespace :release do
171
219
  )
172
220
  File.write(version_file, updated_version_content)
173
221
  puts "✅ Updated: #{current_version} → #{new_version}"
174
-
222
+
175
223
  puts "\n[2/3] Updating CHANGELOG.md..."
176
224
  changelog_file = "CHANGELOG.md"
177
225
  changelog_content = File.read(changelog_file)
178
-
226
+
179
227
  # Check if there's an [Unreleased] section
228
+ today = Time.now.strftime("%Y-%m-%d")
180
229
  if changelog_content.include?("## [Unreleased]")
181
230
  # Replace [Unreleased] with version and date
182
- today = Time.now.strftime("%Y-%m-%d")
183
231
  updated_changelog = changelog_content.sub(
184
- /## \[Unreleased\]/,
232
+ "## [Unreleased]",
185
233
  "## [#{new_version}] - #{today}"
186
234
  )
187
-
235
+
188
236
  # Add new [Unreleased] section at the top
237
+ unreleased = "## [Unreleased]\n\n### Added\n\n### Changed\n\n### Fixed\n\n"
238
+ unreleased += "### Deprecated\n\n### Removed\n\n### Security\n\n\\1"
189
239
  updated_changelog = updated_changelog.sub(
190
240
  /(## \[#{Regexp.escape(new_version)}\] - #{today})/,
191
- "## [Unreleased]\n\n### Added\n\n### Changed\n\n### Fixed\n\n### Deprecated\n\n### Removed\n\n### Security\n\n\\1"
241
+ unreleased
192
242
  )
193
-
243
+
194
244
  File.write(changelog_file, updated_changelog)
195
245
  puts "✅ Updated CHANGELOG.md:"
196
246
  puts " - [Unreleased] → [#{new_version}] - #{today}"
197
247
  puts " - Added new [Unreleased] section"
198
248
  else
199
249
  # No [Unreleased] section, just add version entry
200
- today = Time.now.strftime("%Y-%m-%d")
201
-
250
+
202
251
  # Find where to insert (after the header, before first version)
203
- if changelog_content =~ /(## \[\d+\.\d+\.\d+\])/
204
- updated_changelog = changelog_content.sub(
205
- /(## \[\d+\.\d+\.\d+\])/,
206
- "## [Unreleased]\n\n### Added\n\n### Changed\n\n### Fixed\n\n### Deprecated\n\n### Removed\n\n### Security\n\n## [#{new_version}] - #{today}\n\n### Added\n- Version bump\n\n\\1"
207
- )
252
+
253
+ if /(## \[\d+\.\d+\.\d+\])/.match?(changelog_content)
254
+ new_section = "## [Unreleased]\n\n### Added\n\n### Changed\n\n### Fixed\n\n"
255
+ new_section += "### Deprecated\n\n### Removed\n\n### Security\n\n"
256
+ new_section += "## [#{new_version}] - #{today}\n\n### Added\n- Version bump\n\n\\1"
257
+ updated_changelog = changelog_content.sub(/(## \[\d+\.\d+\.\d+\])/, new_section)
208
258
  else
209
259
  # No previous versions, add after header
210
260
  header_end = changelog_content.index("\n\n") || 0
211
261
  header = changelog_content[0..header_end]
212
- rest = changelog_content[header_end + 1..-1] || ""
262
+ rest = changelog_content[(header_end + 1)..] || ""
213
263
  updated_changelog = "#{header}\n## [#{new_version}] - #{today}\n\n### Added\n- Initial release\n\n#{rest}"
214
264
  end
215
-
265
+
216
266
  File.write(changelog_file, updated_changelog)
217
267
  puts "✅ Added version [#{new_version}] - #{today} to CHANGELOG.md"
218
268
  end
219
-
269
+
220
270
  puts "\n[3/3] Summary"
221
271
  puts "✅ Version bumped: #{current_version} → #{new_version}"
222
272
  puts "✅ Files updated:"
223
273
  puts " - lib/e11y/version.rb"
224
274
  puts " - CHANGELOG.md"
225
-
275
+
226
276
  puts "\n#{'=' * 80}"
227
277
  puts "Next steps:"
228
278
  puts " 1. Review changes: git diff"
@@ -231,6 +281,31 @@ namespace :release do
231
281
  puts "#{'=' * 80}\n"
232
282
  end
233
283
 
284
+ desc "Build e11y and e11y-devtools .gem files (no tests; devtools built in its directory)"
285
+ task :build_gems do
286
+ require_relative "lib/e11y/version"
287
+ require_relative "gems/e11y-devtools/lib/e11y/devtools/version"
288
+
289
+ puts "\n[build] e11y v#{E11y::VERSION}..."
290
+ unless system("gem build e11y.gemspec")
291
+ puts "❌ Error: Failed to build e11y gem"
292
+ exit 1
293
+ end
294
+
295
+ puts "\n[build] e11y-devtools v#{E11y::Devtools::VERSION}..."
296
+ Dir.chdir(E11Y_DEVTOOLS_GEM_DIR) do
297
+ unless system("gem build e11y-devtools.gemspec")
298
+ puts "❌ Error: Failed to build e11y-devtools gem"
299
+ exit 1
300
+ end
301
+ end
302
+
303
+ devtools_artifact = File.join(E11Y_DEVTOOLS_GEM_DIR, "e11y-devtools-#{E11y::Devtools::VERSION}.gem")
304
+ puts "\n✅ Built:"
305
+ puts " - e11y-#{E11y::VERSION}.gem"
306
+ puts " - #{devtools_artifact}"
307
+ end
308
+
234
309
  desc "Prepare release: run tests, build gem, create git tag (safe)"
235
310
  task :prep do
236
311
  require_relative "lib/e11y/version"
@@ -256,13 +331,11 @@ namespace :release do
256
331
  end
257
332
  puts "✅ All tests passed"
258
333
 
259
- # Step 3: Build gem
260
- puts "\n[3/5] Building gem..."
261
- unless system("gem build e11y.gemspec")
262
- puts "❌ Error: Failed to build gem"
263
- exit 1
264
- end
265
- puts "✅ Gem built: e11y-#{version}.gem"
334
+ # Step 3: Build gems (e11y + e11y-devtools)
335
+ puts "\n[3/5] Building gems..."
336
+ Rake::Task["release:build_gems"].invoke
337
+ require_relative "gems/e11y-devtools/lib/e11y/devtools/version"
338
+ puts "✅ Gems built: e11y-#{version}.gem + e11y-devtools-#{E11y::Devtools::VERSION}.gem"
266
339
 
267
340
  # Step 4: Create git tag
268
341
  puts "\n[4/5] Creating git tag..."
@@ -290,28 +363,108 @@ namespace :release do
290
363
  puts " git push origin main"
291
364
  puts " git push origin #{tag_name}"
292
365
  puts " 3. Publish to RubyGems:"
293
- puts " rake release:publish"
366
+ puts " rake release:gem_push"
294
367
  puts "\n"
295
368
  end
296
369
 
297
- desc "Publish gem to RubyGems.org (requires authentication, safe)"
370
+ namespace :rubygems do
371
+ desc "Publish e11y gem only"
372
+ task :push_core do
373
+ require_relative "lib/e11y/version"
374
+ gem_file = "e11y-#{E11y::VERSION}.gem"
375
+
376
+ puts "\n#{'=' * 80}"
377
+ puts "📤 Publishing e11y v#{E11y::VERSION} to RubyGems.org"
378
+ puts "#{'=' * 80}\n"
379
+
380
+ unless File.exist?(gem_file)
381
+ puts "❌ Error: Gem file not found: #{gem_file}"
382
+ puts "Run 'rake release:build_gems' or 'rake release:prep' first"
383
+ exit 1
384
+ end
385
+
386
+ puts "This will publish #{gem_file}"
387
+ puts "You may be prompted for RubyGems credentials and MFA."
388
+ puts "\nContinue? (y/N)"
389
+
390
+ response = $stdin.gets.chomp.downcase
391
+ unless %w[y yes].include?(response)
392
+ puts "❌ Publication cancelled"
393
+ exit 0
394
+ end
395
+
396
+ unless system("gem push #{gem_file}")
397
+ puts "\n❌ Error: Failed to publish e11y"
398
+ exit 1
399
+ end
400
+
401
+ puts "\n✅ Published e11y v#{E11y::VERSION}"
402
+ puts "Verify: https://rubygems.org/gems/e11y/versions/#{E11y::VERSION}"
403
+ end
404
+
405
+ desc "Publish e11y-devtools gem only"
406
+ task :push_devtools do
407
+ require_relative "gems/e11y-devtools/lib/e11y/devtools/version"
408
+ version = E11y::Devtools::VERSION
409
+ gem_file = File.join(E11Y_DEVTOOLS_GEM_DIR, "e11y-devtools-#{version}.gem")
410
+
411
+ puts "\n#{'=' * 80}"
412
+ puts "📤 Publishing e11y-devtools v#{version} to RubyGems.org"
413
+ puts "#{'=' * 80}\n"
414
+
415
+ unless File.exist?(gem_file)
416
+ puts "❌ Error: Gem file not found: #{gem_file}"
417
+ puts "Run 'rake release:build_gems' or 'rake release:prep' first"
418
+ exit 1
419
+ end
420
+
421
+ puts "This will publish #{gem_file}"
422
+ puts "You may be prompted for RubyGems credentials and MFA."
423
+ puts "\nContinue? (y/N)"
424
+
425
+ response = $stdin.gets.chomp.downcase
426
+ unless %w[y yes].include?(response)
427
+ puts "❌ Publication cancelled"
428
+ exit 0
429
+ end
430
+
431
+ unless system("gem push #{gem_file}")
432
+ puts "\n❌ Error: Failed to publish e11y-devtools"
433
+ exit 1
434
+ end
435
+
436
+ puts "\n✅ Published e11y-devtools v#{version}"
437
+ puts "Verify: https://rubygems.org/gems/e11y-devtools/versions/#{version}"
438
+ end
439
+ end
440
+
441
+ desc "Publish e11y then e11y-devtools to RubyGems.org (requires authentication, MFA)"
298
442
  task :gem_push do
299
443
  require_relative "lib/e11y/version"
300
- version = E11y::VERSION
301
- gem_file = "e11y-#{version}.gem"
444
+ require_relative "gems/e11y-devtools/lib/e11y/devtools/version"
445
+
446
+ core_gem = "e11y-#{E11y::VERSION}.gem"
447
+ devtools_gem = File.join(E11Y_DEVTOOLS_GEM_DIR, "e11y-devtools-#{E11y::Devtools::VERSION}.gem")
302
448
 
303
449
  puts "\n#{'=' * 80}"
304
- puts "📤 Publishing e11y v#{version} to RubyGems.org"
450
+ puts "📤 Publishing to RubyGems.org"
305
451
  puts "#{'=' * 80}\n"
306
452
 
307
- unless File.exist?(gem_file)
308
- puts "❌ Error: Gem file not found: #{gem_file}"
309
- puts "Run 'rake release:prep' first"
453
+ unless File.exist?(core_gem)
454
+ puts "❌ Error: Gem file not found: #{core_gem}"
455
+ puts "Run 'rake release:build_gems' or 'rake release:prep' first"
456
+ exit 1
457
+ end
458
+ unless File.exist?(devtools_gem)
459
+ puts "❌ Error: Gem file not found: #{devtools_gem}"
460
+ puts "Run 'rake release:build_gems' or 'rake release:prep' first"
310
461
  exit 1
311
462
  end
312
463
 
313
- puts "This will publish #{gem_file} to RubyGems.org"
314
- puts "You will be prompted for your RubyGems credentials and MFA code."
464
+ puts "This will publish (e11y first, then e11y-devtools):"
465
+ puts " 1. #{core_gem}"
466
+ puts " 2. #{devtools_gem}"
467
+ puts "\nYou may be prompted for RubyGems credentials and MFA for each push."
315
468
  puts "\nContinue? (y/N)"
316
469
 
317
470
  response = $stdin.gets.chomp.downcase
@@ -320,17 +473,19 @@ namespace :release do
320
473
  exit 0
321
474
  end
322
475
 
323
- unless system("gem push #{gem_file}")
324
- puts "\n❌ Error: Failed to publish gem"
325
- puts "Make sure you have:"
326
- puts " 1. RubyGems account (https://rubygems.org/sign_up)"
327
- puts " 2. Signed in: gem signin"
328
- puts " 3. MFA enabled on your account"
476
+ unless system("gem push #{core_gem}")
477
+ puts "\n❌ Error: Failed to publish e11y"
478
+ exit 1
479
+ end
480
+
481
+ unless system("gem push #{devtools_gem}")
482
+ puts "\n❌ Error: Failed to publish e11y-devtools (e11y may already be on RubyGems)"
329
483
  exit 1
330
484
  end
331
485
 
332
- puts "\n✅ Successfully published e11y v#{version} to RubyGems.org!"
333
- puts "\nVerify: https://rubygems.org/gems/e11y/versions/#{version}"
486
+ puts "\n✅ Successfully published both gems!"
487
+ puts " e11y: https://rubygems.org/gems/e11y/versions/#{E11y::VERSION}"
488
+ puts " e11y-devtools: https://rubygems.org/gems/e11y-devtools/versions/#{E11y::Devtools::VERSION}"
334
489
  end
335
490
 
336
491
  desc "Push git changes and tag to GitHub (safe)"
@@ -396,19 +551,56 @@ namespace :release do
396
551
  puts "=" * 80
397
552
  puts "\nPost-release tasks:"
398
553
  puts " 1. Create GitHub release: https://github.com/arturseletskiy/e11y/releases/new"
399
- puts " 2. Verify on RubyGems: https://rubygems.org/gems/e11y"
554
+ puts " 2. Verify on RubyGems: https://rubygems.org/gems/e11y and /gems/e11y-devtools"
400
555
  puts " 3. Update README badges"
401
556
  puts " 4. Announce on social media"
402
557
  puts "\n"
403
558
  end
404
559
 
405
- desc "Clean up built gems"
560
+ desc "Clean up built gem files (repo root + gems/e11y-devtools)"
406
561
  task :clean do
407
562
  puts "🧹 Cleaning up gem files..."
408
563
  FileList["*.gem"].each do |gem_file|
409
564
  File.delete(gem_file)
410
565
  puts " Deleted: #{gem_file}"
411
566
  end
567
+ FileList[File.join(E11Y_DEVTOOLS_GEM_DIR, "*.gem")].each do |gem_file|
568
+ File.delete(gem_file)
569
+ puts " Deleted: #{gem_file}"
570
+ end
412
571
  puts "✅ Clean complete"
413
572
  end
414
573
  end
574
+
575
+ # ---------------------------------------------------------------------------
576
+ # Cucumber acceptance tests
577
+ # ---------------------------------------------------------------------------
578
+ begin
579
+ require "cucumber/rake/task"
580
+
581
+ namespace :cucumber do
582
+ desc "Run all Cucumber acceptance tests"
583
+ Cucumber::Rake::Task.new(:all) do |t|
584
+ t.cucumber_opts = ["--format", "progress", "features/"]
585
+ end
586
+
587
+ desc "Run only @wip (known-bug) Cucumber scenarios"
588
+ Cucumber::Rake::Task.new(:wip) do |t|
589
+ t.cucumber_opts = ["--tags", "@wip", "--format", "progress", "features/"]
590
+ end
591
+
592
+ desc "Run passing Cucumber scenarios (exclude @wip)"
593
+ Cucumber::Rake::Task.new(:passing) do |t|
594
+ # Quote tag expression so shell keeps "not @wip" as one arg (Cucumber::Rake::Task uses cmd.join(' '))
595
+ t.cucumber_opts = ["--tags", '"not @wip"', "--format", "progress", "features/"]
596
+ end
597
+ end
598
+
599
+ desc "Run all Cucumber acceptance tests (alias for cucumber:all)"
600
+ task cucumber: "cucumber:all"
601
+ rescue LoadError
602
+ desc "Cucumber not available — install with: bundle install --with development"
603
+ task :cucumber do
604
+ warn "Cucumber gem is not available. Run: bundle install --with development"
605
+ end
606
+ end
data/config/README.md CHANGED
@@ -80,4 +80,4 @@ Edit `docker-compose.yml` and change port mappings.
80
80
 
81
81
  ⚠️ **This is for TESTING ONLY!** Do not use these configurations in production.
82
82
 
83
- For production setup, see `docs/guides/production-deployment.md` (Phase 5).
83
+ For production setup, see [QUICK-START](../docs/QUICK-START.md) and [CONFIGURATION](../docs/CONFIGURATION.md).
@@ -26,6 +26,18 @@ schema_config:
26
26
  prefix: index_
27
27
  period: 24h
28
28
 
29
+ # Fast flush for integration tests
30
+ ingester:
31
+ chunk_idle_period: 1s # Flush chunks after 1 second of inactivity
32
+ chunk_retain_period: 1s # Keep chunks in memory for 1 second before flush
33
+ max_chunk_age: 2s # Force flush after 2 seconds
34
+ chunk_target_size: 0 # Disable size-based flush delay
35
+ chunk_encoding: snappy
36
+ wal:
37
+ enabled: true
38
+ dir: /tmp/loki/wal
39
+ replay_memory_ceiling: 100MB
40
+
29
41
  ruler:
30
42
  alertmanager_url: http://localhost:9093
31
43
 
@@ -0,0 +1,44 @@
1
+ # OpenTelemetry Collector config for E11y integration tests
2
+ # Receives OTLP (logs, traces, metrics) and exports to debug + Loki
3
+ #
4
+ # Usage: docker compose up -d otel-collector
5
+ # App sends to: http://localhost:4318 (HTTP) or localhost:4317 (gRPC)
6
+
7
+ receivers:
8
+ otlp:
9
+ protocols:
10
+ grpc:
11
+ endpoint: 0.0.0.0:4317
12
+ http:
13
+ endpoint: 0.0.0.0:4318
14
+
15
+ processors:
16
+ batch:
17
+ timeout: 5s
18
+ send_batch_size: 1024
19
+
20
+ exporters:
21
+ debug:
22
+ verbosity: detailed
23
+ sampling_initial: 5
24
+ sampling_thereafter: 200
25
+
26
+ # Forward logs to Loki (same backend as direct Loki adapter)
27
+ # Requires contrib image: otel/opentelemetry-collector-contrib
28
+ loki:
29
+ endpoint: http://loki:3100/loki/api/v1/push
30
+
31
+ service:
32
+ pipelines:
33
+ logs:
34
+ receivers: [otlp]
35
+ processors: [batch]
36
+ exporters: [debug, loki]
37
+ traces:
38
+ receivers: [otlp]
39
+ processors: [batch]
40
+ exporters: [debug]
41
+ metrics:
42
+ receivers: [otlp]
43
+ processors: [batch]
44
+ exporters: [debug]
data/cucumber.yml ADDED
@@ -0,0 +1 @@
1
+ default: --publish-quiet