tasker-rb 0.1.3-x86_64-linux

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 (63) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +55 -0
  3. data/lib/tasker_core/batch_processing/batch_aggregation_scenario.rb +85 -0
  4. data/lib/tasker_core/batch_processing/batch_worker_context.rb +238 -0
  5. data/lib/tasker_core/bootstrap.rb +394 -0
  6. data/lib/tasker_core/client.rb +165 -0
  7. data/lib/tasker_core/domain_events/base_publisher.rb +220 -0
  8. data/lib/tasker_core/domain_events/base_subscriber.rb +178 -0
  9. data/lib/tasker_core/domain_events/publisher_registry.rb +253 -0
  10. data/lib/tasker_core/domain_events/subscriber_registry.rb +152 -0
  11. data/lib/tasker_core/domain_events.rb +43 -0
  12. data/lib/tasker_core/errors/CLAUDE.md +7 -0
  13. data/lib/tasker_core/errors/common.rb +305 -0
  14. data/lib/tasker_core/errors/error_classifier.rb +61 -0
  15. data/lib/tasker_core/errors.rb +4 -0
  16. data/lib/tasker_core/event_bridge.rb +330 -0
  17. data/lib/tasker_core/handlers.rb +159 -0
  18. data/lib/tasker_core/internal.rb +31 -0
  19. data/lib/tasker_core/logger.rb +234 -0
  20. data/lib/tasker_core/models.rb +337 -0
  21. data/lib/tasker_core/observability/types.rb +158 -0
  22. data/lib/tasker_core/observability.rb +292 -0
  23. data/lib/tasker_core/registry/handler_registry.rb +453 -0
  24. data/lib/tasker_core/registry/resolver_chain.rb +258 -0
  25. data/lib/tasker_core/registry/resolvers/base_resolver.rb +90 -0
  26. data/lib/tasker_core/registry/resolvers/class_constant_resolver.rb +156 -0
  27. data/lib/tasker_core/registry/resolvers/explicit_mapping_resolver.rb +146 -0
  28. data/lib/tasker_core/registry/resolvers/method_dispatch_wrapper.rb +144 -0
  29. data/lib/tasker_core/registry/resolvers/registry_resolver.rb +229 -0
  30. data/lib/tasker_core/registry/resolvers.rb +42 -0
  31. data/lib/tasker_core/registry.rb +12 -0
  32. data/lib/tasker_core/step_handler/api.rb +48 -0
  33. data/lib/tasker_core/step_handler/base.rb +354 -0
  34. data/lib/tasker_core/step_handler/batchable.rb +50 -0
  35. data/lib/tasker_core/step_handler/decision.rb +53 -0
  36. data/lib/tasker_core/step_handler/mixins/api.rb +452 -0
  37. data/lib/tasker_core/step_handler/mixins/batchable.rb +465 -0
  38. data/lib/tasker_core/step_handler/mixins/decision.rb +252 -0
  39. data/lib/tasker_core/step_handler/mixins.rb +66 -0
  40. data/lib/tasker_core/subscriber.rb +212 -0
  41. data/lib/tasker_core/task_handler/base.rb +254 -0
  42. data/lib/tasker_core/tasker_rb.so +0 -0
  43. data/lib/tasker_core/template_discovery.rb +181 -0
  44. data/lib/tasker_core/test_environment.rb +313 -0
  45. data/lib/tasker_core/tracing.rb +166 -0
  46. data/lib/tasker_core/types/batch_processing_outcome.rb +301 -0
  47. data/lib/tasker_core/types/client_types.rb +145 -0
  48. data/lib/tasker_core/types/decision_point_outcome.rb +177 -0
  49. data/lib/tasker_core/types/error_types.rb +72 -0
  50. data/lib/tasker_core/types/simple_message.rb +151 -0
  51. data/lib/tasker_core/types/step_context.rb +328 -0
  52. data/lib/tasker_core/types/step_handler_call_result.rb +307 -0
  53. data/lib/tasker_core/types/step_message.rb +112 -0
  54. data/lib/tasker_core/types/step_types.rb +207 -0
  55. data/lib/tasker_core/types/task_template.rb +240 -0
  56. data/lib/tasker_core/types/task_types.rb +148 -0
  57. data/lib/tasker_core/types.rb +132 -0
  58. data/lib/tasker_core/version.rb +13 -0
  59. data/lib/tasker_core/worker/CLAUDE.md +7 -0
  60. data/lib/tasker_core/worker/event_poller.rb +224 -0
  61. data/lib/tasker_core/worker/in_process_domain_event_poller.rb +271 -0
  62. data/lib/tasker_core.rb +161 -0
  63. metadata +292 -0
@@ -0,0 +1,158 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry-struct'
4
+ require 'dry-types'
5
+
6
+ module TaskerCore
7
+ module Observability
8
+ # Type definitions for observability data structures
9
+ #
10
+ # These types use dry-struct for automatic validation and provide
11
+ # structured access to health, metrics, and configuration data.
12
+ module Types
13
+ include Dry.Types()
14
+
15
+ # ==========================================================================
16
+ # Health Types
17
+ # ==========================================================================
18
+
19
+ # Basic health check response
20
+ class BasicHealth < Dry::Struct
21
+ attribute :status, Types::Strict::String
22
+ attribute :worker_id, Types::Strict::String
23
+ attribute :timestamp, Types::Strict::String
24
+ end
25
+
26
+ # Individual health check result
27
+ class HealthCheck < Dry::Struct
28
+ attribute :status, Types::Strict::String
29
+ attribute :message, Types::Strict::String.optional
30
+ attribute :duration_ms, Types::Strict::Integer
31
+ attribute :last_checked, Types::Strict::String
32
+ end
33
+
34
+ # Worker system information
35
+ class WorkerSystemInfo < Dry::Struct
36
+ attribute :version, Types::Strict::String
37
+ attribute :environment, Types::Strict::String
38
+ attribute :uptime_seconds, Types::Strict::Integer
39
+ attribute :worker_type, Types::Strict::String
40
+ attribute :database_pool_size, Types::Strict::Integer
41
+ attribute :command_processor_active, Types::Strict::Bool
42
+ attribute :supported_namespaces, Types::Strict::Array.of(Types::Strict::String)
43
+ end
44
+
45
+ # Detailed health check response
46
+ class DetailedHealth < Dry::Struct
47
+ attribute :status, Types::Strict::String
48
+ attribute :timestamp, Types::Strict::String
49
+ attribute :worker_id, Types::Strict::String
50
+ attribute :checks, Types::Strict::Hash.map(Types::Strict::String, HealthCheck)
51
+ attribute :system_info, WorkerSystemInfo
52
+ end
53
+
54
+ # ==========================================================================
55
+ # Metrics Types
56
+ # ==========================================================================
57
+
58
+ # Event router statistics
59
+ class EventRouterStats < Dry::Struct
60
+ attribute :total_routed, Types::Strict::Integer
61
+ attribute :durable_routed, Types::Strict::Integer
62
+ attribute :fast_routed, Types::Strict::Integer
63
+ attribute :broadcast_routed, Types::Strict::Integer
64
+ attribute :fast_delivery_errors, Types::Strict::Integer
65
+ attribute :routing_errors, Types::Strict::Integer
66
+ end
67
+
68
+ # In-process event bus statistics
69
+ class InProcessEventBusStats < Dry::Struct
70
+ attribute :total_events_dispatched, Types::Strict::Integer
71
+ attribute :rust_handler_dispatches, Types::Strict::Integer
72
+ attribute :ffi_channel_dispatches, Types::Strict::Integer
73
+ attribute :rust_handler_errors, Types::Strict::Integer
74
+ attribute :ffi_channel_drops, Types::Strict::Integer
75
+ attribute :rust_subscriber_patterns, Types::Strict::Integer
76
+ attribute :rust_handler_count, Types::Strict::Integer
77
+ attribute :ffi_subscriber_count, Types::Strict::Integer
78
+ end
79
+
80
+ # Domain event statistics
81
+ class DomainEventStats < Dry::Struct
82
+ attribute :router, EventRouterStats
83
+ attribute :in_process_bus, InProcessEventBusStats
84
+ attribute :captured_at, Types::Strict::String
85
+ attribute :worker_id, Types::Strict::String
86
+ end
87
+
88
+ # ==========================================================================
89
+ # Template Types
90
+ # ==========================================================================
91
+
92
+ # Template cache statistics
93
+ class CacheStats < Dry::Struct
94
+ attribute :total_entries, Types::Strict::Integer
95
+ attribute :hits, Types::Strict::Integer
96
+ attribute :misses, Types::Strict::Integer
97
+ attribute :evictions, Types::Strict::Integer
98
+ attribute :last_maintenance, Types::Strict::String.optional
99
+ end
100
+
101
+ # Cache operation result
102
+ class CacheOperationResult < Dry::Struct
103
+ attribute :success, Types::Strict::Bool
104
+ attribute :message, Types::Strict::String
105
+ attribute :timestamp, Types::Strict::String
106
+ end
107
+
108
+ # Template validation result
109
+ class TemplateValidation < Dry::Struct
110
+ attribute :valid, Types::Strict::Bool
111
+ attribute :namespace, Types::Strict::String
112
+ attribute :name, Types::Strict::String
113
+ attribute :version, Types::Strict::String
114
+ attribute :handler_count, Types::Strict::Integer
115
+ attribute :issues, Types::Strict::Array.of(Types::Strict::String)
116
+ attribute :handler_metadata, Types::Strict::Hash.optional
117
+ end
118
+
119
+ # ==========================================================================
120
+ # Config Types (TAS-150: whitelist-only safe config exposure)
121
+ # ==========================================================================
122
+
123
+ # Configuration metadata (non-sensitive system info)
124
+ class ConfigMetadata < Dry::Struct
125
+ attribute :timestamp, Types::Strict::String
126
+ attribute :environment, Types::Strict::String
127
+ attribute :version, Types::Strict::String
128
+ end
129
+
130
+ # Non-sensitive auth configuration summary
131
+ class SafeAuthConfig < Dry::Struct
132
+ attribute :enabled, Types::Strict::Bool
133
+ attribute :verification_method, Types::Strict::String
134
+ attribute :jwt_issuer, Types::Strict::String
135
+ attribute :jwt_audience, Types::Strict::String
136
+ attribute :api_key_header, Types::Strict::String
137
+ attribute :api_key_count, Types::Strict::Integer
138
+ attribute :strict_validation, Types::Strict::Bool
139
+ attribute :allowed_algorithms, Types::Strict::Array.of(Types::Strict::String)
140
+ end
141
+
142
+ # Non-sensitive messaging configuration
143
+ class SafeMessagingConfig < Dry::Struct
144
+ attribute :backend, Types::Strict::String
145
+ attribute :queues, Types::Strict::Array.of(Types::Strict::String)
146
+ end
147
+
148
+ # Worker configuration response (safe fields only)
149
+ class RuntimeConfig < Dry::Struct
150
+ attribute :metadata, ConfigMetadata
151
+ attribute :worker_id, Types::Strict::String
152
+ attribute :worker_type, Types::Strict::String
153
+ attribute :auth, SafeAuthConfig
154
+ attribute :messaging, SafeMessagingConfig
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,292 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require_relative 'observability/types'
5
+
6
+ module TaskerCore
7
+ # Observability - Access worker health, metrics, and configuration via FFI
8
+ #
9
+ # TAS-77: This module provides Ruby-friendly access to worker observability data
10
+ # without requiring the HTTP web API. Data is fetched directly from Rust via FFI.
11
+ #
12
+ # @example Check worker health
13
+ # health = TaskerCore::Observability.health_basic
14
+ # puts health.status # => "healthy"
15
+ # puts health.worker_id
16
+ #
17
+ # @example Get detailed health for K8s probes
18
+ # if TaskerCore::Observability.ready?
19
+ # puts "Worker ready to receive requests"
20
+ # end
21
+ #
22
+ # @example Get metrics
23
+ # events = TaskerCore::Observability.event_stats
24
+ # puts "Events routed: #{events.router.total_routed}"
25
+ #
26
+ # @example Query configuration
27
+ # config = TaskerCore::Observability.config
28
+ # puts "Environment: #{config.metadata.environment}"
29
+ #
30
+ # @example List templates
31
+ # templates_json = TaskerCore::Observability.templates_list
32
+ # templates = JSON.parse(templates_json)
33
+ #
34
+ module Observability
35
+ class << self
36
+ # ========================================================================
37
+ # Health Methods
38
+ # ========================================================================
39
+
40
+ # Get basic health status
41
+ #
42
+ # @return [Types::BasicHealth] Basic health response
43
+ def health_basic
44
+ json = TaskerCore::FFI.health_basic
45
+ data = JSON.parse(json)
46
+ Types::BasicHealth.new(
47
+ status: data['status'],
48
+ worker_id: data['worker_id'],
49
+ timestamp: data['timestamp']
50
+ )
51
+ end
52
+
53
+ # Get liveness status (Kubernetes liveness probe)
54
+ #
55
+ # @return [Types::BasicHealth] Liveness response
56
+ def health_live
57
+ json = TaskerCore::FFI.health_live
58
+ data = JSON.parse(json)
59
+ Types::BasicHealth.new(
60
+ status: data['status'],
61
+ worker_id: data['worker_id'],
62
+ timestamp: data['timestamp']
63
+ )
64
+ end
65
+
66
+ # Get readiness status (Kubernetes readiness probe)
67
+ #
68
+ # @return [Types::DetailedHealth] Detailed health with checks
69
+ def health_ready
70
+ json = TaskerCore::FFI.health_ready
71
+ parse_detailed_health(json)
72
+ end
73
+
74
+ # Get detailed health information
75
+ #
76
+ # @return [Types::DetailedHealth] Comprehensive health data
77
+ def health_detailed
78
+ json = TaskerCore::FFI.health_detailed
79
+ parse_detailed_health(json)
80
+ end
81
+
82
+ # Check if worker is ready to receive requests
83
+ #
84
+ # @return [Boolean] true if worker is ready
85
+ def ready?
86
+ health_ready.status == 'healthy'
87
+ rescue StandardError
88
+ false
89
+ end
90
+
91
+ # Check if worker is alive
92
+ #
93
+ # @return [Boolean] true if worker is alive
94
+ def alive?
95
+ # Liveness probe returns 'alive' status, not 'healthy'
96
+ health_live.status == 'alive'
97
+ rescue StandardError
98
+ false
99
+ end
100
+
101
+ # ========================================================================
102
+ # Metrics Methods
103
+ # ========================================================================
104
+
105
+ # Get worker metrics as JSON string
106
+ #
107
+ # @return [String] JSON-formatted metrics
108
+ def metrics_worker
109
+ TaskerCore::FFI.metrics_worker
110
+ end
111
+
112
+ # Get domain event statistics
113
+ #
114
+ # @return [Types::DomainEventStats] Event routing statistics
115
+ def event_stats
116
+ json = TaskerCore::FFI.metrics_events
117
+ data = JSON.parse(json)
118
+
119
+ Types::DomainEventStats.new(
120
+ router: Types::EventRouterStats.new(
121
+ total_routed: data['router']['total_routed'],
122
+ durable_routed: data['router']['durable_routed'],
123
+ fast_routed: data['router']['fast_routed'],
124
+ broadcast_routed: data['router']['broadcast_routed'],
125
+ fast_delivery_errors: data['router']['fast_delivery_errors'],
126
+ routing_errors: data['router']['routing_errors']
127
+ ),
128
+ in_process_bus: Types::InProcessEventBusStats.new(
129
+ total_events_dispatched: data['in_process_bus']['total_events_dispatched'],
130
+ rust_handler_dispatches: data['in_process_bus']['rust_handler_dispatches'],
131
+ ffi_channel_dispatches: data['in_process_bus']['ffi_channel_dispatches'],
132
+ rust_handler_errors: data['in_process_bus']['rust_handler_errors'],
133
+ ffi_channel_drops: data['in_process_bus']['ffi_channel_drops'],
134
+ rust_subscriber_patterns: data['in_process_bus']['rust_subscriber_patterns'],
135
+ rust_handler_count: data['in_process_bus']['rust_handler_count'],
136
+ ffi_subscriber_count: data['in_process_bus']['ffi_subscriber_count']
137
+ ),
138
+ captured_at: data['captured_at'],
139
+ worker_id: data['worker_id']
140
+ )
141
+ end
142
+
143
+ # Get Prometheus-formatted metrics
144
+ #
145
+ # @return [String] Prometheus text format metrics
146
+ def prometheus_metrics
147
+ TaskerCore::FFI.metrics_prometheus
148
+ end
149
+
150
+ # ========================================================================
151
+ # Template Methods
152
+ # ========================================================================
153
+
154
+ # List all templates as JSON
155
+ #
156
+ # @param include_cache_stats [Boolean] Include cache statistics
157
+ # @return [String] JSON-formatted template list
158
+ def templates_list(include_cache_stats: false)
159
+ TaskerCore::FFI.templates_list(include_cache_stats)
160
+ end
161
+
162
+ # Get a specific template as JSON
163
+ #
164
+ # @param namespace [String] Template namespace
165
+ # @param name [String] Template name
166
+ # @param version [String] Template version
167
+ # @return [String] JSON-formatted template
168
+ def template_get(namespace:, name:, version:)
169
+ TaskerCore::FFI.template_get(namespace, name, version)
170
+ end
171
+
172
+ # Validate a template for worker execution
173
+ #
174
+ # @param namespace [String] Template namespace
175
+ # @param name [String] Template name
176
+ # @param version [String] Template version
177
+ # @return [Types::TemplateValidation] Validation result
178
+ def template_validate(namespace:, name:, version:)
179
+ json = TaskerCore::FFI.template_validate(namespace, name, version)
180
+ data = JSON.parse(json)
181
+
182
+ Types::TemplateValidation.new(
183
+ valid: data['valid'],
184
+ namespace: data['namespace'],
185
+ name: data['name'],
186
+ version: data['version'],
187
+ handler_count: data['handler_count'],
188
+ issues: data['issues'] || [],
189
+ handler_metadata: data['handler_metadata']
190
+ )
191
+ end
192
+
193
+ # Get template cache statistics
194
+ #
195
+ # @return [Types::CacheStats] Cache statistics
196
+ def cache_stats
197
+ json = TaskerCore::FFI.templates_cache_stats
198
+ data = JSON.parse(json)
199
+
200
+ # Map Rust field names to Ruby struct names
201
+ Types::CacheStats.new(
202
+ total_entries: data['total_cached'] || 0,
203
+ hits: data['cache_hits'] || 0,
204
+ misses: data['cache_misses'] || 0,
205
+ evictions: data['cache_evictions'] || 0,
206
+ last_maintenance: nil # Not provided by Rust API
207
+ )
208
+ end
209
+
210
+ # TAS-169: Removed cache_clear and template_refresh methods.
211
+ # Cache operations are now internal-only. Restart worker to refresh templates.
212
+ # Distributed cache status is available via health_detailed.
213
+
214
+ # ========================================================================
215
+ # Config Methods
216
+ # ========================================================================
217
+
218
+ # Get runtime configuration (safe fields only, no secrets)
219
+ #
220
+ # @return [Types::RuntimeConfig] Configuration data
221
+ def config
222
+ json = TaskerCore::FFI.config_runtime
223
+ data = JSON.parse(json)
224
+
225
+ Types::RuntimeConfig.new(
226
+ metadata: Types::ConfigMetadata.new(
227
+ timestamp: data['metadata']['timestamp'],
228
+ environment: data['metadata']['environment'],
229
+ version: data['metadata']['version']
230
+ ),
231
+ worker_id: data['worker_id'],
232
+ worker_type: data['worker_type'],
233
+ auth: Types::SafeAuthConfig.new(
234
+ enabled: data['auth']['enabled'],
235
+ verification_method: data['auth']['verification_method'],
236
+ jwt_issuer: data['auth']['jwt_issuer'],
237
+ jwt_audience: data['auth']['jwt_audience'],
238
+ api_key_header: data['auth']['api_key_header'],
239
+ api_key_count: data['auth']['api_key_count'],
240
+ strict_validation: data['auth']['strict_validation'],
241
+ allowed_algorithms: data['auth']['allowed_algorithms']
242
+ ),
243
+ messaging: Types::SafeMessagingConfig.new(
244
+ backend: data['messaging']['backend'],
245
+ queues: data['messaging']['queues']
246
+ )
247
+ )
248
+ end
249
+
250
+ # Get the current environment name
251
+ #
252
+ # @return [String] Environment name
253
+ def environment
254
+ TaskerCore::FFI.config_environment
255
+ end
256
+
257
+ private
258
+
259
+ # Parse detailed health JSON into typed struct
260
+ def parse_detailed_health(json)
261
+ data = JSON.parse(json)
262
+
263
+ checks = data['checks'].transform_values do |check|
264
+ Types::HealthCheck.new(
265
+ status: check['status'],
266
+ message: check['message'],
267
+ duration_ms: check['duration_ms'],
268
+ last_checked: check['last_checked']
269
+ )
270
+ end
271
+
272
+ system_info = Types::WorkerSystemInfo.new(
273
+ version: data['system_info']['version'],
274
+ environment: data['system_info']['environment'],
275
+ uptime_seconds: data['system_info']['uptime_seconds'],
276
+ worker_type: data['system_info']['worker_type'],
277
+ database_pool_size: data['system_info']['database_pool_size'],
278
+ command_processor_active: data['system_info']['command_processor_active'],
279
+ supported_namespaces: data['system_info']['supported_namespaces']
280
+ )
281
+
282
+ Types::DetailedHealth.new(
283
+ status: data['status'],
284
+ timestamp: data['timestamp'],
285
+ worker_id: data['worker_id'],
286
+ checks: checks,
287
+ system_info: system_info
288
+ )
289
+ end
290
+ end
291
+ end
292
+ end