shikibu 0.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.
@@ -0,0 +1,284 @@
1
+ -- migrate:up
2
+
3
+ -- Schema version tracking
4
+ CREATE TABLE IF NOT EXISTS schema_version (
5
+ version INTEGER PRIMARY KEY,
6
+ applied_at TEXT NOT NULL DEFAULT (datetime('now')),
7
+ description TEXT NOT NULL
8
+ );
9
+
10
+ -- Workflow definitions (source code storage)
11
+ CREATE TABLE IF NOT EXISTS workflow_definitions (
12
+ workflow_name TEXT NOT NULL,
13
+ source_hash TEXT NOT NULL,
14
+ source_code TEXT NOT NULL,
15
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
16
+ PRIMARY KEY (workflow_name, source_hash)
17
+ );
18
+
19
+ CREATE INDEX IF NOT EXISTS idx_definitions_name ON workflow_definitions(workflow_name);
20
+ CREATE INDEX IF NOT EXISTS idx_definitions_hash ON workflow_definitions(source_hash);
21
+
22
+ -- Workflow instances with distributed locking support
23
+ CREATE TABLE IF NOT EXISTS workflow_instances (
24
+ instance_id TEXT PRIMARY KEY,
25
+ workflow_name TEXT NOT NULL,
26
+ source_hash TEXT NOT NULL,
27
+ owner_service TEXT NOT NULL,
28
+ framework TEXT NOT NULL DEFAULT 'python',
29
+ status TEXT NOT NULL DEFAULT 'running',
30
+ current_activity_id TEXT,
31
+ continued_from TEXT,
32
+ started_at TEXT NOT NULL DEFAULT (datetime('now')),
33
+ updated_at TEXT NOT NULL DEFAULT (datetime('now')),
34
+ input_data TEXT NOT NULL,
35
+ output_data TEXT,
36
+ locked_by TEXT,
37
+ locked_at TEXT,
38
+ lock_timeout_seconds INTEGER,
39
+ lock_expires_at TEXT,
40
+ CONSTRAINT valid_status CHECK (
41
+ status IN ('running', 'completed', 'failed', 'waiting_for_event', 'waiting_for_timer', 'waiting_for_message', 'compensating', 'cancelled', 'recurred')
42
+ ),
43
+ FOREIGN KEY (continued_from) REFERENCES workflow_instances(instance_id),
44
+ FOREIGN KEY (workflow_name, source_hash) REFERENCES workflow_definitions(workflow_name, source_hash)
45
+ );
46
+
47
+ CREATE INDEX IF NOT EXISTS idx_instances_status ON workflow_instances(status);
48
+ CREATE INDEX IF NOT EXISTS idx_instances_workflow ON workflow_instances(workflow_name);
49
+ CREATE INDEX IF NOT EXISTS idx_instances_owner ON workflow_instances(owner_service);
50
+ CREATE INDEX IF NOT EXISTS idx_instances_framework ON workflow_instances(framework);
51
+ CREATE INDEX IF NOT EXISTS idx_instances_locked ON workflow_instances(locked_by, locked_at);
52
+ CREATE INDEX IF NOT EXISTS idx_instances_lock_expires ON workflow_instances(lock_expires_at);
53
+ CREATE INDEX IF NOT EXISTS idx_instances_updated ON workflow_instances(updated_at);
54
+ CREATE INDEX IF NOT EXISTS idx_instances_hash ON workflow_instances(source_hash);
55
+ CREATE INDEX IF NOT EXISTS idx_instances_continued_from ON workflow_instances(continued_from);
56
+ CREATE INDEX IF NOT EXISTS idx_instances_resumable ON workflow_instances(status, locked_by);
57
+
58
+ -- Workflow execution history (for deterministic replay)
59
+ CREATE TABLE IF NOT EXISTS workflow_history (
60
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
61
+ instance_id TEXT NOT NULL,
62
+ activity_id TEXT NOT NULL,
63
+ event_type TEXT NOT NULL,
64
+ data_type TEXT NOT NULL DEFAULT 'json',
65
+ event_data TEXT,
66
+ event_data_binary BLOB,
67
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
68
+ FOREIGN KEY (instance_id) REFERENCES workflow_instances(instance_id) ON DELETE CASCADE,
69
+ CONSTRAINT unique_instance_activity UNIQUE (instance_id, activity_id)
70
+ );
71
+
72
+ CREATE INDEX IF NOT EXISTS idx_history_instance ON workflow_history(instance_id, activity_id);
73
+ CREATE INDEX IF NOT EXISTS idx_history_created ON workflow_history(created_at);
74
+
75
+ -- Archived workflow history (for recur pattern)
76
+ CREATE TABLE IF NOT EXISTS workflow_history_archive (
77
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
78
+ instance_id TEXT NOT NULL,
79
+ activity_id TEXT NOT NULL,
80
+ event_type TEXT NOT NULL,
81
+ data_type TEXT NOT NULL DEFAULT 'json',
82
+ event_data TEXT,
83
+ event_data_binary BLOB,
84
+ created_at TEXT NOT NULL,
85
+ archived_at TEXT NOT NULL DEFAULT (datetime('now')),
86
+ FOREIGN KEY (instance_id) REFERENCES workflow_instances(instance_id) ON DELETE CASCADE
87
+ );
88
+
89
+ CREATE INDEX IF NOT EXISTS idx_history_archive_instance ON workflow_history_archive(instance_id);
90
+ CREATE INDEX IF NOT EXISTS idx_history_archive_archived ON workflow_history_archive(archived_at);
91
+
92
+ -- Compensation transactions (LIFO stack for Saga pattern)
93
+ CREATE TABLE IF NOT EXISTS workflow_compensations (
94
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
95
+ instance_id TEXT NOT NULL,
96
+ activity_id TEXT NOT NULL,
97
+ activity_name TEXT NOT NULL,
98
+ args TEXT NOT NULL,
99
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
100
+ FOREIGN KEY (instance_id) REFERENCES workflow_instances(instance_id) ON DELETE CASCADE
101
+ );
102
+
103
+ CREATE INDEX IF NOT EXISTS idx_compensations_instance ON workflow_compensations(instance_id, created_at DESC);
104
+
105
+ -- Timer subscriptions (for wait_timer)
106
+ CREATE TABLE IF NOT EXISTS workflow_timer_subscriptions (
107
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
108
+ instance_id TEXT NOT NULL,
109
+ timer_id TEXT NOT NULL,
110
+ expires_at TEXT NOT NULL,
111
+ activity_id TEXT,
112
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
113
+ FOREIGN KEY (instance_id) REFERENCES workflow_instances(instance_id) ON DELETE CASCADE,
114
+ CONSTRAINT unique_instance_timer UNIQUE (instance_id, timer_id)
115
+ );
116
+
117
+ CREATE INDEX IF NOT EXISTS idx_timer_subscriptions_expires ON workflow_timer_subscriptions(expires_at);
118
+ CREATE INDEX IF NOT EXISTS idx_timer_subscriptions_instance ON workflow_timer_subscriptions(instance_id);
119
+
120
+ -- Group memberships (Erlang pg style)
121
+ CREATE TABLE IF NOT EXISTS workflow_group_memberships (
122
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
123
+ instance_id TEXT NOT NULL,
124
+ group_name TEXT NOT NULL,
125
+ joined_at TEXT NOT NULL DEFAULT (datetime('now')),
126
+ FOREIGN KEY (instance_id) REFERENCES workflow_instances(instance_id) ON DELETE CASCADE,
127
+ CONSTRAINT unique_instance_group UNIQUE (instance_id, group_name)
128
+ );
129
+
130
+ CREATE INDEX IF NOT EXISTS idx_group_memberships_group ON workflow_group_memberships(group_name);
131
+ CREATE INDEX IF NOT EXISTS idx_group_memberships_instance ON workflow_group_memberships(instance_id);
132
+
133
+ -- Transactional outbox pattern
134
+ CREATE TABLE IF NOT EXISTS outbox_events (
135
+ event_id TEXT PRIMARY KEY,
136
+ event_type TEXT NOT NULL,
137
+ event_source TEXT NOT NULL,
138
+ data_type TEXT NOT NULL DEFAULT 'json',
139
+ event_data TEXT,
140
+ event_data_binary BLOB,
141
+ content_type TEXT NOT NULL DEFAULT 'application/json',
142
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
143
+ published_at TEXT,
144
+ status TEXT NOT NULL DEFAULT 'pending',
145
+ retry_count INTEGER DEFAULT 0,
146
+ last_error TEXT,
147
+ CONSTRAINT valid_outbox_status CHECK (status IN ('pending', 'processing', 'published', 'failed', 'invalid', 'expired'))
148
+ );
149
+
150
+ CREATE INDEX IF NOT EXISTS idx_outbox_status ON outbox_events(status, created_at);
151
+ CREATE INDEX IF NOT EXISTS idx_outbox_retry ON outbox_events(status, retry_count);
152
+ CREATE INDEX IF NOT EXISTS idx_outbox_published ON outbox_events(published_at);
153
+
154
+ -- Channel messages (persistent message queue)
155
+ CREATE TABLE IF NOT EXISTS channel_messages (
156
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
157
+ channel TEXT NOT NULL,
158
+ message_id TEXT NOT NULL UNIQUE,
159
+ data_type TEXT NOT NULL,
160
+ data TEXT,
161
+ data_binary BLOB,
162
+ metadata TEXT,
163
+ published_at TEXT NOT NULL DEFAULT (datetime('now')),
164
+ CONSTRAINT valid_data_type CHECK (data_type IN ('json', 'binary')),
165
+ CONSTRAINT data_type_consistency CHECK (
166
+ (data_type = 'json' AND data IS NOT NULL AND data_binary IS NULL) OR
167
+ (data_type = 'binary' AND data IS NULL AND data_binary IS NOT NULL)
168
+ )
169
+ );
170
+
171
+ CREATE INDEX IF NOT EXISTS idx_channel_messages_channel ON channel_messages(channel, published_at);
172
+ CREATE INDEX IF NOT EXISTS idx_channel_messages_id ON channel_messages(id);
173
+
174
+ -- Channel subscriptions
175
+ CREATE TABLE IF NOT EXISTS channel_subscriptions (
176
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
177
+ instance_id TEXT NOT NULL,
178
+ channel TEXT NOT NULL,
179
+ mode TEXT NOT NULL,
180
+ activity_id TEXT,
181
+ cursor_message_id INTEGER,
182
+ timeout_at TEXT,
183
+ subscribed_at TEXT NOT NULL DEFAULT (datetime('now')),
184
+ FOREIGN KEY (instance_id) REFERENCES workflow_instances(instance_id) ON DELETE CASCADE,
185
+ CONSTRAINT valid_mode CHECK (mode IN ('broadcast', 'competing')),
186
+ CONSTRAINT unique_instance_channel UNIQUE (instance_id, channel)
187
+ );
188
+
189
+ CREATE INDEX IF NOT EXISTS idx_channel_subscriptions_channel ON channel_subscriptions(channel);
190
+ CREATE INDEX IF NOT EXISTS idx_channel_subscriptions_instance ON channel_subscriptions(instance_id);
191
+ CREATE INDEX IF NOT EXISTS idx_channel_subscriptions_waiting ON channel_subscriptions(channel, activity_id);
192
+ CREATE INDEX IF NOT EXISTS idx_channel_subscriptions_timeout ON channel_subscriptions(timeout_at);
193
+
194
+ -- Channel delivery cursors (broadcast mode: track who read what)
195
+ CREATE TABLE IF NOT EXISTS channel_delivery_cursors (
196
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
197
+ channel TEXT NOT NULL,
198
+ instance_id TEXT NOT NULL,
199
+ last_delivered_id INTEGER NOT NULL,
200
+ updated_at TEXT NOT NULL DEFAULT (datetime('now')),
201
+ FOREIGN KEY (instance_id) REFERENCES workflow_instances(instance_id) ON DELETE CASCADE,
202
+ CONSTRAINT unique_channel_instance UNIQUE (channel, instance_id)
203
+ );
204
+
205
+ CREATE INDEX IF NOT EXISTS idx_channel_delivery_cursors_channel ON channel_delivery_cursors(channel);
206
+
207
+ -- Channel message claims (competing mode: who is processing what)
208
+ CREATE TABLE IF NOT EXISTS channel_message_claims (
209
+ message_id TEXT PRIMARY KEY,
210
+ instance_id TEXT NOT NULL,
211
+ claimed_at TEXT NOT NULL DEFAULT (datetime('now')),
212
+ FOREIGN KEY (message_id) REFERENCES channel_messages(message_id) ON DELETE CASCADE,
213
+ FOREIGN KEY (instance_id) REFERENCES workflow_instances(instance_id) ON DELETE CASCADE
214
+ );
215
+
216
+ CREATE INDEX IF NOT EXISTS idx_channel_message_claims_instance ON channel_message_claims(instance_id);
217
+
218
+ -- System locks (for coordinating background tasks across pods)
219
+ CREATE TABLE IF NOT EXISTS system_locks (
220
+ lock_name TEXT PRIMARY KEY,
221
+ locked_by TEXT,
222
+ locked_at TEXT,
223
+ lock_expires_at TEXT
224
+ );
225
+
226
+ CREATE INDEX IF NOT EXISTS idx_system_locks_expires ON system_locks(lock_expires_at);
227
+
228
+
229
+ -- migrate:down
230
+
231
+ DROP INDEX IF EXISTS idx_channel_message_claims_instance;
232
+ DROP TABLE IF EXISTS channel_message_claims;
233
+
234
+ DROP INDEX IF EXISTS idx_channel_delivery_cursors_channel;
235
+ DROP TABLE IF EXISTS channel_delivery_cursors;
236
+
237
+ DROP INDEX IF EXISTS idx_channel_subscriptions_waiting;
238
+ DROP INDEX IF EXISTS idx_channel_subscriptions_instance;
239
+ DROP INDEX IF EXISTS idx_channel_subscriptions_channel;
240
+ DROP TABLE IF EXISTS channel_subscriptions;
241
+
242
+ DROP INDEX IF EXISTS idx_channel_messages_id;
243
+ DROP INDEX IF EXISTS idx_channel_messages_channel;
244
+ DROP TABLE IF EXISTS channel_messages;
245
+
246
+ DROP INDEX IF EXISTS idx_outbox_published;
247
+ DROP INDEX IF EXISTS idx_outbox_retry;
248
+ DROP INDEX IF EXISTS idx_outbox_status;
249
+ DROP TABLE IF EXISTS outbox_events;
250
+
251
+ DROP INDEX IF EXISTS idx_group_memberships_instance;
252
+ DROP INDEX IF EXISTS idx_group_memberships_group;
253
+ DROP TABLE IF EXISTS workflow_group_memberships;
254
+
255
+ DROP INDEX IF EXISTS idx_timer_subscriptions_instance;
256
+ DROP INDEX IF EXISTS idx_timer_subscriptions_expires;
257
+ DROP TABLE IF EXISTS workflow_timer_subscriptions;
258
+
259
+ DROP INDEX IF EXISTS idx_compensations_instance;
260
+ DROP TABLE IF EXISTS workflow_compensations;
261
+
262
+ DROP INDEX IF EXISTS idx_history_archive_archived;
263
+ DROP INDEX IF EXISTS idx_history_archive_instance;
264
+ DROP TABLE IF EXISTS workflow_history_archive;
265
+
266
+ DROP INDEX IF EXISTS idx_history_created;
267
+ DROP INDEX IF EXISTS idx_history_instance;
268
+ DROP TABLE IF EXISTS workflow_history;
269
+
270
+ DROP INDEX IF EXISTS idx_instances_continued_from;
271
+ DROP INDEX IF EXISTS idx_instances_hash;
272
+ DROP INDEX IF EXISTS idx_instances_updated;
273
+ DROP INDEX IF EXISTS idx_instances_locked;
274
+ DROP INDEX IF EXISTS idx_instances_framework;
275
+ DROP INDEX IF EXISTS idx_instances_owner;
276
+ DROP INDEX IF EXISTS idx_instances_workflow;
277
+ DROP INDEX IF EXISTS idx_instances_status;
278
+ DROP TABLE IF EXISTS workflow_instances;
279
+
280
+ DROP INDEX IF EXISTS idx_definitions_hash;
281
+ DROP INDEX IF EXISTS idx_definitions_name;
282
+ DROP TABLE IF EXISTS workflow_definitions;
283
+
284
+ DROP TABLE IF EXISTS schema_version;
@@ -0,0 +1,91 @@
1
+ # Database Column Values Reference
2
+
3
+ This document defines the standard values for database columns that must be consistent across all Durax framework implementations (Edda/Python, Romancy/Go, etc.).
4
+
5
+ ## workflow_instances.status
6
+
7
+ | Value | Description |
8
+ |-------|-------------|
9
+ | `running` | Workflow is actively executing |
10
+ | `completed` | Workflow finished successfully |
11
+ | `failed` | Workflow failed with an error |
12
+ | `cancelled` | Workflow was cancelled by user |
13
+ | `waiting_for_event` | Workflow is waiting for an external event (via `wait_event`) |
14
+ | `waiting_for_timer` | Workflow is waiting for a timer to expire (via `sleep`/`sleep_until`) |
15
+ | `waiting_for_message` | Workflow is waiting for a channel message (via `receive`) |
16
+ | `recurred` | Workflow completed one iteration and is ready to restart (Erlang-style tail recursion) |
17
+ | `compensating` | Workflow is executing compensation handlers |
18
+
19
+ ## workflow_history.event_type
20
+
21
+ All event types use **PascalCase** for cross-language consistency.
22
+
23
+ | Value | Description |
24
+ |-------|-------------|
25
+ | `ActivityCompleted` | An activity finished successfully |
26
+ | `ActivityFailed` | An activity failed with an error |
27
+ | `EventReceived` | An external event was received (via `wait_event`) |
28
+ | `TimerExpired` | A timer expired (via `sleep`/`sleep_until`) |
29
+ | `ChannelMessageReceived` | A channel message was received (via `receive`) |
30
+ | `MessageTimeout` | A channel message receive timed out |
31
+ | `CompensationExecuted` | A compensation handler executed successfully |
32
+ | `CompensationFailed` | A compensation handler failed |
33
+ | `WorkflowFailed` | The workflow failed (recorded before status update) |
34
+ | `WorkflowCancelled` | The workflow was cancelled |
35
+
36
+ ## workflow_history.data_type
37
+
38
+ | Value | Description |
39
+ |-------|-------------|
40
+ | `json` | Event data is stored as JSON text in `event_data` column |
41
+ | `binary` | Event data is stored as raw bytes in `event_data_binary` column |
42
+
43
+ ## channel_subscriptions.mode
44
+
45
+ | Value | Description |
46
+ |-------|-------------|
47
+ | `broadcast` | All subscribers receive a copy of each message |
48
+ | `competing` | Only one subscriber receives each message (work queue pattern) |
49
+
50
+ ## channel_messages.data_type
51
+
52
+ | Value | Description |
53
+ |-------|-------------|
54
+ | `json` | Message data is stored as JSON text in `data` column |
55
+ | `binary` | Message data is stored as raw bytes in `data_binary` column |
56
+
57
+ ## outbox_events.status
58
+
59
+ | Value | Description |
60
+ |-------|-------------|
61
+ | `pending` | Event is waiting to be published |
62
+ | `processing` | Event is currently being processed by the relayer |
63
+ | `published` | Event was successfully published |
64
+ | `failed` | Event publishing failed after all retries |
65
+ | `invalid` | Event is malformed and cannot be published |
66
+ | `expired` | Event expired before it could be published |
67
+
68
+ ## workflow_instances.framework
69
+
70
+ | Value | Description |
71
+ |-------|-------------|
72
+ | `python` | Workflow is managed by Edda (Python) |
73
+ | `go` | Workflow is managed by Romancy (Go) |
74
+
75
+ This column enables cross-language interoperability: any framework can deliver messages to workflows managed by other frameworks, while each framework only processes (replays/resumes) its own workflows.
76
+
77
+ ---
78
+
79
+ ## Cross-Language Compatibility Notes
80
+
81
+ 1. **Event type naming**: Always use PascalCase (e.g., `ActivityCompleted`, not `activity_completed`)
82
+
83
+ 2. **Message delivery**: Any framework can deliver channel messages to any workflow, regardless of the target workflow's framework. The delivering framework:
84
+ - Acquires a lock on the target workflow
85
+ - Records the message in `workflow_history` with the correct `event_type`
86
+ - Updates the workflow status to `running`
87
+ - Releases the lock
88
+
89
+ 3. **Workflow processing**: Each framework only processes workflows where `framework` matches its own identifier. This ensures correct replay behavior.
90
+
91
+ 4. **Activity IDs**: Both frameworks use the format `{activity_name}:{counter}` for deterministic replay (e.g., `fetch_data:1`, `receive_orders:2`).
metadata ADDED
@@ -0,0 +1,231 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: shikibu
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Yasushi Itoh
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2025-12-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: concurrent-ruby
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: connection_pool
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.4'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.4'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rack
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.1'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.1'
55
+ - !ruby/object:Gem::Dependency
56
+ name: sequel
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '5.0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '5.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: sqlite3
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '2.0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '2.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: mysql2
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.5'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.5'
97
+ - !ruby/object:Gem::Dependency
98
+ name: pg
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '1.5'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '1.5'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rake
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '13.0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '13.0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rspec
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '3.13'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '3.13'
139
+ - !ruby/object:Gem::Dependency
140
+ name: rubocop
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '1.68'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '1.68'
153
+ - !ruby/object:Gem::Dependency
154
+ name: testcontainers
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: '0.1'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - "~>"
165
+ - !ruby/object:Gem::Version
166
+ version: '0.1'
167
+ description: Shikibu is a Ruby port of Edda, providing deterministic replay, saga
168
+ pattern, and channel-based messaging for durable workflows.
169
+ email:
170
+ executables: []
171
+ extensions: []
172
+ extra_rdoc_files: []
173
+ files:
174
+ - LICENSE
175
+ - README.md
176
+ - lib/shikibu.rb
177
+ - lib/shikibu/activity.rb
178
+ - lib/shikibu/app.rb
179
+ - lib/shikibu/channels.rb
180
+ - lib/shikibu/constants.rb
181
+ - lib/shikibu/context.rb
182
+ - lib/shikibu/errors.rb
183
+ - lib/shikibu/integrations/active_job.rb
184
+ - lib/shikibu/integrations/sidekiq.rb
185
+ - lib/shikibu/locking.rb
186
+ - lib/shikibu/middleware/rack_app.rb
187
+ - lib/shikibu/notify/notify_base.rb
188
+ - lib/shikibu/notify/pg_notify.rb
189
+ - lib/shikibu/notify/wake_event.rb
190
+ - lib/shikibu/outbox/relayer.rb
191
+ - lib/shikibu/replay.rb
192
+ - lib/shikibu/retry_policy.rb
193
+ - lib/shikibu/storage/migrations.rb
194
+ - lib/shikibu/storage/sequel_storage.rb
195
+ - lib/shikibu/version.rb
196
+ - lib/shikibu/worker.rb
197
+ - lib/shikibu/workflow.rb
198
+ - schema/LICENSE
199
+ - schema/README.md
200
+ - schema/db/migrations/mysql/20251217000000_initial_schema.sql
201
+ - schema/db/migrations/postgresql/20251217000000_initial_schema.sql
202
+ - schema/db/migrations/sqlite/20251217000000_initial_schema.sql
203
+ - schema/docs/column-values.md
204
+ homepage: https://github.com/durax-io/shikibu
205
+ licenses:
206
+ - MIT
207
+ metadata:
208
+ homepage_uri: https://github.com/durax-io/shikibu
209
+ source_code_uri: https://github.com/durax-io/shikibu
210
+ changelog_uri: https://github.com/durax-io/shikibu/blob/main/CHANGELOG.md
211
+ rubygems_mfa_required: 'true'
212
+ post_install_message:
213
+ rdoc_options: []
214
+ require_paths:
215
+ - lib
216
+ required_ruby_version: !ruby/object:Gem::Requirement
217
+ requirements:
218
+ - - ">="
219
+ - !ruby/object:Gem::Version
220
+ version: 3.3.0
221
+ required_rubygems_version: !ruby/object:Gem::Requirement
222
+ requirements:
223
+ - - ">="
224
+ - !ruby/object:Gem::Version
225
+ version: '0'
226
+ requirements: []
227
+ rubygems_version: 3.5.22
228
+ signing_key:
229
+ specification_version: 4
230
+ summary: CloudEvents-native Durable Execution framework for Ruby
231
+ test_files: []