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
@@ -12,92 +12,20 @@ This document captures promising ideas for future versions of `e11y` that are no
12
12
 
13
13
  ---
14
14
 
15
- ## 1. Quick Start Presets
15
+ ## Status: What's Already Done (as of v0.2.0)
16
16
 
17
- ### Problem
18
-
19
- Configuration complexity can be overwhelming for new users. Setting up production-ready configuration requires understanding multiple subsystems (sampling, compression, retention, payload optimization).
20
-
21
- ### Proposal
22
-
23
- Provide pre-configured profiles for common scenarios:
24
-
25
- ```ruby
26
- E11y.configure do |config|
27
- # Option 1: Use a preset
28
- config.use_preset :production_high_traffic
29
-
30
- # Option 2: Use a preset with overrides
31
- config.use_preset :production_high_traffic do |preset|
32
- preset.sampling.max_events_per_sec = 5_000 # Override default
33
- end
34
- end
35
- ```
36
-
37
- ### Available Presets
38
-
39
- | Preset | Description | Sample Rate | Compression | Retention |
40
- |--------|-------------|-------------|-------------|-----------|
41
- | `:development` | Local dev, no sampling | 100% | None | 1 day |
42
- | `:staging` | Pre-prod testing | 50% | Gzip | 7 days |
43
- | `:production_low_traffic` | < 1K events/sec | 80% | Gzip | 30 days |
44
- | `:production_high_traffic` | > 10K events/sec | 10% | Zstd | 7 days hot, 90 days warm |
45
- | `:production_cost_optimized` | Aggressive cost reduction | 5% | Zstd level 9 | 3 days hot, 30 days warm |
46
-
47
- ### Implementation Example
48
-
49
- ```ruby
50
- module E11y
51
- module Presets
52
- PRODUCTION_HIGH_TRAFFIC = {
53
- adaptive_sampling: {
54
- enabled: true,
55
- load_based: { max_events_per_sec: 10_000 },
56
- error_based: { enabled: true },
57
- value_based: {
58
- high_value_patterns: [/^payment\./, /^order\./, /^error\./],
59
- low_value_patterns: [/^debug\./, /^health_check/]
60
- }
61
- },
62
- compression: {
63
- enabled: true,
64
- algorithm: :zstd,
65
- level: 3
66
- },
67
- retention_tagging: {
68
- enabled: true,
69
- default_retention: 7.days,
70
- retention_by_pattern: {
71
- 'audit.*' => 7.years,
72
- 'payment.*' => 1.year,
73
- 'debug.*' => 1.day
74
- }
75
- },
76
- payload_minimization: {
77
- enabled: true,
78
- truncate_strings_at: 1000,
79
- truncate_arrays_at: 100,
80
- remove_null_fields: true
81
- }
82
- }.freeze
83
- end
84
- end
85
- ```
86
-
87
- ### Benefits
88
-
89
- - ✅ Faster onboarding (< 5 minutes to production-ready config)
90
- - ✅ Best practices baked in
91
- - ✅ Easy to customize (override specific settings)
92
- - ✅ Reduces configuration errors
93
-
94
- ### Priority
95
-
96
- **Medium (v1.1)**
17
+ | Backlog Item | Status | Notes |
18
+ |--------------|--------|-------|
19
+ | **§1 Sampling Budget** | Not done | |
20
+ | **§2 Global Async** | ❌ Not done | |
21
+ | **§3 Ring Buffer** | ⚠️ Implemented, not integrated | `E11y::Buffers::RingBuffer` exists; Loki uses `[]` + Mutex |
22
+ | **§4 Multi-Tenant** | ⚠️ Partial | Loki adapter has `tenant_id` (X-Scope-OrgID); baggage allows `tenant` key. No full multi-tenant isolation |
97
23
 
98
24
  ---
99
25
 
100
- ## 2. Sampling Budget
26
+ ## 1. Sampling Budget
27
+
28
+ > **Related (implemented):** Adaptive sampling (error-spike, load-based), value-based (`sample_by_value`), per-event `sample_rate`. No budget/cap, no Redis state.
101
29
 
102
30
  ### Problem
103
31
 
@@ -196,15 +124,113 @@ monthly_cost = $1,000 * 30 = $30,000/month
196
124
 
197
125
  ---
198
126
 
199
- ## 3. Additional Ideas (Placeholder)
127
+ ## 2. Global Async Mode and Queue
128
+
129
+ ### Problem
130
+
131
+ Current pipeline is **synchronous**: `Event.track() → Pipeline → Routing → Adapter.write()`. Batching happens only inside individual adapters (Loki, OTel). There is no global async layer between `track()` and adapters.
132
+
133
+ The [01-SCALE-REQUIREMENTS.md](../prd/01-SCALE-REQUIREMENTS.md) document specifies a global async configuration that is **not implemented**:
134
+
135
+ ```ruby
136
+ # Specified but NOT implemented
137
+ E11y.configure do |config|
138
+ config.async do
139
+ queue_size 10_000 # 10k events buffer
140
+ batch_size 500 # Moderate batching
141
+ flush_interval 200 # ms
142
+ worker_threads 1 # Single worker
143
+ end
144
+ end
145
+ ```
146
+
147
+ ### Missing Self-Monitoring Metrics
148
+
149
+ The scale requirements also specify metrics that are not wired:
150
+
151
+ - `E11y.stats.drops_total` — total dropped events
152
+ - `E11y.stats.events_processed_total` — total events processed
153
+ - `e11y_internal_queue_utilization_ratio` — buffer fill level (0–1)
154
+ - `e11y_internal_queue_size` / `e11y_internal_queue_capacity`
155
+
156
+ ### Proposal
157
+
158
+ 1. **Add global async config** — optional `config.async` block with `queue_size`, `batch_size`, `flush_interval`, `worker_threads`
159
+ 2. **Use `RingBuffer`** — `E11y::Buffers::RingBuffer` exists but is unused; it could be the backing store for the global queue
160
+ 3. **Worker thread(s)** — background consumer that pops from `RingBuffer` and writes to adapters in batches
161
+ 4. **Self-monitoring** — expose `e11y_internal_*` metrics for queue health, drops, throughput
162
+
163
+ ### Architecture Options
164
+
165
+ | Option | Description | Complexity |
166
+ |--------|-------------|------------|
167
+ | **A: Keep current** | Batching stays in adapters only | None |
168
+ | **B: Global queue** | Single RingBuffer before routing, worker threads | Medium |
169
+ | **C: Per-adapter queues** | Each adapter has its own RingBuffer + worker | High |
170
+
171
+ ### Benefits
172
+
173
+ - ✅ Decouples `track()` from I/O (network, disk)
174
+ - ✅ Predictable latency under load (<1ms p99 for track)
175
+ - ✅ Backpressure handling (drop_oldest when full)
176
+ - ✅ Self-monitoring (queue utilization, drops)
177
+
178
+ ### Trade-offs
179
+
180
+ - ⚠️ Complexity (thread management, shutdown)
181
+ - ⚠️ Data loss window (events in buffer on crash)
182
+ - ⚠️ Memory overhead (buffer capacity × avg event size)
183
+
184
+ ### Priority
185
+
186
+ **Medium (v1.1)** — Required for small teams at scale; optional for MVP
187
+
188
+ ---
189
+
190
+ ## 3. In-Memory Ring Buffer (Integration)
191
+
192
+ ### Problem
193
+
194
+ `E11y::Buffers::RingBuffer` is **fully implemented** (`lib/e11y/buffers/ring_buffer.rb`) but **not used** anywhere in the pipeline:
195
+
196
+ - **Loki adapter** uses `@buffer = []` + `Mutex` (not RingBuffer)
197
+ - **EphemeralBuffer** uses `Concurrent::Array` in `Thread.current`
198
+ - **RingBuffer** exists only in unit tests and benchmarks
199
+
200
+ PRD Phase 1 (00-ICP-AND-TIMELINE.md) specifies "In-memory ring buffer (SPSC)" as a Week 1–2 deliverable, but it was never integrated.
201
+
202
+ ### RingBuffer Spec
203
+
204
+ - Lock-free SPSC (Single-Producer, Single-Consumer)
205
+ - 100K events capacity (default)
206
+ - Overflow strategies: `:drop_oldest`, `:drop_newest`, `:block`
207
+ - Target: 100K+ events/sec, <10μs p99 per push/pop
208
+
209
+ ### Proposal
210
+
211
+ 1. **Option A: Integrate into global async** — Use RingBuffer as the backing store when `config.async` is enabled (see §2)
212
+ 2. **Option B: Replace Loki buffer** — Swap `[]` + Mutex for RingBuffer in Loki adapter for better throughput
213
+ 3. **Option C: Remove from PRD** — Document that RingBuffer is "future-ready" for async mode; adapter-level batching is sufficient for MVP
214
+
215
+ ### Recommendation
216
+
217
+ **Option A** — RingBuffer is the natural choice for global async queue. Integrate when implementing §2 (Global Async Mode).
218
+
219
+ ### Priority
220
+
221
+ **Medium (v1.1)** — Depends on §2 (Global Async Mode)
222
+
223
+ ---
224
+
225
+ ## 4. Additional Ideas (Placeholder)
200
226
 
201
227
  Future ideas to be added:
202
228
 
203
229
  - **ML-Based Anomaly Detection:** Automatically detect unusual patterns in events
204
230
  - **Event Replay from Storage:** Replay events from cold storage for debugging
205
- - **Multi-Tenant Support:** Isolate events by tenant/customer
206
- - **Event Transformation Rules:** Transform events before sending to adapters
207
- - **Custom Retention Policies:** More granular control over data lifecycle
231
+ - **Multi-Tenant Support:** Isolate events by tenant/customer — *Loki has `tenant_id`; baggage allows `tenant`*
232
+ - **Event Transformation Rules:** Transform events before sending to adapters — *routing + PII filter exist*
233
+ - **Custom Retention Policies:** More granular control over data lifecycle — *`retention_period` per event exists*
208
234
 
209
235
  ---
210
236
 
@@ -217,7 +243,13 @@ Future ideas to be added:
217
243
 
218
244
  ## Related ADRs
219
245
 
220
- - [ADR-009: Cost Optimization](../ADR-009-cost-optimization.md) - Current implementation
246
+ - [ADR-009: Cost Optimization](../architecture/ADR-009-cost-optimization.md) - Current implementation
247
+ - [ADR-001: Architecture](../architecture/ADR-001-architecture.md) - RingBuffer specification (§3.3.1)
248
+
249
+ ## Related Documents
250
+
251
+ - [01-SCALE-REQUIREMENTS.md](../prd/01-SCALE-REQUIREMENTS.md) - Specifies `config.async`, self-monitoring metrics
252
+ - [00-ICP-AND-TIMELINE.md](../prd/00-ICP-AND-TIMELINE.md) - Phase 1 "In-memory ring buffer (SPSC)"
221
253
 
222
254
  ---
223
255
 
data/e11y.gemspec CHANGED
@@ -17,7 +17,7 @@ Gem::Specification.new do |spec|
17
17
  • Schema-validated events - catch bugs before production with dry-schema
18
18
 
19
19
  DEVELOPER EXPERIENCE:
20
- 5-minute setup (not 2-week migration)
20
+ Minimal setup one config block, works with stdout out of the box
21
21
  • Auto-metrics from events (no manual Yabeda.increment)
22
22
  • Rails-first design (follows Rails conventions)
23
23
  • Pluggable adapters (Loki, Sentry, OpenTelemetry, custom backends)
@@ -73,7 +73,7 @@ Gem::Specification.new do |spec|
73
73
  spec.add_development_dependency "rspec", "~> 3.12"
74
74
  spec.add_development_dependency "rubocop", "~> 1.50"
75
75
  spec.add_development_dependency "rubocop-rake", "~> 0.6"
76
- spec.add_development_dependency "rubocop-rspec", "~> 2.22"
76
+ spec.add_development_dependency "rubocop-rspec", "~> 3.0"
77
77
  spec.add_development_dependency "simplecov", "~> 0.22"
78
78
  spec.add_development_dependency "webmock", "~> 3.19" # For HTTP adapter testing
79
79
  spec.add_development_dependency "yard", "~> 0.9"
@@ -0,0 +1,158 @@
1
+ # e11y-devtools
2
+
3
+ Developer tools for [e11y](https://github.com/aseletskiy/e11y) — the Rails observability gem.
4
+
5
+ Three complementary viewers for the same JSONL log:
6
+
7
+ | Viewer | How to use |
8
+ |--------|-----------|
9
+ | **TUI** (terminal) | `bundle exec e11y` |
10
+ | **Browser Overlay** | Automatic in development — floating badge in bottom-right |
11
+ | **MCP Server** | `bundle exec e11y mcp` — AI integration for Cursor / Claude Code |
12
+
13
+ ## Installation
14
+
15
+ Add to your Gemfile (development group only):
16
+
17
+ ```ruby
18
+ # Gemfile
19
+ gem "e11y", "~> 1.0"
20
+ gem "e11y-devtools", "~> 0.1.0", group: :development
21
+ ```
22
+
23
+ Then run `bundle install`.
24
+
25
+ ## TUI — Interactive Log Viewer
26
+
27
+ ```bash
28
+ bundle exec e11y # Open TUI (default)
29
+ bundle exec e11y tui # Same as above
30
+ bundle exec e11y tail # Stream events to stdout
31
+ bundle exec e11y help # Show help
32
+ ```
33
+
34
+ ### TUI Keyboard Shortcuts
35
+
36
+ | Key | Action |
37
+ |-----|--------|
38
+ | `↑` / `k` | Move up |
39
+ | `↓` / `j` | Move down |
40
+ | `Enter` | Drill in (interactions → events → detail) |
41
+ | `Esc` / `b` | Go back |
42
+ | `w` | Filter: web requests only |
43
+ | `j` | Filter: background jobs only |
44
+ | `a` | Filter: all sources |
45
+ | `r` | Reload manually |
46
+ | `c` | Copy event JSON to clipboard (in detail view) |
47
+ | `q` | Quit |
48
+
49
+ ## Browser Overlay
50
+
51
+ When `gem "e11y-devtools"` is in your Gemfile, the overlay appears automatically in development (Rails mounts `E11y::Devtools::Overlay::Engine` at `/_e11y`).
52
+
53
+ The UI is a **Svelte** bundle (`overlay.js`) with:
54
+
55
+ - **FAB** (bottom-right): total events, warn/error counts; brief **pulse** when **new** `warn` or `error`/`fatal` rows appear (not on every poll).
56
+ - **Fullscreen panel**: tabs **Problems** (error/fatal from recent buffer, 1-click → JSON) and **Interactions** (grouped traces like TUI). If there are errors in the recent snapshot, the panel opens on **Problems** first.
57
+ - **Wide viewport** (~900px+): interactions use a **split view** (list left, events right); narrow screens keep the stacked flow.
58
+ - Source filter chips (web / job / all) apply to **Interactions** only.
59
+ - **JSON API** under `/_e11y/v1/`: `GET interactions`, `GET traces/:trace_id/events`, `GET events/recent`.
60
+
61
+ ### Rebuild the overlay asset
62
+
63
+ After changing `gems/e11y-devtools/frontend/`:
64
+
65
+ ```bash
66
+ cd gems/e11y-devtools/frontend && npm install && npm run build
67
+ ```
68
+
69
+ Output: `lib/e11y/devtools/overlay/assets/overlay.js` (served at `/_e11y/overlay.js`).
70
+
71
+ ### Local UI prototype (mocks)
72
+
73
+ ```bash
74
+ cd gems/e11y-devtools/frontend && npm run dev
75
+ ```
76
+
77
+ Uses `public/mocks/v1/*.json` — see `frontend/README.md`.
78
+
79
+ ## MCP Server — AI Integration
80
+
81
+ Start the server:
82
+
83
+ ```bash
84
+ # stdio (for Cursor / Claude Code)
85
+ bundle exec e11y mcp
86
+
87
+ # HTTP (for direct integration)
88
+ bundle exec e11y mcp --port 3099
89
+ ```
90
+
91
+ Add to `.cursor/mcp.json` or `~/.claude/mcp.json`:
92
+
93
+ ```json
94
+ {
95
+ "mcpServers": {
96
+ "e11y": {
97
+ "command": "bundle",
98
+ "args": ["exec", "e11y", "mcp"],
99
+ "cwd": "/path/to/your/rails/app"
100
+ }
101
+ }
102
+ }
103
+ ```
104
+
105
+ ### Available MCP Tools
106
+
107
+ | Tool | Description |
108
+ |------|-------------|
109
+ | `recent_events` | Get latest N events (filterable by severity) |
110
+ | `events_by_trace` | Get all events for a trace ID |
111
+ | `search` | Full-text search across event names and payloads |
112
+ | `stats` | Aggregate statistics (total, by severity, oldest/newest) |
113
+ | `interactions` | Time-grouped interactions (parallel requests) |
114
+ | `event_detail` | Full payload for a single event by ID |
115
+ | `errors` | Recent error/fatal events only — fastest way to see what went wrong |
116
+ | `clear` | Clear the dev log |
117
+
118
+ ## Configuration
119
+
120
+ ```ruby
121
+ # config/initializers/e11y.rb
122
+ E11y.configure do |config|
123
+ config.register_adapter :dev_log, E11y::Adapters::DevLog.new(
124
+ path: Rails.root.join("log", "e11y_dev.jsonl"),
125
+ max_size: ENV.fetch("E11Y_MAX_SIZE", 50).to_i * 1024 * 1024, # 50 MB
126
+ max_lines: ENV.fetch("E11Y_MAX_EVENTS", 10_000).to_i,
127
+ keep_rotated: ENV.fetch("E11Y_KEEP_ROTATED", 5).to_i
128
+ )
129
+ end
130
+ ```
131
+
132
+ ### Environment Variables
133
+
134
+ | Variable | Default | Description |
135
+ |----------|---------|-------------|
136
+ | `E11Y_MAX_EVENTS` | `10000` | Max lines before rotation |
137
+ | `E11Y_MAX_SIZE` | `50` | Max log size in MB before rotation |
138
+ | `E11Y_KEEP_ROTATED` | `5` | Number of compressed `.gz` files to keep |
139
+
140
+ ## Log Format
141
+
142
+ Events are stored as JSONL (one JSON object per line) at `log/e11y_dev.jsonl`.
143
+ Rotated files are numbered and gzip-compressed: `e11y_dev.jsonl.1.gz`, `.2.gz`, etc.
144
+
145
+ ## Architecture
146
+
147
+ ```
148
+ log/e11y_dev.jsonl ← E11y::Adapters::DevLog (write)
149
+
150
+ E11y::Adapters::DevLog::Query (read, cache, search, grouping)
151
+
152
+ ┌─────┴──────┬──────────────┬──────────────┐
153
+ TUI Browser MCP Server
154
+ (ratatui_ruby) Overlay (gem 'mcp')
155
+ (Rack)
156
+ ```
157
+
158
+ The JSONL file is the single source of truth. All viewers are stateless readers — they never write.
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ E11y::Devtools::Overlay::Engine.routes.draw do
4
+ get "overlay.js", to: "e11y/devtools/overlay/rails#overlay_js"
5
+ get "events", to: "e11y/devtools/overlay/rails#events"
6
+ get "events/recent", to: "e11y/devtools/overlay/rails#recent"
7
+ delete "events", to: "e11y/devtools/overlay/rails#clear"
8
+ get "stats", to: "e11y/devtools/overlay/rails#stats"
9
+
10
+ scope "v1" do
11
+ get "interactions", to: "e11y/devtools/overlay/rails#v1_interactions"
12
+ get "traces/:trace_id/events", to: "e11y/devtools/overlay/rails#v1_trace_events"
13
+ get "events/recent", to: "e11y/devtools/overlay/rails#v1_events_recent"
14
+ end
15
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/e11y/devtools/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "e11y-devtools"
7
+ spec.version = E11y::Devtools::VERSION
8
+ spec.authors = ["Artur Seletskiy"]
9
+ spec.summary = "Developer tools for E11y: TUI, Browser Overlay, MCP Server"
10
+
11
+ spec.required_ruby_version = ">= 3.2"
12
+
13
+ spec.files = Dir["lib/**/*.rb", "lib/**/*.js", "config/**/*.rb", "exe/*", "*.md"]
14
+ spec.bindir = "exe"
15
+ spec.executables = ["e11y"]
16
+ spec.require_paths = ["lib"]
17
+
18
+ spec.add_dependency "e11y", "~> #{E11y::Devtools::CORE_VERSION}"
19
+ spec.add_dependency "mcp", ">= 0.8"
20
+ spec.add_dependency "ratatui_ruby", "~> 1.4"
21
+
22
+ # Optional but recommended for performance
23
+ spec.add_development_dependency "oj"
24
+ spec.metadata["rubygems_mfa_required"] = "true"
25
+ end
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "e11y/devtools"
5
+
6
+ command = ARGV.shift || "tui"
7
+
8
+ case command
9
+ when "tui"
10
+ require "e11y/devtools/tui/app"
11
+ E11y::Devtools::Tui::App.new.run
12
+ when "mcp"
13
+ require "e11y/devtools/mcp/server"
14
+ E11y::Devtools::Mcp::Server.new.run(
15
+ transport: ARGV.include?("--port") ? :http : :stdio,
16
+ port: (ARGV[ARGV.index("--port") + 1] if ARGV.include?("--port"))&.to_i
17
+ )
18
+ when "tail"
19
+ require "e11y/devtools/tui/tail"
20
+ E11y::Devtools::Tui::Tail.new.run
21
+ when "help", "--help", "-h"
22
+ puts <<~HELP
23
+ bundle exec e11y [command]
24
+
25
+ Commands:
26
+ tui (default) Interactive TUI — browse events and traces
27
+ mcp MCP server for Cursor / Claude Code AI integration
28
+ tail Stream new events to stdout (pipe-friendly)
29
+ help Show this help
30
+ HELP
31
+ else
32
+ warn "Unknown command: #{command}. Run `bundle exec e11y help`."
33
+ exit 1
34
+ end
@@ -0,0 +1,24 @@
1
+ # Logs
2
+ logs
3
+ *.log
4
+ npm-debug.log*
5
+ yarn-debug.log*
6
+ yarn-error.log*
7
+ pnpm-debug.log*
8
+ lerna-debug.log*
9
+
10
+ node_modules
11
+ dist
12
+ dist-ssr
13
+ *.local
14
+
15
+ # Editor directories and files
16
+ .vscode/*
17
+ !.vscode/extensions.json
18
+ .idea
19
+ .DS_Store
20
+ *.suo
21
+ *.ntvs*
22
+ *.njsproj
23
+ *.sln
24
+ *.sw?
@@ -0,0 +1,51 @@
1
+ # e11y browser overlay — frontend (Svelte)
2
+
3
+ Prototype and production bundle for the dev-only overlay injected into Rails apps at `/_e11y/overlay.js`.
4
+
5
+ ## API mocks (Phase 1)
6
+
7
+ Static JSON mirrors the target `/_e11y/v1/` contract:
8
+
9
+ | Path | File |
10
+ |------|------|
11
+ | `GET /v1/events/recent` | `public/mocks/v1/events/recent.json` |
12
+ | `GET /v1/interactions` | `public/mocks/v1/interactions.json` |
13
+ | `GET /v1/traces/:trace_id/events` | `public/mocks/v1/traces/<trace_id>/events.json` |
14
+
15
+ Trace ids are 32-char hex strings (like `DevLog` / OpenTelemetry). Files live under `public/mocks/v1/traces/<trace_id>/events.json`.
16
+
17
+ **Regenerate mocks** (deterministic `Random.new(42)`):
18
+
19
+ ```bash
20
+ npm run generate-mocks
21
+ # or: E11Y_MOCK_TRACES=52 ruby scripts/generate_mocks.rb # stress (~300+ events)
22
+ ```
23
+
24
+ Default is `E11Y_MOCK_TRACES=24` (~150 events). `interactions.json` is rebuilt using the same 500ms window grouping as `E11y::Adapters::DevLog::Query`.
25
+
26
+ **Overlay UX (dev):** Problems tab includes a stacked **volume bar** over `recent` (UTC time scale, drag to **brush** a range and narrow the error list, double-click or **Clear range** to reset), search, and expandable payload rows. Trace event lists support **severity + text filters**, inline **payload expand**, **±2 context** highlight after opening an event and going back, and detail **Copy trace_id / request_id** plus collapsible payload / metadata / full JSON.
27
+
28
+ ## Setup
29
+
30
+ ```bash
31
+ cd gems/e11y-devtools/frontend
32
+ npm install
33
+ ```
34
+
35
+ ## Dev server
36
+
37
+ ```bash
38
+ npm run dev
39
+ ```
40
+
41
+ Open the URL Vite prints (default `http://localhost:5173`) to load `index.html` with a fake host page and the overlay.
42
+
43
+ ## Production build
44
+
45
+ After the Svelte app is scaffolded (plan Task 2+):
46
+
47
+ ```bash
48
+ npm run build
49
+ ```
50
+
51
+ Output is written to `../lib/e11y/devtools/overlay/assets/overlay.js` for the Rails engine to serve.
@@ -0,0 +1,14 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>e11y overlay (dev)</title>
8
+ </head>
9
+ <body>
10
+ <p style="padding: 1rem; font-family: system-ui">Demo host page — overlay FAB bottom-right.</p>
11
+ <div id="app" class="e11y-demo-host"></div>
12
+ <script type="module" src="/src/main.ts"></script>
13
+ </body>
14
+ </html>