hatchet-sdk 0.3.0 → 0.4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 50f6b879a05c1dcff6205d3c2a73f028a1240b6c3a9684bba3c12e44fdd60b95
4
- data.tar.gz: 199bcb67d317658dab9d0ae60df2b9283c35fe244db5dc90957b266ae9e70b94
3
+ metadata.gz: b4ab895cedb8b0d897c4c9001a9aae71b2502ca0c11a8f56bcf8f177648236dd
4
+ data.tar.gz: b7ddc732b63d5465fb37aba0cdd8899294a1fcf4dbdd83e99a69aeca3875cde0
5
5
  SHA512:
6
- metadata.gz: 926d6bb826724262fe08e47c46a7fe6657db35a393873fc84b4e075f1d22d7cc2e0c40c393b98ea7df67b03e2a6a6e75ce4875b4958eba92f2370cb4f5e5ab09
7
- data.tar.gz: 108dc252376de2eebcfd486f652bc30487b538530a3abe2030a93e32eaa1412867afae248be7808f4c2d59351a94025576defe227362d9bd3d351ab4f3ba23dc
6
+ metadata.gz: db979be9acf23c7d580260972f1a35b9757e420b8a05053ab35f10c094510bafebaa4c64c69aff6e6580f681a0ca86ba03ac108ac1e3e7fae10f370ed7015dff
7
+ data.tar.gz: a7e3ba74d34509e2854d2a98e12c6f5419652217266db4eab276c9065e74f0338e585e6b647649a6e82b171da8321e29c40b28146a052522339399673076dc45
data/.rubocop.yml CHANGED
@@ -28,9 +28,6 @@ Style/FrozenStringLiteralComment:
28
28
  Style/Documentation:
29
29
  Enabled: false
30
30
 
31
- # TODO(gregfurman): Requires refactoring classes in lib/hatchet-sdk.rb:73:1
32
- Style/OneClassPerFile:
33
- Enabled: false
34
31
 
35
32
  Style/TrailingCommaInArguments:
36
33
  EnforcedStyleForMultiline: consistent_comma
data/CHANGELOG.md CHANGED
@@ -5,6 +5,18 @@ All notable changes to Hatchet's Ruby SDK will be documented in this changelog.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.4.0] - 2026-06-03
9
+
10
+ ### Added
11
+
12
+ - Adds support for defining **idempotency keys** on workflows and standalone tasks via an `idempotency` option, which ensures that they're only run once in a provided time window, based on a CEL expression. Triggers that collide with an existing run raise an `IdempotencyCollisionError` containing the existing run's ID.
13
+
14
+ ## [0.3.1] - 2026-06-12
15
+
16
+ ### Fixed
17
+
18
+ - Fixed an issue where errors raised by child tasks spawned inside a durable parent task were not propagated back to the parent. The parent can now catch the child's error and handle it gracefully.
19
+
8
20
  ## [0.3.0] - 2026-04-28
9
21
 
10
22
  ### Added
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "json"
4
4
  require "google/protobuf/timestamp_pb"
5
+ require "google/rpc/status_pb"
5
6
 
6
7
  module Hatchet
7
8
  module Clients
@@ -93,6 +94,9 @@ module Hatchet
93
94
  response = @v0_stub.trigger_workflow(request, metadata: @config.auth_metadata)
94
95
  response.workflow_run_id
95
96
  rescue ::GRPC::AlreadyExists => e
97
+ run_id = extract_idempotency_run_id(e)
98
+ raise(IdempotencyCollisionError, run_id) if run_id
99
+
96
100
  raise DedupeViolationError, "Deduplication violation: #{e.message}"
97
101
  rescue ::GRPC::ResourceExhausted => e
98
102
  raise ResourceExhaustedError, e.message
@@ -108,6 +112,7 @@ module Hatchet
108
112
  # @param workflow_name [String] The workflow name (will be namespaced)
109
113
  # @param items [Array<Hash>] Array of { input:, options: } items
110
114
  # @return [Array<String>] Array of workflow run IDs
115
+ # @raise [BulkTriggerIdempotencyCollisionError] If any runs collide on idempotency keys
111
116
  def bulk_trigger_workflow(workflow_name, items)
112
117
  ensure_connected!
113
118
 
@@ -150,6 +155,12 @@ module Hatchet
150
155
  response = @v0_stub.bulk_trigger_workflow(bulk_request, metadata: @config.auth_metadata)
151
156
  all_run_ids.concat(response.workflow_run_ids.to_a)
152
157
  end
158
+ rescue ::GRPC::AlreadyExists => e
159
+ collision = extract_bulk_trigger_idempotency_collision(e)
160
+
161
+ raise collision if collision
162
+
163
+ raise Error, "gRPC error triggering bulk workflow: #{e.class}: #{e.message}"
153
164
  rescue ::GRPC::ResourceExhausted => e
154
165
  raise ResourceExhaustedError, e.message
155
166
  rescue ::GRPC::BadStatus => e
@@ -284,6 +295,102 @@ module Hatchet
284
295
  end
285
296
  end
286
297
 
298
+ def extract_idempotency_run_id(grpc_error)
299
+ status_bin = grpc_error.to_status.metadata&.[]("grpc-status-details-bin")
300
+ return nil unless status_bin
301
+
302
+ rpc_status = Google::Rpc::Status.decode(status_bin.b)
303
+ rpc_status.details.each do |any|
304
+ next unless any.type_url.include?("IdempotencyCollisionError") &&
305
+ !any.type_url.include?("BulkTriggerIdempotencyCollisionError")
306
+
307
+ return ::V1::IdempotencyCollisionError.decode(any.value).existing_run_external_id
308
+ end
309
+ nil
310
+ rescue StandardError
311
+ nil
312
+ end
313
+
314
+ def extract_bulk_trigger_idempotency_collision(grpc_error)
315
+ status_bin = grpc_error.to_status.metadata&.[]("grpc-status-details-bin")
316
+ return nil unless status_bin
317
+
318
+ rpc_status = Google::Rpc::Status.decode(status_bin.b)
319
+ rpc_status.details.each do |any|
320
+ next unless any.type_url.include?("BulkTriggerIdempotencyCollisionError")
321
+
322
+ successful_ids, collisions = decode_bulk_trigger_collision_bytes(any.value)
323
+ return BulkTriggerIdempotencyCollisionError.new(
324
+ successful_workflow_run_external_ids: successful_ids,
325
+ collisions: collisions,
326
+ )
327
+ end
328
+ nil
329
+ rescue StandardError
330
+ nil
331
+ end
332
+
333
+ # Manually decode a BulkTriggerIdempotencyCollisionError proto message.
334
+ # Field 1 (repeated string): successful_workflow_run_external_ids
335
+ # Field 2 (repeated message): collisions (each is an IdempotencyCollisionError)
336
+ def decode_bulk_trigger_collision_bytes(bytes)
337
+ successful_ids = []
338
+ collisions = []
339
+ data = bytes.b
340
+ pos = 0
341
+
342
+ while pos < data.bytesize
343
+ tag, bytes_read = decode_proto_varint(data, pos)
344
+ pos += bytes_read
345
+ field_number = tag >> 3
346
+ wire_type = tag & 0x7
347
+
348
+ case wire_type
349
+ when 2
350
+ len, bytes_read = decode_proto_varint(data, pos)
351
+ pos += bytes_read
352
+ field_bytes = data.byteslice(pos, len)
353
+ pos += len
354
+
355
+ if field_number == 1
356
+ successful_ids << field_bytes.force_encoding("UTF-8")
357
+ elsif field_number == 2
358
+ proto = ::V1::IdempotencyCollisionError.decode(field_bytes)
359
+ collisions << IdempotencyCollisionError.new(proto.existing_run_external_id)
360
+ end
361
+ when 0
362
+ _, bytes_read = decode_proto_varint(data, pos)
363
+ pos += bytes_read
364
+ when 5
365
+ pos += 4
366
+ when 1
367
+ pos += 8
368
+ else
369
+ break
370
+ end
371
+ end
372
+
373
+ [successful_ids, collisions]
374
+ rescue StandardError
375
+ [[], []]
376
+ end
377
+
378
+ def decode_proto_varint(data, pos)
379
+ result = 0
380
+ shift = 0
381
+ bytes_read = 0
382
+ loop do
383
+ byte = data.getbyte(pos + bytes_read)
384
+ return [result, bytes_read] if byte.nil?
385
+
386
+ bytes_read += 1
387
+ result |= (byte & 0x7f) << shift
388
+ shift += 7
389
+ break if byte.nobits?(0x80)
390
+ end
391
+ [result, bytes_read]
392
+ end
393
+
287
394
  def ensure_connected!
288
395
  return if @v0_stub && @v1_stub
289
396
 
@@ -8,7 +8,7 @@ require 'v1/shared/condition_pb'
8
8
  require 'v1/shared/trigger_pb'
9
9
 
10
10
 
11
- descriptor_data = "\n\x13v1/dispatcher.proto\x12\x02v1\x1a\x19v1/shared/condition.proto\x1a\x17v1/shared/trigger.proto\"5\n DurableTaskRequestRegisterWorker\x12\x11\n\tworker_id\x18\x01 \x01(\t\"6\n!DurableTaskResponseRegisterWorker\x12\x11\n\tworker_id\x18\x01 \x01(\t\"y\n\x17\x44urableEventLogEntryRef\x12 \n\x18\x64urable_task_external_id\x18\x01 \x01(\t\x12\x18\n\x10invocation_count\x18\x02 \x01(\x05\x12\x11\n\tbranch_id\x18\x03 \x01(\x03\x12\x0f\n\x07node_id\x18\x04 \x01(\x03\"^\n\x16\x44urableTaskRunAckEntry\x12\x0f\n\x07node_id\x18\x01 \x01(\x03\x12\x11\n\tbranch_id\x18\x02 \x01(\x03\x12 \n\x18workflow_run_external_id\x18\x03 \x01(\t\"\xa3\x01\n\x1f\x44urableTaskEventMemoAckResponse\x12(\n\x03ref\x18\x01 \x01(\x0b\x32\x1b.v1.DurableEventLogEntryRef\x12\x1c\n\x14memo_already_existed\x18\x02 \x01(\x08\x12 \n\x13memo_result_payload\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x42\x16\n\x14_memo_result_payload\"\x95\x01\n&DurableTaskEventTriggerRunsAckResponse\x12 \n\x18\x64urable_task_external_id\x18\x01 \x01(\t\x12\x18\n\x10invocation_count\x18\x02 \x01(\x05\x12/\n\x0brun_entries\x18\x03 \x03(\x0b\x32\x1a.v1.DurableTaskRunAckEntry\"N\n\"DurableTaskEventWaitForAckResponse\x12(\n\x03ref\x18\x01 \x01(\x0b\x32\x1b.v1.DurableEventLogEntryRef\"f\n)DurableTaskEventLogEntryCompletedResponse\x12(\n\x03ref\x18\x01 \x01(\x0b\x32\x1b.v1.DurableEventLogEntryRef\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\"\x7f\n!DurableTaskEvictInvocationRequest\x12\x18\n\x10invocation_count\x18\x01 \x01(\x05\x12 \n\x18\x64urable_task_external_id\x18\x02 \x01(\t\x12\x13\n\x06reason\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\t\n\x07_reason\"\\\n\x1e\x44urableTaskEvictionAckResponse\x12\x18\n\x10invocation_count\x18\x01 \x01(\x05\x12 \n\x18\x64urable_task_external_id\x18\x02 \x01(\t\"\x82\x01\n DurableTaskAwaitedCompletedEntry\x12 \n\x18\x64urable_task_external_id\x18\x01 \x01(\t\x12\x11\n\tbranch_id\x18\x02 \x01(\x03\x12\x0f\n\x07node_id\x18\x03 \x01(\x03\x12\x18\n\x10invocation_count\x18\x04 \x01(\x05\"j\n\x1c\x44urableTaskServerEvictNotice\x12 \n\x18\x64urable_task_external_id\x18\x01 \x01(\t\x12\x18\n\x10invocation_count\x18\x02 \x01(\x05\x12\x0e\n\x06reason\x18\x03 \x01(\t\"r\n\x1e\x44urableTaskWorkerStatusRequest\x12\x11\n\tworker_id\x18\x01 \x01(\t\x12=\n\x0fwaiting_entries\x18\x02 \x03(\x0b\x32$.v1.DurableTaskAwaitedCompletedEntry\"m\n\x1e\x44urableTaskCompleteMemoRequest\x12(\n\x03ref\x18\x01 \x01(\x0b\x32\x1b.v1.DurableEventLogEntryRef\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\x12\x10\n\x08memo_key\x18\x03 \x01(\x0c\"\x83\x01\n\x16\x44urableTaskMemoRequest\x12\x18\n\x10invocation_count\x18\x01 \x01(\x05\x12 \n\x18\x64urable_task_external_id\x18\x02 \x01(\t\x12\x0b\n\x03key\x18\x03 \x01(\x0c\x12\x14\n\x07payload\x18\x04 \x01(\x0cH\x00\x88\x01\x01\x42\n\n\x08_payload\"\x8d\x01\n\x1d\x44urableTaskTriggerRunsRequest\x12\x18\n\x10invocation_count\x18\x01 \x01(\x05\x12 \n\x18\x64urable_task_external_id\x18\x02 \x01(\t\x12\x30\n\x0ctrigger_opts\x18\x03 \x03(\x0b\x32\x1a.v1.TriggerWorkflowRequest\"\xd3\x01\n\x19\x44urableTaskWaitForRequest\x12\x18\n\x10invocation_count\x18\x01 \x01(\x05\x12 \n\x18\x64urable_task_external_id\x18\x02 \x01(\t\x12\x44\n\x13wait_for_conditions\x18\x03 \x01(\x0b\x32\".v1.DurableEventListenerConditionsH\x00\x88\x01\x01\x12\x12\n\x05label\x18\x04 \x01(\tH\x01\x88\x01\x01\x42\x16\n\x14_wait_for_conditionsB\x08\n\x06_label\"\xb7\x03\n\x12\x44urableTaskRequest\x12?\n\x0fregister_worker\x18\x01 \x01(\x0b\x32$.v1.DurableTaskRequestRegisterWorkerH\x00\x12*\n\x04memo\x18\x02 \x01(\x0b\x32\x1a.v1.DurableTaskMemoRequestH\x00\x12\x39\n\x0ctrigger_runs\x18\x03 \x01(\x0b\x32!.v1.DurableTaskTriggerRunsRequestH\x00\x12\x31\n\x08wait_for\x18\x04 \x01(\x0b\x32\x1d.v1.DurableTaskWaitForRequestH\x00\x12\x41\n\x10\x65vict_invocation\x18\x05 \x01(\x0b\x32%.v1.DurableTaskEvictInvocationRequestH\x00\x12;\n\rworker_status\x18\x06 \x01(\x0b\x32\".v1.DurableTaskWorkerStatusRequestH\x00\x12;\n\rcomplete_memo\x18\x07 \x01(\x0b\x32\".v1.DurableTaskCompleteMemoRequestH\x00\x42\t\n\x07message\"\x89\x01\n\x18\x44urableTaskErrorResponse\x12(\n\x03ref\x18\x01 \x01(\x0b\x32\x1b.v1.DurableEventLogEntryRef\x12,\n\nerror_type\x18\x02 \x01(\x0e\x32\x18.v1.DurableTaskErrorType\x12\x15\n\rerror_message\x18\x03 \x01(\t\"\x92\x04\n\x13\x44urableTaskResponse\x12@\n\x0fregister_worker\x18\x01 \x01(\x0b\x32%.v1.DurableTaskResponseRegisterWorkerH\x00\x12\x37\n\x08memo_ack\x18\x02 \x01(\x0b\x32#.v1.DurableTaskEventMemoAckResponseH\x00\x12\x46\n\x10trigger_runs_ack\x18\x03 \x01(\x0b\x32*.v1.DurableTaskEventTriggerRunsAckResponseH\x00\x12>\n\x0cwait_for_ack\x18\x04 \x01(\x0b\x32&.v1.DurableTaskEventWaitForAckResponseH\x00\x12H\n\x0f\x65ntry_completed\x18\x05 \x01(\x0b\x32-.v1.DurableTaskEventLogEntryCompletedResponseH\x00\x12-\n\x05\x65rror\x18\x06 \x01(\x0b\x32\x1c.v1.DurableTaskErrorResponseH\x00\x12:\n\x0c\x65viction_ack\x18\x07 \x01(\x0b\x32\".v1.DurableTaskEvictionAckResponseH\x00\x12\x38\n\x0cserver_evict\x18\x08 \x01(\x0b\x32 .v1.DurableTaskServerEvictNoticeH\x00\x42\t\n\x07message\"z\n\x1bRegisterDurableEventRequest\x12\x0f\n\x07task_id\x18\x01 \x01(\t\x12\x12\n\nsignal_key\x18\x02 \x01(\t\x12\x36\n\nconditions\x18\x03 \x01(\x0b\x32\".v1.DurableEventListenerConditions\"\x1e\n\x1cRegisterDurableEventResponse\"C\n\x1cListenForDurableEventRequest\x12\x0f\n\x07task_id\x18\x01 \x01(\t\x12\x12\n\nsignal_key\x18\x02 \x01(\t\"A\n\x0c\x44urableEvent\x12\x0f\n\x07task_id\x18\x01 \x01(\t\x12\x12\n\nsignal_key\x18\x02 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\x0c*k\n\x14\x44urableTaskErrorType\x12\'\n#DURABLE_TASK_ERROR_TYPE_UNSPECIFIED\x10\x00\x12*\n&DURABLE_TASK_ERROR_TYPE_NONDETERMINISM\x10\x01\x32\x84\x02\n\x0cV1Dispatcher\x12\x44\n\x0b\x44urableTask\x12\x16.v1.DurableTaskRequest\x1a\x17.v1.DurableTaskResponse\"\x00(\x01\x30\x01\x12[\n\x14RegisterDurableEvent\x12\x1f.v1.RegisterDurableEventRequest\x1a .v1.RegisterDurableEventResponse\"\x00\x12Q\n\x15ListenForDurableEvent\x12 .v1.ListenForDurableEventRequest\x1a\x10.v1.DurableEvent\"\x00(\x01\x30\x01\x42\x42Z@github.com/hatchet-dev/hatchet/internal/services/shared/proto/v1b\x06proto3"
11
+ descriptor_data = "\n\x13v1/dispatcher.proto\x12\x02v1\x1a\x19v1/shared/condition.proto\x1a\x17v1/shared/trigger.proto\"5\n DurableTaskRequestRegisterWorker\x12\x11\n\tworker_id\x18\x01 \x01(\t\"6\n!DurableTaskResponseRegisterWorker\x12\x11\n\tworker_id\x18\x01 \x01(\t\"y\n\x17\x44urableEventLogEntryRef\x12 \n\x18\x64urable_task_external_id\x18\x01 \x01(\t\x12\x18\n\x10invocation_count\x18\x02 \x01(\x05\x12\x11\n\tbranch_id\x18\x03 \x01(\x03\x12\x0f\n\x07node_id\x18\x04 \x01(\x03\"^\n\x16\x44urableTaskRunAckEntry\x12\x0f\n\x07node_id\x18\x01 \x01(\x03\x12\x11\n\tbranch_id\x18\x02 \x01(\x03\x12 \n\x18workflow_run_external_id\x18\x03 \x01(\t\"\xa3\x01\n\x1f\x44urableTaskEventMemoAckResponse\x12(\n\x03ref\x18\x01 \x01(\x0b\x32\x1b.v1.DurableEventLogEntryRef\x12\x1c\n\x14memo_already_existed\x18\x02 \x01(\x08\x12 \n\x13memo_result_payload\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x42\x16\n\x14_memo_result_payload\"\x95\x01\n&DurableTaskEventTriggerRunsAckResponse\x12 \n\x18\x64urable_task_external_id\x18\x01 \x01(\t\x12\x18\n\x10invocation_count\x18\x02 \x01(\x05\x12/\n\x0brun_entries\x18\x03 \x03(\x0b\x32\x1a.v1.DurableTaskRunAckEntry\"N\n\"DurableTaskEventWaitForAckResponse\x12(\n\x03ref\x18\x01 \x01(\x0b\x32\x1b.v1.DurableEventLogEntryRef\"\xa8\x01\n)DurableTaskEventLogEntryCompletedResponse\x12(\n\x03ref\x18\x01 \x01(\x0b\x32\x1b.v1.DurableEventLogEntryRef\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\x12\x12\n\nis_failure\x18\x03 \x01(\x08\x12\x1a\n\rerror_message\x18\x04 \x01(\tH\x00\x88\x01\x01\x42\x10\n\x0e_error_message\"\x7f\n!DurableTaskEvictInvocationRequest\x12\x18\n\x10invocation_count\x18\x01 \x01(\x05\x12 \n\x18\x64urable_task_external_id\x18\x02 \x01(\t\x12\x13\n\x06reason\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\t\n\x07_reason\"\\\n\x1e\x44urableTaskEvictionAckResponse\x12\x18\n\x10invocation_count\x18\x01 \x01(\x05\x12 \n\x18\x64urable_task_external_id\x18\x02 \x01(\t\"\x82\x01\n DurableTaskAwaitedCompletedEntry\x12 \n\x18\x64urable_task_external_id\x18\x01 \x01(\t\x12\x11\n\tbranch_id\x18\x02 \x01(\x03\x12\x0f\n\x07node_id\x18\x03 \x01(\x03\x12\x18\n\x10invocation_count\x18\x04 \x01(\x05\"j\n\x1c\x44urableTaskServerEvictNotice\x12 \n\x18\x64urable_task_external_id\x18\x01 \x01(\t\x12\x18\n\x10invocation_count\x18\x02 \x01(\x05\x12\x0e\n\x06reason\x18\x03 \x01(\t\"r\n\x1e\x44urableTaskWorkerStatusRequest\x12\x11\n\tworker_id\x18\x01 \x01(\t\x12=\n\x0fwaiting_entries\x18\x02 \x03(\x0b\x32$.v1.DurableTaskAwaitedCompletedEntry\"m\n\x1e\x44urableTaskCompleteMemoRequest\x12(\n\x03ref\x18\x01 \x01(\x0b\x32\x1b.v1.DurableEventLogEntryRef\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\x12\x10\n\x08memo_key\x18\x03 \x01(\x0c\"\x83\x01\n\x16\x44urableTaskMemoRequest\x12\x18\n\x10invocation_count\x18\x01 \x01(\x05\x12 \n\x18\x64urable_task_external_id\x18\x02 \x01(\t\x12\x0b\n\x03key\x18\x03 \x01(\x0c\x12\x14\n\x07payload\x18\x04 \x01(\x0cH\x00\x88\x01\x01\x42\n\n\x08_payload\"\x8d\x01\n\x1d\x44urableTaskTriggerRunsRequest\x12\x18\n\x10invocation_count\x18\x01 \x01(\x05\x12 \n\x18\x64urable_task_external_id\x18\x02 \x01(\t\x12\x30\n\x0ctrigger_opts\x18\x03 \x03(\x0b\x32\x1a.v1.TriggerWorkflowRequest\"\xd3\x01\n\x19\x44urableTaskWaitForRequest\x12\x18\n\x10invocation_count\x18\x01 \x01(\x05\x12 \n\x18\x64urable_task_external_id\x18\x02 \x01(\t\x12\x44\n\x13wait_for_conditions\x18\x03 \x01(\x0b\x32\".v1.DurableEventListenerConditionsH\x00\x88\x01\x01\x12\x12\n\x05label\x18\x04 \x01(\tH\x01\x88\x01\x01\x42\x16\n\x14_wait_for_conditionsB\x08\n\x06_label\"\xb7\x03\n\x12\x44urableTaskRequest\x12?\n\x0fregister_worker\x18\x01 \x01(\x0b\x32$.v1.DurableTaskRequestRegisterWorkerH\x00\x12*\n\x04memo\x18\x02 \x01(\x0b\x32\x1a.v1.DurableTaskMemoRequestH\x00\x12\x39\n\x0ctrigger_runs\x18\x03 \x01(\x0b\x32!.v1.DurableTaskTriggerRunsRequestH\x00\x12\x31\n\x08wait_for\x18\x04 \x01(\x0b\x32\x1d.v1.DurableTaskWaitForRequestH\x00\x12\x41\n\x10\x65vict_invocation\x18\x05 \x01(\x0b\x32%.v1.DurableTaskEvictInvocationRequestH\x00\x12;\n\rworker_status\x18\x06 \x01(\x0b\x32\".v1.DurableTaskWorkerStatusRequestH\x00\x12;\n\rcomplete_memo\x18\x07 \x01(\x0b\x32\".v1.DurableTaskCompleteMemoRequestH\x00\x42\t\n\x07message\"\x89\x01\n\x18\x44urableTaskErrorResponse\x12(\n\x03ref\x18\x01 \x01(\x0b\x32\x1b.v1.DurableEventLogEntryRef\x12,\n\nerror_type\x18\x02 \x01(\x0e\x32\x18.v1.DurableTaskErrorType\x12\x15\n\rerror_message\x18\x03 \x01(\t\"\x92\x04\n\x13\x44urableTaskResponse\x12@\n\x0fregister_worker\x18\x01 \x01(\x0b\x32%.v1.DurableTaskResponseRegisterWorkerH\x00\x12\x37\n\x08memo_ack\x18\x02 \x01(\x0b\x32#.v1.DurableTaskEventMemoAckResponseH\x00\x12\x46\n\x10trigger_runs_ack\x18\x03 \x01(\x0b\x32*.v1.DurableTaskEventTriggerRunsAckResponseH\x00\x12>\n\x0cwait_for_ack\x18\x04 \x01(\x0b\x32&.v1.DurableTaskEventWaitForAckResponseH\x00\x12H\n\x0f\x65ntry_completed\x18\x05 \x01(\x0b\x32-.v1.DurableTaskEventLogEntryCompletedResponseH\x00\x12-\n\x05\x65rror\x18\x06 \x01(\x0b\x32\x1c.v1.DurableTaskErrorResponseH\x00\x12:\n\x0c\x65viction_ack\x18\x07 \x01(\x0b\x32\".v1.DurableTaskEvictionAckResponseH\x00\x12\x38\n\x0cserver_evict\x18\x08 \x01(\x0b\x32 .v1.DurableTaskServerEvictNoticeH\x00\x42\t\n\x07message\"z\n\x1bRegisterDurableEventRequest\x12\x0f\n\x07task_id\x18\x01 \x01(\t\x12\x12\n\nsignal_key\x18\x02 \x01(\t\x12\x36\n\nconditions\x18\x03 \x01(\x0b\x32\".v1.DurableEventListenerConditions\"\x1e\n\x1cRegisterDurableEventResponse\"C\n\x1cListenForDurableEventRequest\x12\x0f\n\x07task_id\x18\x01 \x01(\t\x12\x12\n\nsignal_key\x18\x02 \x01(\t\"A\n\x0c\x44urableEvent\x12\x0f\n\x07task_id\x18\x01 \x01(\t\x12\x12\n\nsignal_key\x18\x02 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\x0c*k\n\x14\x44urableTaskErrorType\x12\'\n#DURABLE_TASK_ERROR_TYPE_UNSPECIFIED\x10\x00\x12*\n&DURABLE_TASK_ERROR_TYPE_NONDETERMINISM\x10\x01\x32\x84\x02\n\x0cV1Dispatcher\x12\x44\n\x0b\x44urableTask\x12\x16.v1.DurableTaskRequest\x1a\x17.v1.DurableTaskResponse\"\x00(\x01\x30\x01\x12[\n\x14RegisterDurableEvent\x12\x1f.v1.RegisterDurableEventRequest\x1a .v1.RegisterDurableEventResponse\"\x00\x12Q\n\x15ListenForDurableEvent\x12 .v1.ListenForDurableEventRequest\x1a\x10.v1.DurableEvent\"\x00(\x01\x30\x01\x42\x42Z@github.com/hatchet-dev/hatchet/internal/services/shared/proto/v1b\x06proto3"
12
12
 
13
13
  pool = ::Google::Protobuf::DescriptorPool.generated_pool
14
14
  pool.add_serialized_file(descriptor_data)
@@ -9,7 +9,7 @@ require 'v1/shared/condition_pb'
9
9
  require 'v1/shared/trigger_pb'
10
10
 
11
11
 
12
- descriptor_data = "\n\x12v1/workflows.proto\x12\x02v1\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x19v1/shared/condition.proto\x1a\x17v1/shared/trigger.proto\"[\n\x12\x43\x61ncelTasksRequest\x12\x14\n\x0c\x65xternal_ids\x18\x01 \x03(\t\x12$\n\x06\x66ilter\x18\x02 \x01(\x0b\x32\x0f.v1.TasksFilterH\x00\x88\x01\x01\x42\t\n\x07_filter\"[\n\x12ReplayTasksRequest\x12\x14\n\x0c\x65xternal_ids\x18\x01 \x03(\t\x12$\n\x06\x66ilter\x18\x02 \x01(\x0b\x32\x0f.v1.TasksFilterH\x00\x88\x01\x01\x42\t\n\x07_filter\"\xb7\x01\n\x0bTasksFilter\x12\x10\n\x08statuses\x18\x01 \x03(\t\x12)\n\x05since\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12.\n\x05until\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.TimestampH\x00\x88\x01\x01\x12\x14\n\x0cworkflow_ids\x18\x04 \x03(\t\x12\x1b\n\x13\x61\x64\x64itional_metadata\x18\x05 \x03(\tB\x08\n\x06_until\".\n\x13\x43\x61ncelTasksResponse\x12\x17\n\x0f\x63\x61ncelled_tasks\x18\x01 \x03(\t\"-\n\x13ReplayTasksResponse\x12\x16\n\x0ereplayed_tasks\x18\x01 \x03(\t\"\xae\x02\n\x19TriggerWorkflowRunRequest\x12\x15\n\rworkflow_name\x18\x01 \x01(\t\x12\r\n\x05input\x18\x02 \x01(\x0c\x12\x1b\n\x13\x61\x64\x64itional_metadata\x18\x03 \x01(\x0c\x12\x15\n\x08priority\x18\x04 \x01(\x05H\x00\x88\x01\x01\x12U\n\x15\x64\x65sired_worker_labels\x18\x05 \x03(\x0b\x32\x36.v1.TriggerWorkflowRunRequest.DesiredWorkerLabelsEntry\x1aS\n\x18\x44\x65siredWorkerLabelsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12&\n\x05value\x18\x02 \x01(\x0b\x32\x17.v1.DesiredWorkerLabels:\x02\x38\x01\x42\x0b\n\t_priority\"1\n\x1aTriggerWorkflowRunResponse\x12\x13\n\x0b\x65xternal_id\x18\x01 \x01(\t\"X\n\x18\x42ranchDurableTaskRequest\x12\x18\n\x10task_external_id\x18\x01 \x01(\t\x12\x0f\n\x07node_id\x18\x02 \x01(\x03\x12\x11\n\tbranch_id\x18\x03 \x01(\x03\"Y\n\x19\x42ranchDurableTaskResponse\x12\x18\n\x10task_external_id\x18\x01 \x01(\t\x12\x0f\n\x07node_id\x18\x02 \x01(\x03\x12\x11\n\tbranch_id\x18\x03 \x01(\x03\"\xac\x04\n\x1c\x43reateWorkflowVersionRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x0f\n\x07version\x18\x03 \x01(\t\x12\x16\n\x0e\x65vent_triggers\x18\x04 \x03(\t\x12\x15\n\rcron_triggers\x18\x05 \x03(\t\x12!\n\x05tasks\x18\x06 \x03(\x0b\x32\x12.v1.CreateTaskOpts\x12$\n\x0b\x63oncurrency\x18\x07 \x01(\x0b\x32\x0f.v1.Concurrency\x12\x17\n\ncron_input\x18\x08 \x01(\tH\x00\x88\x01\x01\x12\x30\n\x0fon_failure_task\x18\t \x01(\x0b\x32\x12.v1.CreateTaskOptsH\x01\x88\x01\x01\x12\'\n\x06sticky\x18\n \x01(\x0e\x32\x12.v1.StickyStrategyH\x02\x88\x01\x01\x12\x1d\n\x10\x64\x65\x66\x61ult_priority\x18\x0b \x01(\x05H\x03\x88\x01\x01\x12(\n\x0f\x63oncurrency_arr\x18\x0c \x03(\x0b\x32\x0f.v1.Concurrency\x12*\n\x0f\x64\x65\x66\x61ult_filters\x18\r \x03(\x0b\x32\x11.v1.DefaultFilter\x12\x1e\n\x11input_json_schema\x18\x0e \x01(\x0cH\x04\x88\x01\x01\x42\r\n\x0b_cron_inputB\x12\n\x10_on_failure_taskB\t\n\x07_stickyB\x13\n\x11_default_priorityB\x14\n\x12_input_json_schema\"T\n\rDefaultFilter\x12\x12\n\nexpression\x18\x01 \x01(\t\x12\r\n\x05scope\x18\x02 \x01(\t\x12\x14\n\x07payload\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x42\n\n\x08_payload\"\x93\x01\n\x0b\x43oncurrency\x12\x12\n\nexpression\x18\x01 \x01(\t\x12\x15\n\x08max_runs\x18\x02 \x01(\x05H\x00\x88\x01\x01\x12\x39\n\x0elimit_strategy\x18\x03 \x01(\x0e\x32\x1c.v1.ConcurrencyLimitStrategyH\x01\x88\x01\x01\x42\x0b\n\t_max_runsB\x11\n\x0f_limit_strategy\"\xb7\x05\n\x0e\x43reateTaskOpts\x12\x13\n\x0breadable_id\x18\x01 \x01(\t\x12\x0e\n\x06\x61\x63tion\x18\x02 \x01(\t\x12\x0f\n\x07timeout\x18\x03 \x01(\t\x12\x0e\n\x06inputs\x18\x04 \x01(\t\x12\x0f\n\x07parents\x18\x05 \x03(\t\x12\x0f\n\x07retries\x18\x06 \x01(\x05\x12,\n\x0brate_limits\x18\x07 \x03(\x0b\x32\x17.v1.CreateTaskRateLimit\x12;\n\rworker_labels\x18\x08 \x03(\x0b\x32$.v1.CreateTaskOpts.WorkerLabelsEntry\x12\x1b\n\x0e\x62\x61\x63koff_factor\x18\t \x01(\x02H\x00\x88\x01\x01\x12 \n\x13\x62\x61\x63koff_max_seconds\x18\n \x01(\x05H\x01\x88\x01\x01\x12$\n\x0b\x63oncurrency\x18\x0b \x03(\x0b\x32\x0f.v1.Concurrency\x12+\n\nconditions\x18\x0c \x01(\x0b\x32\x12.v1.TaskConditionsH\x02\x88\x01\x01\x12\x1d\n\x10schedule_timeout\x18\r \x01(\tH\x03\x88\x01\x01\x12\x12\n\nis_durable\x18\x0e \x01(\x08\x12;\n\rslot_requests\x18\x0f \x03(\x0b\x32$.v1.CreateTaskOpts.SlotRequestsEntry\x1aL\n\x11WorkerLabelsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12&\n\x05value\x18\x02 \x01(\x0b\x32\x17.v1.DesiredWorkerLabels:\x02\x38\x01\x1a\x33\n\x11SlotRequestsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x05:\x02\x38\x01\x42\x11\n\x0f_backoff_factorB\x16\n\x14_backoff_max_secondsB\r\n\x0b_conditionsB\x13\n\x11_schedule_timeout\"\xfd\x01\n\x13\x43reateTaskRateLimit\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x12\n\x05units\x18\x02 \x01(\x05H\x00\x88\x01\x01\x12\x15\n\x08key_expr\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x17\n\nunits_expr\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x1e\n\x11limit_values_expr\x18\x05 \x01(\tH\x03\x88\x01\x01\x12,\n\x08\x64uration\x18\x06 \x01(\x0e\x32\x15.v1.RateLimitDurationH\x04\x88\x01\x01\x42\x08\n\x06_unitsB\x0b\n\t_key_exprB\r\n\x0b_units_exprB\x14\n\x12_limit_values_exprB\x0b\n\t_duration\"@\n\x1d\x43reateWorkflowVersionResponse\x12\n\n\x02id\x18\x01 \x01(\t\x12\x13\n\x0bworkflow_id\x18\x02 \x01(\t\"+\n\x14GetRunDetailsRequest\x12\x13\n\x0b\x65xternal_id\x18\x01 \x01(\t\"\xaa\x01\n\rTaskRunDetail\x12\x13\n\x0b\x65xternal_id\x18\x01 \x01(\t\x12\x1d\n\x06status\x18\x02 \x01(\x0e\x32\r.v1.RunStatus\x12\x12\n\x05\x65rror\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06output\x18\x04 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x0breadable_id\x18\x05 \x01(\t\x12\x12\n\nis_evicted\x18\x06 \x01(\x08\x42\x08\n\x06_errorB\t\n\x07_output\"\x84\x02\n\x15GetRunDetailsResponse\x12\r\n\x05input\x18\x01 \x01(\x0c\x12\x1d\n\x06status\x18\x02 \x01(\x0e\x32\r.v1.RunStatus\x12:\n\ttask_runs\x18\x03 \x03(\x0b\x32\'.v1.GetRunDetailsResponse.TaskRunsEntry\x12\x0c\n\x04\x64one\x18\x04 \x01(\x08\x12\x1b\n\x13\x61\x64\x64itional_metadata\x18\x05 \x01(\x0c\x12\x12\n\nis_evicted\x18\x06 \x01(\x08\x1a\x42\n\rTaskRunsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12 \n\x05value\x18\x02 \x01(\x0b\x32\x11.v1.TaskRunDetail:\x02\x38\x01*$\n\x0eStickyStrategy\x12\x08\n\x04SOFT\x10\x00\x12\x08\n\x04HARD\x10\x01*]\n\x11RateLimitDuration\x12\n\n\x06SECOND\x10\x00\x12\n\n\x06MINUTE\x10\x01\x12\x08\n\x04HOUR\x10\x02\x12\x07\n\x03\x44\x41Y\x10\x03\x12\x08\n\x04WEEK\x10\x04\x12\t\n\x05MONTH\x10\x05\x12\x08\n\x04YEAR\x10\x06*[\n\tRunStatus\x12\n\n\x06QUEUED\x10\x00\x12\x0b\n\x07RUNNING\x10\x01\x12\r\n\tCOMPLETED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\x12\r\n\tCANCELLED\x10\x04\x12\x0b\n\x07\x45VICTED\x10\x05*\x7f\n\x18\x43oncurrencyLimitStrategy\x12\x16\n\x12\x43\x41NCEL_IN_PROGRESS\x10\x00\x12\x0f\n\x0b\x44ROP_NEWEST\x10\x01\x12\x10\n\x0cQUEUE_NEWEST\x10\x02\x12\x15\n\x11GROUP_ROUND_ROBIN\x10\x03\x12\x11\n\rCANCEL_NEWEST\x10\x04\x32\xcf\x03\n\x0c\x41\x64minService\x12R\n\x0bPutWorkflow\x12 .v1.CreateWorkflowVersionRequest\x1a!.v1.CreateWorkflowVersionResponse\x12>\n\x0b\x43\x61ncelTasks\x12\x16.v1.CancelTasksRequest\x1a\x17.v1.CancelTasksResponse\x12>\n\x0bReplayTasks\x12\x16.v1.ReplayTasksRequest\x1a\x17.v1.ReplayTasksResponse\x12S\n\x12TriggerWorkflowRun\x12\x1d.v1.TriggerWorkflowRunRequest\x1a\x1e.v1.TriggerWorkflowRunResponse\x12\x44\n\rGetRunDetails\x12\x18.v1.GetRunDetailsRequest\x1a\x19.v1.GetRunDetailsResponse\x12P\n\x11\x42ranchDurableTask\x12\x1c.v1.BranchDurableTaskRequest\x1a\x1d.v1.BranchDurableTaskResponseBBZ@github.com/hatchet-dev/hatchet/internal/services/shared/proto/v1b\x06proto3"
12
+ descriptor_data = "\n\x12v1/workflows.proto\x12\x02v1\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x19v1/shared/condition.proto\x1a\x17v1/shared/trigger.proto\"[\n\x12\x43\x61ncelTasksRequest\x12\x14\n\x0c\x65xternal_ids\x18\x01 \x03(\t\x12$\n\x06\x66ilter\x18\x02 \x01(\x0b\x32\x0f.v1.TasksFilterH\x00\x88\x01\x01\x42\t\n\x07_filter\"[\n\x12ReplayTasksRequest\x12\x14\n\x0c\x65xternal_ids\x18\x01 \x03(\t\x12$\n\x06\x66ilter\x18\x02 \x01(\x0b\x32\x0f.v1.TasksFilterH\x00\x88\x01\x01\x42\t\n\x07_filter\"\xb7\x01\n\x0bTasksFilter\x12\x10\n\x08statuses\x18\x01 \x03(\t\x12)\n\x05since\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12.\n\x05until\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.TimestampH\x00\x88\x01\x01\x12\x14\n\x0cworkflow_ids\x18\x04 \x03(\t\x12\x1b\n\x13\x61\x64\x64itional_metadata\x18\x05 \x03(\tB\x08\n\x06_until\".\n\x13\x43\x61ncelTasksResponse\x12\x17\n\x0f\x63\x61ncelled_tasks\x18\x01 \x03(\t\"-\n\x13ReplayTasksResponse\x12\x16\n\x0ereplayed_tasks\x18\x01 \x03(\t\"\xae\x02\n\x19TriggerWorkflowRunRequest\x12\x15\n\rworkflow_name\x18\x01 \x01(\t\x12\r\n\x05input\x18\x02 \x01(\x0c\x12\x1b\n\x13\x61\x64\x64itional_metadata\x18\x03 \x01(\x0c\x12\x15\n\x08priority\x18\x04 \x01(\x05H\x00\x88\x01\x01\x12U\n\x15\x64\x65sired_worker_labels\x18\x05 \x03(\x0b\x32\x36.v1.TriggerWorkflowRunRequest.DesiredWorkerLabelsEntry\x1aS\n\x18\x44\x65siredWorkerLabelsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12&\n\x05value\x18\x02 \x01(\x0b\x32\x17.v1.DesiredWorkerLabels:\x02\x38\x01\x42\x0b\n\t_priority\"1\n\x1aTriggerWorkflowRunResponse\x12\x13\n\x0b\x65xternal_id\x18\x01 \x01(\t\"X\n\x18\x42ranchDurableTaskRequest\x12\x18\n\x10task_external_id\x18\x01 \x01(\t\x12\x0f\n\x07node_id\x18\x02 \x01(\x03\x12\x11\n\tbranch_id\x18\x03 \x01(\x03\"Y\n\x19\x42ranchDurableTaskResponse\x12\x18\n\x10task_external_id\x18\x01 \x01(\t\x12\x0f\n\x07node_id\x18\x02 \x01(\x03\x12\x11\n\tbranch_id\x18\x03 \x01(\x03\"\xed\x04\n\x1c\x43reateWorkflowVersionRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x0f\n\x07version\x18\x03 \x01(\t\x12\x16\n\x0e\x65vent_triggers\x18\x04 \x03(\t\x12\x15\n\rcron_triggers\x18\x05 \x03(\t\x12!\n\x05tasks\x18\x06 \x03(\x0b\x32\x12.v1.CreateTaskOpts\x12$\n\x0b\x63oncurrency\x18\x07 \x01(\x0b\x32\x0f.v1.Concurrency\x12\x17\n\ncron_input\x18\x08 \x01(\tH\x00\x88\x01\x01\x12\x30\n\x0fon_failure_task\x18\t \x01(\x0b\x32\x12.v1.CreateTaskOptsH\x01\x88\x01\x01\x12\'\n\x06sticky\x18\n \x01(\x0e\x32\x12.v1.StickyStrategyH\x02\x88\x01\x01\x12\x1d\n\x10\x64\x65\x66\x61ult_priority\x18\x0b \x01(\x05H\x03\x88\x01\x01\x12(\n\x0f\x63oncurrency_arr\x18\x0c \x03(\x0b\x32\x0f.v1.Concurrency\x12*\n\x0f\x64\x65\x66\x61ult_filters\x18\r \x03(\x0b\x32\x11.v1.DefaultFilter\x12\x1e\n\x11input_json_schema\x18\x0e \x01(\x0cH\x04\x88\x01\x01\x12/\n\x0bidempotency\x18\x0f \x01(\x0b\x32\x15.v1.IdempotencyConfigH\x05\x88\x01\x01\x42\r\n\x0b_cron_inputB\x12\n\x10_on_failure_taskB\t\n\x07_stickyB\x13\n\x11_default_priorityB\x14\n\x12_input_json_schemaB\x0e\n\x0c_idempotency\"7\n\x11IdempotencyConfig\x12\x12\n\nexpression\x18\x01 \x01(\t\x12\x0e\n\x06ttl_ms\x18\x02 \x01(\x03\"=\n\x19IdempotencyCollisionError\x12 \n\x18\x65xisting_run_external_id\x18\x01 \x01(\t\"T\n\rDefaultFilter\x12\x12\n\nexpression\x18\x01 \x01(\t\x12\r\n\x05scope\x18\x02 \x01(\t\x12\x14\n\x07payload\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x42\n\n\x08_payload\"\x93\x01\n\x0b\x43oncurrency\x12\x12\n\nexpression\x18\x01 \x01(\t\x12\x15\n\x08max_runs\x18\x02 \x01(\x05H\x00\x88\x01\x01\x12\x39\n\x0elimit_strategy\x18\x03 \x01(\x0e\x32\x1c.v1.ConcurrencyLimitStrategyH\x01\x88\x01\x01\x42\x0b\n\t_max_runsB\x11\n\x0f_limit_strategy\"\xb7\x05\n\x0e\x43reateTaskOpts\x12\x13\n\x0breadable_id\x18\x01 \x01(\t\x12\x0e\n\x06\x61\x63tion\x18\x02 \x01(\t\x12\x0f\n\x07timeout\x18\x03 \x01(\t\x12\x0e\n\x06inputs\x18\x04 \x01(\t\x12\x0f\n\x07parents\x18\x05 \x03(\t\x12\x0f\n\x07retries\x18\x06 \x01(\x05\x12,\n\x0brate_limits\x18\x07 \x03(\x0b\x32\x17.v1.CreateTaskRateLimit\x12;\n\rworker_labels\x18\x08 \x03(\x0b\x32$.v1.CreateTaskOpts.WorkerLabelsEntry\x12\x1b\n\x0e\x62\x61\x63koff_factor\x18\t \x01(\x02H\x00\x88\x01\x01\x12 \n\x13\x62\x61\x63koff_max_seconds\x18\n \x01(\x05H\x01\x88\x01\x01\x12$\n\x0b\x63oncurrency\x18\x0b \x03(\x0b\x32\x0f.v1.Concurrency\x12+\n\nconditions\x18\x0c \x01(\x0b\x32\x12.v1.TaskConditionsH\x02\x88\x01\x01\x12\x1d\n\x10schedule_timeout\x18\r \x01(\tH\x03\x88\x01\x01\x12\x12\n\nis_durable\x18\x0e \x01(\x08\x12;\n\rslot_requests\x18\x0f \x03(\x0b\x32$.v1.CreateTaskOpts.SlotRequestsEntry\x1aL\n\x11WorkerLabelsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12&\n\x05value\x18\x02 \x01(\x0b\x32\x17.v1.DesiredWorkerLabels:\x02\x38\x01\x1a\x33\n\x11SlotRequestsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x05:\x02\x38\x01\x42\x11\n\x0f_backoff_factorB\x16\n\x14_backoff_max_secondsB\r\n\x0b_conditionsB\x13\n\x11_schedule_timeout\"\xfd\x01\n\x13\x43reateTaskRateLimit\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x12\n\x05units\x18\x02 \x01(\x05H\x00\x88\x01\x01\x12\x15\n\x08key_expr\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x17\n\nunits_expr\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x1e\n\x11limit_values_expr\x18\x05 \x01(\tH\x03\x88\x01\x01\x12,\n\x08\x64uration\x18\x06 \x01(\x0e\x32\x15.v1.RateLimitDurationH\x04\x88\x01\x01\x42\x08\n\x06_unitsB\x0b\n\t_key_exprB\r\n\x0b_units_exprB\x14\n\x12_limit_values_exprB\x0b\n\t_duration\"@\n\x1d\x43reateWorkflowVersionResponse\x12\n\n\x02id\x18\x01 \x01(\t\x12\x13\n\x0bworkflow_id\x18\x02 \x01(\t\"+\n\x14GetRunDetailsRequest\x12\x13\n\x0b\x65xternal_id\x18\x01 \x01(\t\"\xaa\x01\n\rTaskRunDetail\x12\x13\n\x0b\x65xternal_id\x18\x01 \x01(\t\x12\x1d\n\x06status\x18\x02 \x01(\x0e\x32\r.v1.RunStatus\x12\x12\n\x05\x65rror\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06output\x18\x04 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x0breadable_id\x18\x05 \x01(\t\x12\x12\n\nis_evicted\x18\x06 \x01(\x08\x42\x08\n\x06_errorB\t\n\x07_output\"\x84\x02\n\x15GetRunDetailsResponse\x12\r\n\x05input\x18\x01 \x01(\x0c\x12\x1d\n\x06status\x18\x02 \x01(\x0e\x32\r.v1.RunStatus\x12:\n\ttask_runs\x18\x03 \x03(\x0b\x32\'.v1.GetRunDetailsResponse.TaskRunsEntry\x12\x0c\n\x04\x64one\x18\x04 \x01(\x08\x12\x1b\n\x13\x61\x64\x64itional_metadata\x18\x05 \x01(\x0c\x12\x12\n\nis_evicted\x18\x06 \x01(\x08\x1a\x42\n\rTaskRunsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12 \n\x05value\x18\x02 \x01(\x0b\x32\x11.v1.TaskRunDetail:\x02\x38\x01*$\n\x0eStickyStrategy\x12\x08\n\x04SOFT\x10\x00\x12\x08\n\x04HARD\x10\x01*]\n\x11RateLimitDuration\x12\n\n\x06SECOND\x10\x00\x12\n\n\x06MINUTE\x10\x01\x12\x08\n\x04HOUR\x10\x02\x12\x07\n\x03\x44\x41Y\x10\x03\x12\x08\n\x04WEEK\x10\x04\x12\t\n\x05MONTH\x10\x05\x12\x08\n\x04YEAR\x10\x06*[\n\tRunStatus\x12\n\n\x06QUEUED\x10\x00\x12\x0b\n\x07RUNNING\x10\x01\x12\r\n\tCOMPLETED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\x12\r\n\tCANCELLED\x10\x04\x12\x0b\n\x07\x45VICTED\x10\x05*\x7f\n\x18\x43oncurrencyLimitStrategy\x12\x16\n\x12\x43\x41NCEL_IN_PROGRESS\x10\x00\x12\x0f\n\x0b\x44ROP_NEWEST\x10\x01\x12\x10\n\x0cQUEUE_NEWEST\x10\x02\x12\x15\n\x11GROUP_ROUND_ROBIN\x10\x03\x12\x11\n\rCANCEL_NEWEST\x10\x04\x32\xcf\x03\n\x0c\x41\x64minService\x12R\n\x0bPutWorkflow\x12 .v1.CreateWorkflowVersionRequest\x1a!.v1.CreateWorkflowVersionResponse\x12>\n\x0b\x43\x61ncelTasks\x12\x16.v1.CancelTasksRequest\x1a\x17.v1.CancelTasksResponse\x12>\n\x0bReplayTasks\x12\x16.v1.ReplayTasksRequest\x1a\x17.v1.ReplayTasksResponse\x12S\n\x12TriggerWorkflowRun\x12\x1d.v1.TriggerWorkflowRunRequest\x1a\x1e.v1.TriggerWorkflowRunResponse\x12\x44\n\rGetRunDetails\x12\x18.v1.GetRunDetailsRequest\x1a\x19.v1.GetRunDetailsResponse\x12P\n\x11\x42ranchDurableTask\x12\x1c.v1.BranchDurableTaskRequest\x1a\x1d.v1.BranchDurableTaskResponseBBZ@github.com/hatchet-dev/hatchet/internal/services/shared/proto/v1b\x06proto3"
13
13
 
14
14
  pool = ::Google::Protobuf::DescriptorPool.generated_pool
15
15
  pool.add_serialized_file(descriptor_data)
@@ -25,6 +25,8 @@ module V1
25
25
  BranchDurableTaskRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("v1.BranchDurableTaskRequest").msgclass
26
26
  BranchDurableTaskResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("v1.BranchDurableTaskResponse").msgclass
27
27
  CreateWorkflowVersionRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("v1.CreateWorkflowVersionRequest").msgclass
28
+ IdempotencyConfig = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("v1.IdempotencyConfig").msgclass
29
+ IdempotencyCollisionError = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("v1.IdempotencyCollisionError").msgclass
28
30
  DefaultFilter = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("v1.DefaultFilter").msgclass
29
31
  Concurrency = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("v1.Concurrency").msgclass
30
32
  CreateTaskOpts = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("v1.CreateTaskOpts").msgclass
@@ -90,6 +90,8 @@ module Hatchet
90
90
  ack[:node_id],
91
91
  )
92
92
 
93
+ raise Hatchet::TaskRunError, result[:error_message] || "child task failed" if result[:is_failure]
94
+
93
95
  result[:payload] || {}
94
96
  end
95
97
  else
@@ -24,6 +24,34 @@ module Hatchet
24
24
  end
25
25
  end
26
26
 
27
+ # Raised when an idempotency key collision occurs.
28
+ # Contains the ID of the existing run that claimed the key.
29
+ class IdempotencyCollisionError < Error
30
+ # @return [String] The external ID of the existing workflow run
31
+ attr_reader :existing_run_external_id
32
+
33
+ def initialize(existing_run_external_id)
34
+ @existing_run_external_id = existing_run_external_id
35
+ super("idempotency key collision: existing run #{existing_run_external_id} already exists")
36
+ end
37
+ end
38
+
39
+ # Raised when one or more runs in a bulk trigger collide on idempotency keys.
40
+ # Contains the IDs of successfully triggered runs and the individual collision errors.
41
+ class BulkTriggerIdempotencyCollisionError < Error
42
+ # @return [Array<String>] External IDs of successfully triggered workflow runs
43
+ attr_reader :successful_workflow_run_external_ids
44
+
45
+ # @return [Array<IdempotencyCollisionError>] The individual collision errors
46
+ attr_reader :collisions
47
+
48
+ def initialize(successful_workflow_run_external_ids:, collisions:)
49
+ @successful_workflow_run_external_ids = successful_workflow_run_external_ids
50
+ @collisions = collisions
51
+ super("idempotency key collision in bulk trigger: #{collisions.length} collision(s)")
52
+ end
53
+ end
54
+
27
55
  # Represents an error from a failed task run
28
56
  class TaskRunError < Error
29
57
  # @return [String] The external ID of the failed task run
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hatchet
4
+ # TTL-based idempotency: prevents duplicate runs within a sliding time window.
5
+ #
6
+ # @example
7
+ # Hatchet::TTLBasedIdempotencyConfig.new(expression: "input.id", ttl_ms: 60_000)
8
+ class TTLBasedIdempotencyConfig
9
+ # @return [String] CEL expression evaluated against workflow input
10
+ attr_reader :expression
11
+
12
+ # @return [Integer] How long the idempotency key lives, in milliseconds
13
+ attr_reader :ttl_ms
14
+
15
+ # @param expression [String] CEL expression to derive the idempotency key
16
+ # @param ttl_ms [Integer] TTL for the idempotency key in milliseconds
17
+ def initialize(expression:, ttl_ms:)
18
+ @expression = expression
19
+ @ttl_ms = ttl_ms
20
+ end
21
+
22
+ # @return [V1::IdempotencyConfig]
23
+ def to_proto
24
+ ::V1::IdempotencyConfig.new(expression: @expression, ttl_ms: @ttl_ms)
25
+ end
26
+ end
27
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Hatchet
4
- VERSION = "0.3.0"
4
+ VERSION = "0.4.0"
5
5
  end
@@ -689,7 +689,7 @@ module Hatchet
689
689
 
690
690
  def parse_entry_completed(completed)
691
691
  payload = nil
692
- if completed.payload && !completed.payload.empty?
692
+ if !completed.is_failure && completed.payload && !completed.payload.empty?
693
693
  begin
694
694
  payload_json = completed.payload.dup.force_encoding("UTF-8")
695
695
  payload = JSON.parse(payload_json)
@@ -702,6 +702,8 @@ module Hatchet
702
702
  durable_task_external_id: completed.ref.durable_task_external_id,
703
703
  node_id: completed.ref.node_id,
704
704
  payload: payload,
705
+ is_failure: completed.is_failure,
706
+ error_message: completed.error_message,
705
707
  }
706
708
  end
707
709
 
@@ -46,6 +46,9 @@ module Hatchet
46
46
  # @return [Task, nil] The on_success task
47
47
  attr_reader :on_success
48
48
 
49
+ # @return [Hatchet::TTLBasedIdempotencyConfig, nil] Idempotency configuration
50
+ attr_reader :idempotency
51
+
49
52
  # @return [String, nil] The workflow ID writer (set after registration)
50
53
  attr_writer :id
51
54
 
@@ -57,6 +60,7 @@ module Hatchet
57
60
  # @param task_defaults [Hash, nil] Default task settings
58
61
  # @param default_filters [Array<DefaultFilter>] Default filters
59
62
  # @param sticky [Symbol, nil] Sticky strategy
63
+ # @param idempotency [Hatchet::TTLBasedIdempotencyConfig, nil] Idempotency configuration
60
64
  # @param client [Hatchet::Client, nil] The client
61
65
  def initialize(
62
66
  name:,
@@ -67,6 +71,7 @@ module Hatchet
67
71
  task_defaults: nil,
68
72
  default_filters: [],
69
73
  sticky: nil,
74
+ idempotency: nil,
70
75
  client: nil
71
76
  )
72
77
  @name = name
@@ -78,6 +83,7 @@ module Hatchet
78
83
  @task_defaults = task_defaults
79
84
  @default_filters = default_filters
80
85
  @sticky = sticky
86
+ @idempotency = idempotency
81
87
  @client = client
82
88
  @on_failure = nil
83
89
  @on_success = nil
@@ -208,6 +214,8 @@ module Hatchet
208
214
  args[:default_priority] = @default_priority if @default_priority
209
215
  args[:default_filters] = filter_protos unless filter_protos.empty?
210
216
 
217
+ args[:idempotency] = @idempotency.to_proto if @idempotency
218
+
211
219
  ::V1::CreateWorkflowVersionRequest.new(**args)
212
220
  end
213
221
 
data/lib/hatchet-sdk.rb CHANGED
@@ -3,80 +3,72 @@
3
3
  require_relative "hatchet/version"
4
4
  require_relative "hatchet/config"
5
5
 
6
- # Define base error class before loading submodules that depend on it
7
- module Hatchet
8
- # Base error class for all Hatchet-related errors
9
- class Error < StandardError; end
10
- end
11
-
12
- require_relative "hatchet/clients"
13
- require_relative "hatchet/features/events"
14
- require_relative "hatchet/features/runs"
15
- require_relative "hatchet/features/tenant"
16
- require_relative "hatchet/features/logs"
17
- require_relative "hatchet/features/workers"
18
- require_relative "hatchet/features/cel"
19
- require_relative "hatchet/features/workflows"
20
- require_relative "hatchet/features/filters"
21
- require_relative "hatchet/features/metrics"
22
- require_relative "hatchet/features/rate_limits"
23
- require_relative "hatchet/features/cron"
24
- require_relative "hatchet/features/scheduled"
25
-
26
- # Core classes
27
- require_relative "hatchet/exceptions"
28
- require_relative "hatchet/engine_version"
29
- require_relative "hatchet/eviction_policy"
30
- require_relative "hatchet/concurrency"
31
- require_relative "hatchet/conditions"
32
- require_relative "hatchet/condition_converter"
33
- require_relative "hatchet/rate_limit"
34
- require_relative "hatchet/labels"
35
- require_relative "hatchet/trigger_options"
36
- require_relative "hatchet/default_filter"
37
- require_relative "hatchet/workflow_run"
38
- require_relative "hatchet/context"
39
- require_relative "hatchet/durable_context"
40
- require_relative "hatchet/task"
41
- require_relative "hatchet/workflow"
42
- require_relative "hatchet/context_vars"
43
- require_relative "hatchet/worker_obj"
44
-
45
- # gRPC connection and client infrastructure
46
- require_relative "hatchet/connection"
47
-
48
- # Generated protobuf contracts (add contracts directory to load path for internal requires)
49
- $LOAD_PATH.unshift(File.join(__dir__, "hatchet", "contracts")) unless $LOAD_PATH.include?(File.join(__dir__, "hatchet", "contracts"))
50
- require_relative "hatchet/contracts/dispatcher/dispatcher_pb"
51
- require_relative "hatchet/contracts/dispatcher/dispatcher_services_pb"
52
- require_relative "hatchet/contracts/events/events_pb"
53
- require_relative "hatchet/contracts/events/events_services_pb"
54
- require_relative "hatchet/contracts/workflows/workflows_pb"
55
- require_relative "hatchet/contracts/workflows/workflows_services_pb"
56
- require_relative "hatchet/contracts/v1/shared/condition_pb"
57
- require_relative "hatchet/contracts/v1/shared/trigger_pb"
58
- require_relative "hatchet/contracts/v1/dispatcher_pb"
59
- require_relative "hatchet/contracts/v1/dispatcher_services_pb"
60
- require_relative "hatchet/contracts/v1/workflows_pb"
61
- require_relative "hatchet/contracts/v1/workflows_services_pb"
62
-
63
- # gRPC client wrappers
64
- require_relative "hatchet/clients/grpc/dispatcher"
65
- require_relative "hatchet/clients/grpc/admin"
66
- require_relative "hatchet/clients/grpc/event_client"
67
-
68
- # Worker runtime
69
- require_relative "hatchet/worker/action_listener"
70
- require_relative "hatchet/worker/workflow_run_listener"
71
- require_relative "hatchet/worker/durable_eviction/cache"
72
- require_relative "hatchet/worker/durable_eviction/manager"
73
- require_relative "hatchet/worker/durable_event_listener"
74
- require_relative "hatchet/worker/runner"
75
-
76
6
  # Ruby SDK for Hatchet workflow engine
77
7
  #
78
8
  # @see https://docs.hatchet.run for Hatchet documentation
79
9
  module Hatchet
10
+ class Error < StandardError; end
11
+
12
+ require_relative "hatchet/clients"
13
+ require_relative "hatchet/features/events"
14
+ require_relative "hatchet/features/runs"
15
+ require_relative "hatchet/features/tenant"
16
+ require_relative "hatchet/features/logs"
17
+ require_relative "hatchet/features/workers"
18
+ require_relative "hatchet/features/cel"
19
+ require_relative "hatchet/features/workflows"
20
+ require_relative "hatchet/features/filters"
21
+ require_relative "hatchet/features/metrics"
22
+ require_relative "hatchet/features/rate_limits"
23
+ require_relative "hatchet/features/cron"
24
+ require_relative "hatchet/features/scheduled"
25
+
26
+ require_relative "hatchet/exceptions"
27
+ require_relative "hatchet/engine_version"
28
+ require_relative "hatchet/eviction_policy"
29
+ require_relative "hatchet/concurrency"
30
+ require_relative "hatchet/idempotency"
31
+ require_relative "hatchet/conditions"
32
+ require_relative "hatchet/condition_converter"
33
+ require_relative "hatchet/rate_limit"
34
+ require_relative "hatchet/labels"
35
+ require_relative "hatchet/trigger_options"
36
+ require_relative "hatchet/default_filter"
37
+ require_relative "hatchet/workflow_run"
38
+ require_relative "hatchet/context"
39
+ require_relative "hatchet/durable_context"
40
+ require_relative "hatchet/task"
41
+ require_relative "hatchet/workflow"
42
+ require_relative "hatchet/context_vars"
43
+ require_relative "hatchet/worker_obj"
44
+
45
+ require_relative "hatchet/connection"
46
+
47
+ $LOAD_PATH.unshift(File.join(__dir__, "hatchet", "contracts")) unless $LOAD_PATH.include?(File.join(__dir__, "hatchet", "contracts"))
48
+ require_relative "hatchet/contracts/dispatcher/dispatcher_pb"
49
+ require_relative "hatchet/contracts/dispatcher/dispatcher_services_pb"
50
+ require_relative "hatchet/contracts/events/events_pb"
51
+ require_relative "hatchet/contracts/events/events_services_pb"
52
+ require_relative "hatchet/contracts/workflows/workflows_pb"
53
+ require_relative "hatchet/contracts/workflows/workflows_services_pb"
54
+ require_relative "hatchet/contracts/v1/shared/condition_pb"
55
+ require_relative "hatchet/contracts/v1/shared/trigger_pb"
56
+ require_relative "hatchet/contracts/v1/dispatcher_pb"
57
+ require_relative "hatchet/contracts/v1/dispatcher_services_pb"
58
+ require_relative "hatchet/contracts/v1/workflows_pb"
59
+ require_relative "hatchet/contracts/v1/workflows_services_pb"
60
+
61
+ require_relative "hatchet/clients/grpc/dispatcher"
62
+ require_relative "hatchet/clients/grpc/admin"
63
+ require_relative "hatchet/clients/grpc/event_client"
64
+
65
+ require_relative "hatchet/worker/action_listener"
66
+ require_relative "hatchet/worker/workflow_run_listener"
67
+ require_relative "hatchet/worker/durable_eviction/cache"
68
+ require_relative "hatchet/worker/durable_eviction/manager"
69
+ require_relative "hatchet/worker/durable_event_listener"
70
+ require_relative "hatchet/worker/runner"
71
+
80
72
  # The main client for interacting with Hatchet services.
81
73
  #
82
74
  # @example Basic usage with API token
@@ -195,7 +187,7 @@ module Hatchet
195
187
  # Create a new workflow definition
196
188
  #
197
189
  # @param name [String] Workflow name
198
- # @param opts [Hash] Workflow options
190
+ # @param opts [Hash] Workflow options (on_events:, concurrency:, idempotency:, etc.)
199
191
  # @return [Hatchet::Workflow]
200
192
  #
201
193
  # @example
@@ -208,17 +200,17 @@ module Hatchet
208
200
  # Create a standalone task (auto-wraps in a single-task workflow)
209
201
  #
210
202
  # @param name [String] Task name
211
- # @param opts [Hash] Task options
203
+ # @param opts [Hash] Task options (on_events:, idempotency:, retries:, etc.)
212
204
  # @yield [input, ctx] The task execution block
213
205
  # @return [Hatchet::Task]
214
206
  #
215
207
  # @example
216
208
  # my_task = hatchet.task(name: "my_task") { |input, ctx| { "result" => "done" } }
217
209
  def task(name:, **opts, &block)
218
- # Create a workflow wrapper for standalone tasks
219
210
  wf = Workflow.new(name: name, client: self,
220
211
  on_events: opts.delete(:on_events) || [],
221
- default_filters: opts.delete(:default_filters) || [],)
212
+ default_filters: opts.delete(:default_filters) || [],
213
+ idempotency: opts.delete(:idempotency),)
222
214
  wf.task(name, **opts, &block)
223
215
  end
224
216
 
@@ -0,0 +1,9 @@
1
+ module Hatchet
2
+ class TTLBasedIdempotencyConfig
3
+ attr_reader expression: String
4
+ attr_reader ttl_ms: Integer
5
+
6
+ def initialize: (expression: String, ttl_ms: Integer) -> void
7
+ def to_proto: () -> untyped
8
+ end
9
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hatchet-sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - gabriel ruttner
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-04-28 00:00:00.000000000 Z
11
+ date: 2026-07-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '1.1'
83
+ - !ruby/object:Gem::Dependency
84
+ name: googleapis-common-protos-types
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '1.0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '1.0'
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: google-protobuf
85
99
  requirement: !ruby/object:Gem::Requirement
@@ -472,6 +486,7 @@ files:
472
486
  - lib/hatchet/features/tenant.rb
473
487
  - lib/hatchet/features/workers.rb
474
488
  - lib/hatchet/features/workflows.rb
489
+ - lib/hatchet/idempotency.rb
475
490
  - lib/hatchet/labels.rb
476
491
  - lib/hatchet/rate_limit.rb
477
492
  - lib/hatchet/task.rb
@@ -519,6 +534,7 @@ files:
519
534
  - sig/hatchet/features/tenant.rbs
520
535
  - sig/hatchet/features/workers.rbs
521
536
  - sig/hatchet/features/workflows.rbs
537
+ - sig/hatchet/idempotency.rbs
522
538
  - sig/hatchet/labels.rbs
523
539
  - sig/hatchet/rate_limit.rbs
524
540
  - sig/hatchet/task.rbs