e11y 0.2.0 → 1.0.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 (230) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +130 -10
  3. data/CHANGELOG.md +56 -1
  4. data/CLAUDE.md +168 -0
  5. data/CONTRIBUTING.md +640 -0
  6. data/README.md +134 -702
  7. data/RELEASE.md +18 -3
  8. data/Rakefile +108 -29
  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 +29 -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} +35 -64
  30. data/docs/{ADR-002-metrics-yabeda.md → architecture/ADR-002-metrics-yabeda.md} +62 -236
  31. data/docs/{ADR-003-slo-observability.md → architecture/ADR-003-slo-observability.md} +27 -466
  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} +209 -339
  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} +41 -83
  40. data/docs/{ADR-013-reliability-error-handling.md → architecture/ADR-013-reliability-error-handling.md} +37 -12
  41. data/docs/{ADR-014-event-driven-slo.md → architecture/ADR-014-event-driven-slo.md} +12 -24
  42. data/docs/{ADR-015-middleware-order.md → architecture/ADR-015-middleware-order.md} +23 -41
  43. data/docs/{ADR-016-self-monitoring-slo.md → architecture/ADR-016-self-monitoring-slo.md} +52 -349
  44. data/docs/{ADR-017-multi-rails-compatibility.md → architecture/ADR-017-multi-rails-compatibility.md} +4 -11
  45. data/docs/architecture/ADR-018-memory-optimization.md +366 -0
  46. data/docs/{ADR-INDEX.md → architecture/ADR-INDEX.md} +11 -6
  47. data/docs/{00-ICP-AND-TIMELINE.md → prd/00-ICP-AND-TIMELINE.md} +6 -6
  48. data/docs/{01-SCALE-REQUIREMENTS.md → prd/01-SCALE-REQUIREMENTS.md} +6 -6
  49. data/docs/prd/01-overview-vision.md +19 -14
  50. data/docs/use_cases/README.md +22 -23
  51. data/docs/use_cases/UC-001-request-scoped-debug-buffering.md +50 -44
  52. data/docs/use_cases/UC-002-business-event-tracking.md +26 -95
  53. data/docs/use_cases/UC-003-event-metrics.md +66 -0
  54. data/docs/use_cases/UC-004-zero-config-slo-tracking.md +42 -101
  55. data/docs/use_cases/UC-005-sentry-integration.md +13 -15
  56. data/docs/use_cases/UC-006-trace-context-management.md +30 -28
  57. data/docs/use_cases/UC-007-pii-filtering.md +35 -87
  58. data/docs/use_cases/UC-008-opentelemetry-integration.md +51 -89
  59. data/docs/use_cases/UC-009-multi-service-tracing.md +4 -4
  60. data/docs/use_cases/UC-010-background-job-tracking.md +5 -5
  61. data/docs/use_cases/UC-011-rate-limiting.md +95 -168
  62. data/docs/use_cases/UC-012-audit-trail.md +21 -46
  63. data/docs/use_cases/UC-013-high-cardinality-protection.md +29 -167
  64. data/docs/use_cases/UC-014-adaptive-sampling.md +2 -2
  65. data/docs/use_cases/UC-015-cost-optimization.md +46 -99
  66. data/docs/use_cases/UC-016-rails-logger-migration.md +39 -213
  67. data/docs/use_cases/UC-017-local-development.md +203 -777
  68. data/docs/use_cases/UC-018-testing-events.md +3 -3
  69. data/docs/use_cases/UC-019-retention-based-routing.md +53 -106
  70. data/docs/use_cases/UC-020-event-versioning.md +8 -9
  71. data/docs/use_cases/UC-021-error-handling-retry-dlq.md +18 -22
  72. data/docs/use_cases/UC-022-event-registry.md +15 -21
  73. data/docs/use_cases/backlog.md +119 -87
  74. data/e11y.gemspec +2 -2
  75. data/gems/e11y-devtools/README.md +136 -0
  76. data/gems/e11y-devtools/config/routes.rb +8 -0
  77. data/gems/e11y-devtools/e11y-devtools.gemspec +25 -0
  78. data/gems/e11y-devtools/exe/e11y +34 -0
  79. data/gems/e11y-devtools/lib/e11y/devtools/mcp/server.rb +96 -0
  80. data/gems/e11y-devtools/lib/e11y/devtools/mcp/tool_base.rb +25 -0
  81. data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/clear.rb +31 -0
  82. data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/errors.rb +35 -0
  83. data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/event_detail.rb +33 -0
  84. data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/events_by_trace.rb +33 -0
  85. data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/interactions.rb +40 -0
  86. data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/recent_events.rb +34 -0
  87. data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/search.rb +34 -0
  88. data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/stats.rb +30 -0
  89. data/gems/e11y-devtools/lib/e11y/devtools/overlay/assets/overlay.js +115 -0
  90. data/gems/e11y-devtools/lib/e11y/devtools/overlay/controller.rb +54 -0
  91. data/gems/e11y-devtools/lib/e11y/devtools/overlay/engine.rb +26 -0
  92. data/gems/e11y-devtools/lib/e11y/devtools/overlay/middleware.rb +80 -0
  93. data/gems/e11y-devtools/lib/e11y/devtools/overlay/rails_controller.rb +42 -0
  94. data/gems/e11y-devtools/lib/e11y/devtools/tui/app.rb +262 -0
  95. data/gems/e11y-devtools/lib/e11y/devtools/tui/grouping.rb +66 -0
  96. data/gems/e11y-devtools/lib/e11y/devtools/tui/widgets/event_detail.rb +62 -0
  97. data/gems/e11y-devtools/lib/e11y/devtools/tui/widgets/event_list.rb +70 -0
  98. data/gems/e11y-devtools/lib/e11y/devtools/tui/widgets/interaction_list.rb +47 -0
  99. data/gems/e11y-devtools/lib/e11y/devtools/version.rb +8 -0
  100. data/gems/e11y-devtools/lib/e11y/devtools.rb +13 -0
  101. data/gems/e11y-devtools/spec/e11y/devtools/mcp/tools_spec.rb +107 -0
  102. data/gems/e11y-devtools/spec/e11y/devtools/overlay/controller_spec.rb +58 -0
  103. data/gems/e11y-devtools/spec/e11y/devtools/overlay/middleware_spec.rb +46 -0
  104. data/gems/e11y-devtools/spec/e11y/devtools/tui/app_spec.rb +85 -0
  105. data/gems/e11y-devtools/spec/e11y/devtools/tui/grouping_spec.rb +64 -0
  106. data/gems/e11y-devtools/spec/spec_helper.rb +5 -0
  107. data/gems/e11y-devtools/spec/tui/widgets/event_list_spec.rb +44 -0
  108. data/gems/e11y-devtools/spec/tui/widgets/interaction_list_spec.rb +62 -0
  109. data/lib/e11y/adapters/audit_encrypted.rb +53 -11
  110. data/lib/e11y/adapters/base.rb +33 -34
  111. data/lib/e11y/adapters/dev_log/file_store.rb +143 -0
  112. data/lib/e11y/adapters/dev_log/query.rb +219 -0
  113. data/lib/e11y/adapters/dev_log.rb +118 -0
  114. data/lib/e11y/adapters/file.rb +3 -6
  115. data/lib/e11y/adapters/in_memory.rb +52 -5
  116. data/lib/e11y/adapters/in_memory_test.rb +29 -0
  117. data/lib/e11y/adapters/loki.rb +58 -23
  118. data/lib/e11y/adapters/null.rb +82 -0
  119. data/lib/e11y/adapters/opentelemetry_collector.rb +183 -0
  120. data/lib/e11y/adapters/otel_logs.rb +136 -23
  121. data/lib/e11y/adapters/sentry.rb +4 -7
  122. data/lib/e11y/adapters/stdout.rb +73 -7
  123. data/lib/e11y/adapters/yabeda.rb +153 -29
  124. data/lib/e11y/buffers/adaptive_buffer.rb +3 -17
  125. data/lib/e11y/buffers/{request_scoped_buffer.rb → ephemeral_buffer.rb} +72 -58
  126. data/lib/e11y/buffers/ring_buffer.rb +3 -16
  127. data/lib/e11y/configuration.rb +272 -0
  128. data/lib/e11y/console.rb +10 -17
  129. data/lib/e11y/current.rb +53 -1
  130. data/lib/e11y/debug/pipeline_inspector.rb +96 -0
  131. data/lib/e11y/documentation/generator.rb +48 -0
  132. data/lib/e11y/event/base.rb +176 -82
  133. data/lib/e11y/event/value_sampling_config.rb +1 -5
  134. data/lib/e11y/events/rails/database/query.rb +1 -4
  135. data/lib/e11y/events/rails/job/failed.rb +2 -0
  136. data/lib/e11y/instruments/active_job.rb +46 -12
  137. data/lib/e11y/instruments/rails_instrumentation.rb +49 -24
  138. data/lib/e11y/instruments/sidekiq.rb +137 -31
  139. data/lib/e11y/linters/base.rb +11 -0
  140. data/lib/e11y/linters/pii/pii_declaration_linter.rb +120 -0
  141. data/lib/e11y/linters/slo/config_consistency_linter.rb +76 -0
  142. data/lib/e11y/linters/slo/explicit_declaration_linter.rb +36 -0
  143. data/lib/e11y/linters/slo/slo_status_from_linter.rb +41 -0
  144. data/lib/e11y/logger/bridge.rb +26 -7
  145. data/lib/e11y/metrics/cardinality_protection.rb +10 -15
  146. data/lib/e11y/metrics/cardinality_tracker.rb +16 -6
  147. data/lib/e11y/metrics/registry.rb +3 -5
  148. data/lib/e11y/metrics/test_backend.rb +62 -0
  149. data/lib/e11y/metrics.rb +56 -10
  150. data/lib/e11y/middleware/adapter_resolver.rb +40 -0
  151. data/lib/e11y/middleware/audit_signing.rb +43 -6
  152. data/lib/e11y/middleware/baggage_protection.rb +75 -0
  153. data/lib/e11y/middleware/dev_log_source.rb +24 -0
  154. data/lib/e11y/middleware/event_slo.rb +23 -9
  155. data/lib/e11y/middleware/otel_span.rb +23 -0
  156. data/lib/e11y/middleware/pii_filter.rb +104 -75
  157. data/lib/e11y/middleware/rate_limiting.rb +54 -27
  158. data/lib/e11y/middleware/request.rb +70 -23
  159. data/lib/e11y/middleware/routing.rb +78 -21
  160. data/lib/e11y/middleware/sampling.rb +66 -17
  161. data/lib/e11y/middleware/self_monitoring_emit.rb +39 -0
  162. data/lib/e11y/middleware/trace_context.rb +45 -10
  163. data/lib/e11y/middleware/track_latency.rb +34 -0
  164. data/lib/e11y/middleware/validation.rb +7 -16
  165. data/lib/e11y/middleware/versioning.rb +26 -22
  166. data/lib/e11y/opentelemetry/semantic_conventions.rb +109 -0
  167. data/lib/e11y/opentelemetry/span_creator.rb +142 -0
  168. data/lib/e11y/pii/patterns.rb +12 -1
  169. data/lib/e11y/pipeline/builder.rb +1 -1
  170. data/lib/e11y/presets/audit_event.rb +13 -2
  171. data/lib/e11y/railtie.rb +52 -15
  172. data/lib/e11y/registry.rb +306 -0
  173. data/lib/e11y/reliability/circuit_breaker.rb +19 -21
  174. data/lib/e11y/reliability/dlq/base.rb +71 -0
  175. data/lib/e11y/reliability/dlq/file_adapter.rb +301 -0
  176. data/lib/e11y/reliability/dlq/file_storage.rb +63 -34
  177. data/lib/e11y/reliability/dlq/filter.rb +37 -54
  178. data/lib/e11y/reliability/retry_handler.rb +26 -29
  179. data/lib/e11y/reliability/retry_rate_limiter.rb +3 -11
  180. data/lib/e11y/sampling/error_spike_detector.rb +0 -2
  181. data/lib/e11y/sampling/load_monitor.rb +5 -9
  182. data/lib/e11y/sampling/stratified_tracker.rb +18 -0
  183. data/lib/e11y/self_monitoring/buffer_monitor.rb +2 -0
  184. data/lib/e11y/self_monitoring/performance_monitor.rb +19 -61
  185. data/lib/e11y/self_monitoring/reliability_monitor.rb +4 -74
  186. data/lib/e11y/slo/config_loader.rb +40 -0
  187. data/lib/e11y/slo/config_validator.rb +58 -0
  188. data/lib/e11y/slo/dashboard_generator.rb +122 -0
  189. data/lib/e11y/slo/event_driven.rb +8 -0
  190. data/lib/e11y/slo/tracker.rb +31 -4
  191. data/lib/e11y/testing/have_tracked_event_matcher.rb +190 -0
  192. data/lib/e11y/testing/rspec_matchers.rb +21 -0
  193. data/lib/e11y/testing/snapshot_matcher.rb +86 -0
  194. data/lib/e11y/trace_context/sampler.rb +35 -0
  195. data/lib/e11y/tracing/faraday_middleware.rb +31 -0
  196. data/lib/e11y/tracing/net_http_patch.rb +33 -0
  197. data/lib/e11y/tracing/propagator.rb +116 -0
  198. data/lib/e11y/tracing.rb +47 -0
  199. data/lib/e11y/version.rb +1 -1
  200. data/lib/e11y/versioning/version_extractor.rb +32 -0
  201. data/lib/e11y.rb +141 -265
  202. data/lib/generators/e11y/event/event_generator.rb +22 -0
  203. data/lib/generators/e11y/event/templates/event.rb.tt +16 -0
  204. data/lib/generators/e11y/grafana_dashboard/grafana_dashboard_generator.rb +30 -0
  205. data/lib/generators/e11y/grafana_dashboard/templates/e11y_dashboard.json +81 -0
  206. data/lib/generators/e11y/install/install_generator.rb +34 -0
  207. data/lib/generators/e11y/install/templates/e11y.rb +239 -0
  208. data/lib/generators/e11y/prometheus_alerts/prometheus_alerts_generator.rb +29 -0
  209. data/lib/generators/e11y/prometheus_alerts/templates/e11y_alerts.yml +28 -0
  210. data/lib/tasks/e11y_docs.rake +30 -0
  211. data/lib/tasks/e11y_events.rake +71 -0
  212. data/lib/tasks/e11y_lint.rake +91 -0
  213. data/lib/tasks/e11y_slo.rake +29 -0
  214. metadata +129 -39
  215. data/docs/ADR-010-developer-experience.md +0 -2166
  216. data/docs/API-REFERENCE-L28.md +0 -914
  217. data/docs/COMPREHENSIVE-CONFIGURATION.md +0 -2366
  218. data/docs/CONTRIBUTING.md +0 -312
  219. data/docs/IMPLEMENTATION_NOTES.md +0 -2804
  220. data/docs/IMPLEMENTATION_PLAN.md +0 -1971
  221. data/docs/IMPLEMENTATION_PLAN_ARCHITECTURE.md +0 -586
  222. data/docs/PLAN.md +0 -148
  223. data/docs/README.md +0 -296
  224. data/docs/design/00-memory-optimization.md +0 -593
  225. data/docs/guides/MIGRATION-L27-L28.md +0 -692
  226. data/docs/guides/PERFORMANCE-BENCHMARKS.md +0 -434
  227. data/docs/guides/README.md +0 -44
  228. data/docs/use_cases/UC-003-pattern-based-metrics.md +0 -1627
  229. data/lib/e11y/adapters/registry.rb +0 -141
  230. /data/docs/{ADR-012-event-evolution.md → architecture/ADR-012-event-evolution.md} +0 -0
@@ -52,7 +52,7 @@ end
52
52
 
53
53
  ## 🎯 Features
54
54
 
55
- > **Implementation:** See [ADR-011: Testing Strategy](../ADR-011-testing-strategy.md) for complete testing architecture, including [Section 3: RSpec Matchers](../ADR-011-testing-strategy.md#3-rspec-matchers), [Section 4: Test Adapters](../ADR-011-testing-strategy.md#4-test-adapters), [Section 6: Snapshot Testing](../ADR-011-testing-strategy.md#6-snapshot-testing), and [Section 8: Integration Testing](../ADR-011-testing-strategy.md#8-integration-testing).
55
+ > **Implementation:** See [ADR-011: Testing Strategy](../architecture/ADR-011-testing-strategy.md) for complete testing architecture, including [Section 3: RSpec Matchers](../architecture/ADR-011-testing-strategy.md#3-rspec-matchers), [Section 4: Test Adapters](../architecture/ADR-011-testing-strategy.md#4-test-adapters), [Section 6: Snapshot Testing](../architecture/ADR-011-testing-strategy.md#6-snapshot-testing), and [Section 8: Integration Testing](../architecture/ADR-011-testing-strategy.md#8-integration-testing).
56
56
 
57
57
  ### 1. RSpec Matchers
58
58
 
@@ -325,7 +325,7 @@ end
325
325
 
326
326
  ### 6. Test Environment Configuration (C13)
327
327
 
328
- > **Implementation:** See [ADR-011 Section 10: Test Environment Configuration](../ADR-011-testing-strategy.md#10-test-environment-configuration) for detailed rationale on disabling production features in tests.
328
+ > **Implementation:** See [ADR-011 Section 10: Test Environment Configuration](../architecture/ADR-011-testing-strategy.md#10-test-environment-configuration) for detailed rationale on disabling production features in tests.
329
329
 
330
330
  **Critical: Production features MUST be disabled in tests for predictability.**
331
331
 
@@ -387,7 +387,7 @@ RSpec.configure do |config|
387
387
  ]
388
388
 
389
389
  # === OPTIONAL: Disable rate limiting ===
390
- config.rate_limiting.enabled = false
390
+ config.rate_limiting_enabled = false
391
391
 
392
392
  # === OPTIONAL: Disable PII filtering (test data is fake) ===
393
393
  config.pii_filtering.enabled = false
@@ -53,24 +53,26 @@ class AuditEvent < E11y::Event::Base
53
53
  end
54
54
  ```
55
55
 
56
- **Centralized Routing:**
56
+ **Routing at collection** (where to write now):
57
57
  ```ruby
58
58
  E11y.configure do |config|
59
59
  config.routing_rules = [
60
60
  ->(event) { :audit_encrypted if event[:audit_event] },
61
61
  ->(event) {
62
62
  days = (Time.parse(event[:retention_until]) - Time.now) / 86400
63
- days > 90 ? :s3_glacier : :loki
63
+ days <= 7 ? :stdout : :loki # Short retention → cheap storage at collection
64
64
  }
65
65
  ]
66
66
  end
67
67
  ```
68
68
 
69
69
  **Result:**
70
- - ✅ **80-97% cost savings** (automatic tiered routing)
71
- - ✅ **Compliance enforcement** (audit → encrypted storage)
70
+ - ✅ **Cost savings** (short retention → stdout, not Loki)
71
+ - ✅ **Compliance** (audit → encrypted storage)
72
72
  - ✅ **Developer experience** (declare intent, routing handles rest)
73
73
 
74
+ > **Archival is a separate moment.** E11y collects events in real time and writes to Loki (hot storage). Each event carries `retention_until` (ISO8601) in its payload. **Archival** (hot → warm → cold) is done by a **separate job** (cron, Loki compaction, export script) — not at collection time. The job filters logs by `retention_until`: `WHERE retention_until > ?` — simple, no custom logic, easy cost control.
75
+
74
76
  ---
75
77
 
76
78
  ## 🎯 Use Case Scenarios
@@ -140,11 +142,8 @@ E11y.configure do |config|
140
142
  # Priority 1: Audit events always to encrypted storage
141
143
  ->(event) { :audit_encrypted if event[:audit_event] },
142
144
 
143
- # Priority 2: Long retention to cold storage
144
- ->(event) {
145
- days = (Time.parse(event[:retention_until]) - Time.now) / 86400
146
- :s3_glacier if days > 90 && !event[:audit_event]
147
- }
145
+ # Other events Loki (archival job handles cold tier later)
146
+ ->(event) { :loki }
148
147
  ]
149
148
  end
150
149
 
@@ -170,8 +169,8 @@ UserDeletedEvent.track(
170
169
  - ✅ **Immutable** → audit trail tamper-proof
171
170
 
172
171
  **Cost Impact:**
173
- - **Before:** Loki storage (30 days, then manual S3) = $5000/month
174
- - **After:** Audit-encrypted + S3 Glacier (automatic) = $50/month
172
+ - **Before:** Loki storage (30 days, manual export) = $5000/month
173
+ - **After:** Audit-encrypted at collection; archival job (separate) exports to cold tier by `retention_until` = $50/month
175
174
  - **Savings:** 99% ($4950/month)
176
175
 
177
176
  ---
@@ -192,37 +191,21 @@ class OrderPlacedEvent < E11y::Event::Base
192
191
  end
193
192
  end
194
193
 
195
- # Configuration
194
+ # At collection: all events → Loki. retention_until in payload.
195
+ # Archival job (cron, separate): filters Loki by retention_until, exports to warm/cold.
196
196
  E11y.configure do |config|
197
- config.routing_rules = [
198
- ->(event) {
199
- days = (Time.parse(event[:retention_until]) - Time.now) / 86400
200
- case days
201
- when 0..30 then :loki # Hot storage
202
- when 31..90 then :s3_standard # Warm storage
203
- else :s3_glacier # Cold storage
204
- end
205
- }
206
- ]
197
+ config.routing_rules = [->(_event) { :loki }]
207
198
  end
208
199
 
209
200
  # Usage
210
- OrderPlacedEvent.track(
211
- order_id: "ORD-123",
212
- amount: 10000,
213
- currency: "USD"
214
- )
215
- # ↓
216
- # retention_until: "2026-04-21T10:30:00Z" (90 days from now)
217
- # ↓
218
- # Routing: days = 90 → :s3_standard adapter (warm storage)
219
- # ↓
220
- # Event written to S3 Standard (cost-optimized)
201
+ OrderPlacedEvent.track(order_id: "ORD-123", amount: 10000, currency: "USD")
202
+ # → Written to Loki with retention_until: "2026-04-21T..."
203
+ # → Archival job (runs later): WHERE retention_until > now + 30d → export to cold
221
204
  ```
222
205
 
223
206
  **Cost Impact:**
224
- - **Before:** Loki only = $200/month
225
- - **After:** Loki (30d) + S3 Standard (60d) = $120/month
207
+ - **Before:** Loki only, no tiering = $200/month
208
+ - **After:** Loki at collection; archival job exports by `retention_until` to cheaper storage = $120/month
226
209
  - **Savings:** 40% ($80/month)
227
210
 
228
211
  ---
@@ -243,17 +226,11 @@ class PaymentFailedEvent < E11y::Event::Base
243
226
  end
244
227
  end
245
228
 
246
- # Configuration
229
+ # At collection: errors → Sentry + Loki. retention_until in payload for archival.
247
230
  E11y.configure do |config|
248
231
  config.routing_rules = [
249
- # Rule 1: Errors always to Sentry
250
- ->(event) { :sentry if event[:severity] == :error },
251
-
252
- # Rule 2: Retention-based storage
253
- ->(event) {
254
- days = (Time.parse(event[:retention_until]) - Time.now) / 86400
255
- days > 30 ? :s3_standard : :loki
256
- }
232
+ ->(event) { [:sentry, :loki] if event[:severity] == :error },
233
+ ->(_event) { :loki }
257
234
  ]
258
235
  end
259
236
 
@@ -268,14 +245,14 @@ PaymentFailedEvent.track(
268
245
  # ↓
269
246
  # Routing:
270
247
  # Rule 1: :sentry (error alerting)
271
- # Rule 2: :s3_standard (90 days storage)
248
+ # Rule 2: :loki (archival job handles tiers later)
272
249
  # ↓
273
250
  # Event written to BOTH adapters
274
251
  ```
275
252
 
276
253
  **Benefits:**
277
254
  - ✅ **Alerting:** Sentry catches errors immediately
278
- - ✅ **Storage:** S3 Standard for 90-day retention
255
+ - ✅ **Storage:** Loki at collection; archival job (separate) exports by `retention_until`
279
256
  - ✅ **Cost:** No duplicate Loki storage ($100/month savings)
280
257
 
281
258
  ---
@@ -345,17 +322,24 @@ CriticalPaymentEvent.track(amount: 100000, user_id: 789)
345
322
  │ │
346
323
  │ Apply routing rules: │
347
324
  │ - Rule 1: audit → encrypted │
348
- │ - Rule 2: >90dcold storage
349
- │ - Rule 3: <30dhot storage
325
+ │ - Rule 2: errorsSentry + Loki
326
+ │ - Rule 3: defaultLoki
350
327
  └─────────────────────────────────┘
351
328
 
352
329
 
353
330
  ┌───────┴───────┐
354
331
  │ │
355
332
  ┌─────▼─────┐ ┌─────▼─────┐
356
- Adapter │ │ Adapter
357
- │ Loki │ │ S3 │
333
+ Loki │ │ Sentry │ (at collection)
358
334
  └───────────┘ └───────────┘
335
+
336
+ │ retention_until in payload
337
+
338
+ ┌─────────────────────────────┐
339
+ │ Archival job (separate) │
340
+ │ Filters by retention_until │
341
+ │ → export to cold storage │
342
+ └─────────────────────────────┘
359
343
  ```
360
344
 
361
345
  ### Component Responsibilities
@@ -365,7 +349,7 @@ CriticalPaymentEvent.track(amount: 100000, user_id: 789)
365
349
  | **Event::Base** | Declare `retention_period`, calculate `retention_until` |
366
350
  | **Configuration** | Define `routing_rules` (lambdas), `default_retention_period` |
367
351
  | **Routing Middleware** | Apply rules, select adapters, write events |
368
- | **Adapters** | Write events to storage (Loki, S3, Sentry, etc.) |
352
+ | **Adapters** | Write events to storage (Loki, File, Sentry, etc.) |
369
353
 
370
354
  ---
371
355
 
@@ -400,73 +384,36 @@ end
400
384
 
401
385
  ```ruby
402
386
  # config/initializers/e11y.rb
387
+ # At collection: route to Loki (or stdout for very short retention).
388
+ # retention_until is in every event payload — archival job (separate) uses it later.
403
389
  E11y.configure do |config|
404
- # Default retention (fallback)
405
390
  config.default_retention_period = 30.days
406
391
 
407
- # Routing rules (evaluated in order)
408
392
  config.routing_rules = [
409
- # Priority 1: Audit events → encrypted storage
410
- ->(event) {
411
- :audit_encrypted if event[:audit_event]
412
- },
413
-
414
- # Priority 2: Errors → Sentry + storage
415
- ->(event) {
416
- [:sentry, :loki] if event[:severity] == :error
417
- },
418
-
419
- # Priority 3: Retention-based tiering
393
+ ->(event) { :audit_encrypted if event[:audit_event] },
394
+ ->(event) { [:sentry, :loki] if event[:severity] == :error },
420
395
  ->(event) {
421
396
  days = (Time.parse(event[:retention_until]) - Time.now) / 86400
422
- case days
423
- when 0..7 then :stdout # Very short → console
424
- when 8..30 then :loki # Short → hot storage
425
- when 31..90 then :s3_standard # Medium → warm storage
426
- else :s3_glacier # Long → cold storage
427
- end
397
+ days <= 7 ? :stdout : :loki # Short → cheap, long → Loki
428
398
  }
429
399
  ]
430
400
 
431
- # Fallback if no rule matches
432
401
  config.fallback_adapters = [:stdout]
433
-
434
- # Register adapters
435
- config.add_adapter :loki, E11y::Adapters::Loki.new(...)
436
- config.add_adapter :s3_standard, E11y::Adapters::File.new(path: 's3://bucket/warm/')
437
- config.add_adapter :s3_glacier, E11y::Adapters::File.new(path: 's3://bucket/cold/')
438
- config.add_adapter :audit_encrypted, E11y::Adapters::AuditEncrypted.new(...)
439
- config.add_adapter :sentry, E11y::Adapters::Sentry.new(...)
402
+ config.adapters[:loki] = E11y::Adapters::Loki.new(...)
403
+ config.adapters[:audit_encrypted] = E11y::Adapters::AuditEncrypted.new(...)
404
+ config.adapters[:sentry] = E11y::Adapters::Sentry.new(...)
440
405
  end
441
406
  ```
442
407
 
443
- ### Step 3: Test Routing
408
+ ### Step 3: Archival Job (Separate Process)
409
+
410
+ Archival runs **later**, not at collection. Example cron job:
444
411
 
445
412
  ```ruby
446
- # spec/e11y/routing_spec.rb
447
- RSpec.describe "Retention-based routing" do
448
- it "routes debug events to stdout" do
449
- event = DebugEvent.track(query: "SELECT...")
450
-
451
- expect(event[:retention_until]).to eq(7.days.from_now.iso8601)
452
- expect(E11y.configuration.adapters[:stdout]).to have_received(:write)
453
- end
454
-
455
- it "routes audit events to encrypted storage" do
456
- event = UserDeletedEvent.track(user_id: 123, deleted_by: 456)
457
-
458
- expect(event[:retention_until]).to eq(7.years.from_now.iso8601)
459
- expect(event[:audit_event]).to be true
460
- expect(E11y.configuration.adapters[:audit_encrypted]).to have_received(:write)
461
- end
462
-
463
- it "routes long retention to cold storage" do
464
- event = BusinessEvent.track(data: "...")
465
- allow(event).to receive(:[]).with(:retention_until).and_return(365.days.from_now.iso8601)
466
-
467
- expect(E11y.configuration.adapters[:s3_glacier]).to have_received(:write)
468
- end
469
- end
413
+ # lib/tasks/archival.rake or separate service
414
+ # Runs daily: reads Loki, filters by retention_until, exports to cold storage
415
+ # SELECT * FROM logs WHERE retention_until > ? AND timestamp < ?
416
+ # export to S3 / object storage
470
417
  ```
471
418
 
472
419
  ---
@@ -506,8 +453,8 @@ end
506
453
 
507
454
  **Monthly Costs:**
508
455
  - Debug logs (7d in stdout): **$0** ✅
509
- - Business events (30d Loki + 60d S3): **$120** ✅
510
- - Audit logs (7y S3 Glacier): **$50** ✅
456
+ - Business events (Loki + archival job exports by retention_until): **$120** ✅
457
+ - Audit logs (audit_encrypted + archival job): **$50** ✅
511
458
  - **Total: $170/month**
512
459
 
513
460
  **Savings: 97% ($5,530/month)**
@@ -573,7 +520,7 @@ end
573
520
 
574
521
  - ✅ **100% of audit events** go to `audit_encrypted` adapter
575
522
  - ✅ **Debug logs** (7d retention) → stdout (free)
576
- - ✅ **Business events** (90d retention) → tiered storage (Loki + S3)
523
+ - ✅ **Business events** (90d retention) → Loki at collection; archival job exports by `retention_until`
577
524
  - ✅ **Cost reduction** of 80%+ compared to manual adapter selection
578
525
  - ✅ **Zero manual intervention** (routing is automatic)
579
526
 
@@ -186,7 +186,7 @@ end
186
186
 
187
187
  ## 🏗️ Architecture
188
188
 
189
- > **Implementation:** See [ADR-012: Event Evolution](../ADR-012-event-evolution.md) for complete versioning architecture, including [Section 3: Naming Convention](../ADR-012-event-evolution.md#3-naming-convention), [Section 4: Version in Payload](../ADR-012-event-evolution.md#4-version-in-payload), [Section 6: Event Registry Integration](../ADR-012-event-evolution.md#6-event-registry-integration), and [Section 7: Migration Strategy](../ADR-012-event-evolution.md#7-migration-strategy).
189
+ > **Implementation:** See [ADR-012: Event Evolution](../architecture/ADR-012-event-evolution.md) for complete versioning architecture, including [Section 3: Naming Convention](../architecture/ADR-012-event-evolution.md#3-naming-convention), [Section 4: Version in Payload](../architecture/ADR-012-event-evolution.md#4-version-in-payload), [Section 6: Event Registry Integration](../architecture/ADR-012-event-evolution.md#6-event-registry-integration), and [Section 7: Migration Strategy](../architecture/ADR-012-event-evolution.md#7-migration-strategy).
190
190
 
191
191
  ### Version Management
192
192
 
@@ -195,7 +195,7 @@ end
195
195
  │ Event Registry (tracks all versions) │
196
196
  │ │
197
197
  │ event_name: 'order.paid' │
198
- │ ├─ V1: OrderPaidV1
198
+ │ ├─ V1: OrderPaid (no suffix per ADR-012)
199
199
  │ ├─ V2: OrderPaidV2 (current) │
200
200
  │ └─ V3: OrderPaidV3 (future) │
201
201
  │ │
@@ -209,7 +209,7 @@ end
209
209
  │ { │
210
210
  │ "@timestamp": "2026-01-12T10:30:00Z", │
211
211
  │ "event_name": "order.paid", │
212
- │ "event_version": 2, ← Version included!
212
+ │ "v": 2, ← Version (ADR-012 uses v: for V2+)
213
213
  │ "payload": { │
214
214
  │ "order_id": "123", │
215
215
  │ "amount": 99.99, │
@@ -231,9 +231,8 @@ E11y.configure do |config|
231
231
  config.versioning do
232
232
  enabled true
233
233
 
234
- # Include version in event payload
234
+ # Include version in event payload (ADR-012: field name is v: for V2+)
235
235
  include_version_in_payload true
236
- version_field :event_version # Field name
237
236
 
238
237
  # Deprecation warnings
239
238
  warn_on_deprecated_version true
@@ -475,9 +474,9 @@ end
475
474
 
476
475
  **4. Use semantic versioning for major changes**
477
476
  ```ruby
478
- # ✅ GOOD: Major version for major changes
479
- class OrderPaidV1 < E11y::Event::Base
480
- version 1 # Initial version
477
+ # ✅ GOOD: V1 no suffix (ADR-012), V2+ with suffix
478
+ class OrderPaid < E11y::Event::Base
479
+ version 1 # Initial version (no V1 suffix)
481
480
  end
482
481
 
483
482
  class OrderPaidV2 < E11y::Event::Base
@@ -599,7 +598,7 @@ class OrderPaid < E11y::Event::Base # V1
599
598
  # Track deprecation usage
600
599
  Events::DeprecatedEventUsed.track(
601
600
  event_class: 'OrderPaid',
602
- event_version: 1,
601
+ version: 1,
603
602
  service: ENV['SERVICE_NAME']
604
603
  )
605
604
  end
@@ -34,7 +34,7 @@
34
34
 
35
35
  - **Retry Policy:** Exponential backoff with jitter
36
36
  - **Dead Letter Queue:** Failed events stored for later analysis/replay
37
- - **Circuit Breaker:** Prevent cascading failures (already covered in UC-011)
37
+ - **Circuit Breaker:** Prevent cascading failures (see [ADR-013 §5](../architecture/ADR-013-reliability-error-handling.md#5-circuit-breaker))
38
38
  - **Observability:** Metrics for failures, retries, DLQ size
39
39
 
40
40
  **Result:** Zero data loss, resilient to transient failures.
@@ -150,21 +150,17 @@ E11y::DeadLetterQueue.size # => 1000 (mostly garbage)
150
150
 
151
151
  **With DLQ filter (GOOD!):**
152
152
  ```ruby
153
- # Config:
154
- E11y.configure do |config|
155
- config.error_handling.dead_letter_queue.filter do
156
- # Don't save health checks to DLQ
157
- never_save do
158
- event_patterns ['health_check.*', 'ping.*']
159
- end
160
-
161
- # Always save payments
162
- always_save do
163
- event_patterns ['payment.*', 'order.*']
164
- end
165
- end
153
+ # Event DSL: declare DLQ behavior per event class
154
+ class Events::HealthCheck < E11y::Event::Base
155
+ use_dlq false # Never save to DLQ
156
+ end
157
+
158
+ class Events::PaymentFailed < E11y::Event::Base
159
+ use_dlq true # Always save to DLQ
166
160
  end
167
161
 
162
+ # Audit events (Presets::AuditEvent) have use_dlq true by default
163
+
168
164
  # Health checks (not saved to DLQ):
169
165
  1000.times do
170
166
  Events::HealthCheck.track(status: 'ok')
@@ -183,7 +179,7 @@ E11y::DeadLetterQueue.size # => 1 (only payment)
183
179
 
184
180
  ## 🏗️ Architecture
185
181
 
186
- > **Implementation:** See [ADR-013: Reliability & Error Handling](../ADR-013-reliability-error-handling.md) for complete error handling architecture, including retry policy with exponential backoff and jitter, circuit breaker pattern, Dead Letter Queue (DLQ) storage strategies, and self-monitoring metrics.
182
+ > **Implementation:** See [ADR-013: Reliability & Error Handling](../architecture/ADR-013-reliability-error-handling.md) for complete error handling architecture, including retry policy with exponential backoff and jitter, circuit breaker pattern, Dead Letter Queue (DLQ) storage strategies, and self-monitoring metrics.
187
183
 
188
184
  ### Retry Pipeline
189
185
 
@@ -445,7 +441,7 @@ E11y::DeadLetterQueue.replay(
445
441
  ### DLQ Replay with PII & Schema Considerations (C07, C15)
446
442
 
447
443
  > **⚠️ CRITICAL:** DLQ replay requires special handling for PII filtering and schema migrations.
448
- > **See:** [ADR-006 Section 5.6](../ADR-006-security-compliance.md#56-pii-handling-for-event-replay-from-dlq-c07-resolution) for C07 (PII double-hashing), [ADR-012 Section 8](../ADR-012-event-evolution.md#8-schema-migrations-and-dlq-replay-c15-resolution--critical) for C15 (schema migrations).
444
+ > **See:** [ADR-006 Section 5.6](../architecture/ADR-006-security-compliance.md#56-pii-handling-for-event-replay-from-dlq-c07-resolution) for C07 (PII double-hashing), [ADR-012 Section 8](../architecture/ADR-012-event-evolution.md#8-schema-migrations-and-dlq-replay-c15-resolution--critical) for C15 (schema migrations).
449
445
 
450
446
  **Problem 1: PII Double-Hashing on Replay (C07)**
451
447
 
@@ -490,7 +486,7 @@ E11y.configure do |config|
490
486
 
491
487
  # === CRITICAL: Enable replay metadata (C07) ===
492
488
  # Replay service automatically adds flags:
493
- # - :replayed => true (skip transformations)
489
+ # - :dlq_replayed => true (skip transformations)
494
490
  # - :pii_filtered => true (already filtered)
495
491
  mark_replayed_events true # ← Default: true
496
492
  end
@@ -505,13 +501,13 @@ module E11y
505
501
 
506
502
  # ✅ CRITICAL: Add replay metadata flags
507
503
  event_data[:metadata] ||= {}
508
- event_data[:metadata][:replayed] = true
504
+ event_data[:metadata][:dlq_replayed] = true
509
505
  event_data[:metadata][:pii_filtered] = true # Already filtered!
510
506
  event_data[:metadata][:replayed_at] = Time.now.utc.iso8601
511
507
  event_data[:metadata][:original_event_id] = event_data[:event_id]
512
508
 
513
509
  # Send through pipeline
514
- # PII filter middleware will skip (checks :replayed flag)
510
+ # PII filter middleware will skip (checks :dlq_replayed flag)
515
511
  E11y::Pipeline.process(event_data)
516
512
  end
517
513
  end
@@ -535,7 +531,7 @@ class PiiFilter < Base
535
531
 
536
532
  def already_filtered?(event_data)
537
533
  metadata = event_data[:metadata] || {}
538
- metadata[:replayed] || metadata[:pii_filtered]
534
+ metadata[:dlq_replayed] || metadata[:pii_filtered]
539
535
  end
540
536
  end
541
537
 
@@ -603,7 +599,7 @@ E11y::DeadLetterQueue.replay_all
603
599
  # Lenient validation for replayed events
604
600
  # (user chooses to allow old schema)
605
601
  lenient_mode_if do |event_data|
606
- event_data.dig(:metadata, :replayed) == true
602
+ event_data.dig(:metadata, :dlq_replayed) == true
607
603
  end
608
604
  end
609
605
  end
@@ -637,7 +633,7 @@ E11y::DeadLetterQueue.replay_all
637
633
  | Decision | Pro | Con | Mitigation |
638
634
  |----------|-----|-----|------------|
639
635
  | **Metadata flags** | Simple, automatic | Metadata size +24 bytes | Acceptable overhead |
640
- | **`:replayed` flag** | Clear intent | None | ✅ Best practice |
636
+ | **`:dlq_replayed` flag** | Clear intent | None | ✅ Best practice |
641
637
  | **Skip PII filter** | Prevents double-hashing | Must trust DLQ integrity | DLQ stored securely (encrypted) |
642
638
 
643
639
  **Trade-offs (C15):**
@@ -18,10 +18,9 @@
18
18
  - Need to grep codebase to find event classes
19
19
  - Hard to document all events
20
20
 
21
- 2. **No runtime introspection**
21
+ 2. **No runtime discovery**
22
22
  - Can't list all registered events at runtime
23
23
  - Can't find event class by name
24
- - Can't inspect event schema programmatically
25
24
 
26
25
  3. **Hard to build tooling**
27
26
  - Can't build event explorer UI (no registry)
@@ -34,7 +33,7 @@
34
33
 
35
34
  - Automatic registration of all event classes
36
35
  - Query registry by event name, version, adapter
37
- - Schema introspection (fields, types, validations)
36
+ - Query by name, severity, adapter (find, where)
38
37
  - Build developer tools (event explorer, documentation generator)
39
38
 
40
39
  **Result:** Full visibility into all events in the system.
@@ -53,7 +52,7 @@ $ grep -r "class.*< E11y::Event::Base" app/events/
53
52
  # → Manual, error-prone, outdated
54
53
 
55
54
  # With registry (AUTOMATIC):
56
- E11y::Registry.all_events
55
+ E11y::Registry.event_classes
57
56
  # => [
58
57
  # Events::OrderCreated,
59
58
  # Events::OrderPaid,
@@ -63,7 +62,7 @@ E11y::Registry.all_events
63
62
  # ]
64
63
 
65
64
  # Generate documentation:
66
- E11y::Registry.all_events.each do |event_class|
65
+ E11y::Registry.event_classes.each do |event_class|
67
66
  puts "## #{event_class.event_name}"
68
67
  puts "Version: #{event_class.version}"
69
68
  puts "Schema: #{event_class.schema_definition}"
@@ -135,7 +134,7 @@ event.severity_level
135
134
  # Rails controller for event explorer
136
135
  class EventExplorerController < ApplicationController
137
136
  def index
138
- @events = E11y::Registry.all_events.map do |event_class|
137
+ @events = E11y::Registry.event_classes.map do |event_class|
139
138
  {
140
139
  name: event_class.event_name,
141
140
  version: event_class.version,
@@ -193,8 +192,8 @@ end
193
192
  │ │
194
193
  │ Indexes: │
195
194
  │ - by_name: 'order.created' → Events::OrderCreatedV2 │
196
- │ - by_adapter: :sentry → [Events::PaymentFailed, ...]
197
- │ - by_severity: :error → [Events::SystemError, ...]
195
+ │ - where(adapter: :sentry) → [Events::PaymentFailed, ...]
196
+ │ - where(severity: :error) → [Events::SystemError, ...]
198
197
  │ - by_version: 2 → [Events::OrderCreatedV2, ...] │
199
198
  └─────────────────────────────────────────────────────────────────┘
200
199
  ```
@@ -239,7 +238,6 @@ E11y.configure do |config|
239
238
  ]
240
239
 
241
240
  # Registry features
242
- enable_introspection true
243
241
  enable_event_explorer true # Web UI at /e11y/events
244
242
  end
245
243
  end
@@ -249,13 +247,13 @@ end
249
247
 
250
248
  ## 📝 Registry API
251
249
 
252
- > **Implementation:** See [ADR-010 Section 5: Event Registry](../ADR-010-developer-experience.md#5-event-registry) for full registry architecture, including event discovery API, introspection, version tracking, and dynamic dispatch.
250
+ > **Implementation:** See [ADR-010 Section 5: Event Registry](../architecture/ADR-010-developer-experience.md#5-event-registry) for full registry architecture (find, event_classes, where, to_documentation).
253
251
 
254
252
  ### Query Events
255
253
 
256
254
  ```ruby
257
255
  # === List All Events ===
258
- E11y::Registry.all_events
256
+ E11y::Registry.event_classes
259
257
  # => [Events::OrderCreated, Events::OrderPaid, ...]
260
258
 
261
259
  E11y::Registry.count
@@ -278,10 +276,6 @@ E11y::Registry.where(severity: :error)
278
276
  E11y::Registry.where(version: 2)
279
277
  # => [Events::OrderCreatedV2, Events::OrderPaidV2, ...]
280
278
 
281
- # === Search ===
282
- E11y::Registry.search('payment')
283
- # => [Events::PaymentProcessed, Events::PaymentFailed, ...]
284
-
285
279
  # === Filtering ===
286
280
  E11y::Registry.filter do |event_class|
287
281
  event_class.adapters.include?(:sentry) &&
@@ -427,7 +421,7 @@ namespace :e11y do
427
421
  output.puts "Total events: #{E11y::Registry.count}"
428
422
  output.puts
429
423
 
430
- E11y::Registry.all_events.each do |event_class|
424
+ E11y::Registry.event_classes.each do |event_class|
431
425
  output.puts "## #{event_class.event_name}"
432
426
  output.puts
433
427
  output.puts "**Version:** #{event_class.version}"
@@ -475,7 +469,7 @@ namespace :e11y do
475
469
  task validate: :environment do
476
470
  errors = []
477
471
 
478
- E11y::Registry.all_events.each do |event_class|
472
+ E11y::Registry.event_classes.each do |event_class|
479
473
  # Check: has example payload
480
474
  if event_class.example_payloads.empty?
481
475
  errors << "#{event_class.name} has no example payloads"
@@ -522,7 +516,7 @@ namespace :e11y do
522
516
  paths: {}
523
517
  }
524
518
 
525
- E11y::Registry.all_events.each do |event_class|
519
+ E11y::Registry.event_classes.each do |event_class|
526
520
  spec[:paths]["/events/#{event_class.event_name}"] = {
527
521
  post: {
528
522
  summary: "Track #{event_class.event_name} event",
@@ -557,9 +551,9 @@ end
557
551
 
558
552
  ```ruby
559
553
  RSpec.describe E11y::Registry do
560
- describe '.all_events' do
554
+ describe '.event_classes' do
561
555
  it 'returns all registered events' do
562
- events = E11y::Registry.all_events
556
+ events = E11y::Registry.event_classes
563
557
 
564
558
  expect(events).to include(Events::OrderCreated)
565
559
  expect(events).to include(Events::OrderPaid)
@@ -636,7 +630,7 @@ end
636
630
 
637
631
  - [ ] Enable registry in config
638
632
  - [ ] Enable eager loading of event classes
639
- - [ ] Access registry: `E11y::Registry.all_events`
633
+ - [ ] Access registry: `E11y::Registry.event_classes`
640
634
  - [ ] Enable event explorer UI (development only)
641
635
  - [ ] Set up documentation generator rake task
642
636
  - [ ] Run validation in CI: `rake e11y:validate`