activejob-temporal 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/AGENTS.md +1 -0
  3. data/CHANGELOG.md +29 -0
  4. data/CLAUDE.md +274 -0
  5. data/CONTRIBUTING.md +69 -0
  6. data/README.md +34 -33
  7. data/activejob-temporal.gemspec +9 -13
  8. data/api/job_payload_schema.json +51 -6
  9. data/bin/temporal-worker +25 -17
  10. data/gemfiles/activejob_contract.gemfile +8 -0
  11. data/gemfiles/temporalio_contract.gemfile +7 -0
  12. data/lib/activejob/temporal/activities/aj_runner_activity.rb +75 -19
  13. data/lib/activejob/temporal/activities/dependency_status_activity.rb +36 -19
  14. data/lib/activejob/temporal/adapter.rb +4 -3
  15. data/lib/activejob/temporal/batch_enqueuer.rb +42 -33
  16. data/lib/activejob/temporal/bind_policy.rb +1 -1
  17. data/lib/activejob/temporal/cancel.rb +66 -29
  18. data/lib/activejob/temporal/certificate_watcher.rb +41 -6
  19. data/lib/activejob/temporal/client.rb +24 -6
  20. data/lib/activejob/temporal/conditional_enqueue.rb +2 -1
  21. data/lib/activejob/temporal/configurable.rb +56 -13
  22. data/lib/activejob/temporal/configuration.rb +218 -7
  23. data/lib/activejob/temporal/configured_job_compatibility.rb +91 -9
  24. data/lib/activejob/temporal/connection_worker_pool.rb +18 -1
  25. data/lib/activejob/temporal/dead_letter_queue.rb +64 -18
  26. data/lib/activejob/temporal/dependency_options.rb +126 -16
  27. data/lib/activejob/temporal/health_check_server.rb +14 -17
  28. data/lib/activejob/temporal/http_line_reader.rb +20 -2
  29. data/lib/activejob/temporal/http_request_failure_handling.rb +41 -0
  30. data/lib/activejob/temporal/inspect.rb +17 -7
  31. data/lib/activejob/temporal/job_id_validation.rb +41 -0
  32. data/lib/activejob/temporal/job_payload_dependencies.rb +23 -0
  33. data/lib/activejob/temporal/locales/en.yml +30 -3
  34. data/lib/activejob/temporal/metrics_server.rb +15 -18
  35. data/lib/activejob/temporal/middleware/chain.rb +7 -0
  36. data/lib/activejob/temporal/observability.rb +14 -2
  37. data/lib/activejob/temporal/payload.rb +64 -25
  38. data/lib/activejob/temporal/payload_encryption.rb +9 -1
  39. data/lib/activejob/temporal/payload_serializers.rb +3 -0
  40. data/lib/activejob/temporal/payload_storage.rb +4 -4
  41. data/lib/activejob/temporal/rails_environment_loader.rb +1 -8
  42. data/lib/activejob/temporal/reload_signal_queue.rb +19 -19
  43. data/lib/activejob/temporal/retry_handler_extractor.rb +18 -3
  44. data/lib/activejob/temporal/schedulable.rb +5 -7
  45. data/lib/activejob/temporal/schedule.rb +2 -2
  46. data/lib/activejob/temporal/signal_query.rb +35 -23
  47. data/lib/activejob/temporal/temporal_options.rb +26 -1
  48. data/lib/activejob/temporal/tls_file.rb +16 -16
  49. data/lib/activejob/temporal/transaction_safety.rb +102 -1
  50. data/lib/activejob/temporal/version.rb +1 -1
  51. data/lib/activejob/temporal/visibility_query.rb +16 -1
  52. data/lib/activejob/temporal/worker_pool.rb +13 -0
  53. data/lib/activejob/temporal/worker_registrations.rb +66 -0
  54. data/lib/activejob/temporal/worker_runtime.rb +17 -0
  55. data/lib/activejob/temporal/workflow_enqueuer.rb +2 -1
  56. data/lib/activejob/temporal/workflow_types.rb +10 -0
  57. data/lib/activejob/temporal/workflows/aj_workflow.rb +17 -4
  58. data/lib/activejob/temporal/workflows/workflow_dependencies.rb +148 -15
  59. data/lib/activejob/temporal.rb +21 -13
  60. data/test/mutant_unit_test.rb +13 -0
  61. metadata +46 -50
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bd1d95e1a889d180f79f3d2eeb2ec31d5c5b4338776cc41656b9077cbdd96da7
4
- data.tar.gz: 66c78d747c740b51e0320ca131d480160ef37af78496cf77632c68bfc1b7915d
3
+ metadata.gz: 8a0aefb6003d5c49d08c531d80ac7ce5ff82ee038a81aee31d3c5f0d2d438f61
4
+ data.tar.gz: c0f3f46a2e57f35a0c05290d129d9905f64c6dbe9be30371e79f99d0905d3cd0
5
5
  SHA512:
6
- metadata.gz: 2d87ef5a4b8a31de5990da5a9effd53bc7e9b1c4a59ad185d632a31d73b23c9b566358785ae5e469a3c96322b092b7b46b6387dd08d9b245d044da102690eafd
7
- data.tar.gz: b51db2d419c9301934241e0bb4b308ea7b398ecf3ba964cc8f7c82e73d75dfbf829839e79d1a42347dfe539b3d2cf289a74dff1516299806b0874face1e13449
6
+ metadata.gz: 16bd26f8f58993b6c60b21a16f0216fe928eb4ad341aad19483fe77cd3f2c2c21ecf67695969df2137fa2b4953a8f984ff1d446696ce9691e33e0431fa3af177
7
+ data.tar.gz: b06bd6d8ca772947228dacfe065e5582156d15fccc8501bff3ec14e8f3c18454d22d2b28c09cb1a59788462997067eaa9f12204ac265fd10273468cd653346ef
data/AGENTS.md ADDED
@@ -0,0 +1 @@
1
+ ./CLAUDE.md
data/CHANGELOG.md CHANGED
@@ -7,6 +7,35 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.3.0] - 2026-08-06
11
+
12
+ ### Added
13
+ - Bearer-token authentication: `api_key` (sent as the `Authorization: Bearer` header) and `api_key_file`, read through the same hardened path as TLS files — for example a projected Kubernetes ServiceAccount token. `api_key_watch` watches the token file and applies rotated tokens to the live connection via the new `ActiveJob::Temporal.refresh_api_key!` (no reconnect); enqueue-side processes using a rotating token file should call `refresh_api_key!` on their own schedule. `api_key` is redacted from `Configuration#inspect`. Caution: the Temporal SDK enables TLS when an API key is set and `tls` is nil — set `tls = false` explicitly for plaintext in-cluster servers (see the worker setup guide).
14
+ - Custom activity hosting: `worker_activities` registers additional activity classes (or class names) on the worker — for example activities invoked by workflows owned by another service, where only the activity name and JSON payloads travel over the wire. `worker_activejob_workloads = false` turns off the built-in ActiveJob workflows and activities for activities-only workers. Entries must be `Temporalio::Activity::Definition` subclasses; anything else fails worker startup with a `WorkerRegistrationError`.
15
+ - `graceful_shutdown_period` is passed to the Temporal worker, so a shutting-down worker lets running activities finish before their tasks are cancelled instead of cancelling them immediately.
16
+ - Configuration validation for the new attributes: `api_key_file` must be a readable regular file, `api_key_watch` requires `api_key_file`, disabling `worker_activejob_workloads` requires `worker_activities`, and `graceful_shutdown_period` must be >= 0.
17
+
18
+ ### Changed
19
+ - Allow temporalio 1.5 and 1.6 (dependency constraint is now `>= 1.4.0, < 1.7`); both added to the SDK contract-test matrix. Note for consumers upgrading within the range: temporalio 1.6 enables gzip gRPC transport compression by default — opt out with `grpc_compression` if an intermediary cannot handle it.
20
+
21
+ ## [0.2.0] - 2026-08-06
22
+
23
+ ### Security
24
+ - Reject payload-declared serializers not permitted by the configured `payload_serializer`, closing a `Marshal.load` code-execution path from attacker-supplied workflow input.
25
+ - Derive the AES-GCM payload encryption context from the activity context instead of the payload, so captured ciphertexts cannot be replayed into other workflows or namespaces.
26
+ - Reject legacy V1 (context-unbound) encrypted payloads by default; `allow_legacy_encrypted_payloads` opts back in during migration.
27
+ - Validate values interpolated into Temporal visibility queries with a strict allowlist, preventing query-structure injection through job IDs and class names in cancel/inspect APIs.
28
+ - Redact encryption keys and TLS settings from `Configuration#inspect`.
29
+ - Cap request line length and header count and enforce a whole-request deadline in the health check and metrics HTTP servers.
30
+
31
+ ### Fixed
32
+ - Retry certificate reloads after a failure so cert+key rotation pairs written within the debounce window are not dropped.
33
+ - Accept symlinked TLS certificate paths (resolved via `File.realpath`), unblocking Kubernetes secret volume mounts.
34
+ - Rescue `SystemCallError` in health check and metrics accept loops so errors like `EMFILE` no longer kill the listener silently.
35
+ - Reject overlapping worker pool health check and metrics port ranges at validation time instead of crash-looping the colliding child.
36
+ - Raise the documented `TemporalConnectionError` from client connect failures.
37
+ - Surface dead letter queue query failures instead of silently dropping entries, and fetch entries concurrently.
38
+
10
39
  <!-- github_changelog_generator:start -->
11
40
 
12
41
  **Implemented enhancements:**
data/CLAUDE.md ADDED
@@ -0,0 +1,274 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Project Overview
6
+
7
+ **activejob-temporal** is a Ruby gem providing a production-ready ActiveJob adapter backed by Temporal's durable execution engine. It enables Rails applications to leverage Temporal's reliability, observability, and fault-tolerance while maintaining full ActiveJob compatibility.
8
+
9
+ ### Architecture Pattern
10
+
11
+ The gem uses a **Temporal Workflow + Activity** pattern:
12
+ 1. **Adapter** (`lib/activejob/temporal/adapter.rb`): Translates ActiveJob's `perform_later` calls into Temporal workflow starts
13
+ 2. **Workflow** (`lib/activejob/temporal/workflows/aj_workflow.rb`): Orchestrates job execution with durable timers for scheduled jobs
14
+ 3. **Activity** (`lib/activejob/temporal/activities/aj_runner_activity.rb`): Executes the actual job logic
15
+ 4. **Supporting Services**: Payload serialization, retry mapping, search attributes, logging
16
+
17
+ Each enqueued job becomes a workflow execution, with optional activities for the job logic and retry handling managed through Temporal's native mechanisms.
18
+
19
+ ## Key Components
20
+
21
+ - **Configuration** (`lib/activejob/temporal.rb`): DSL-style setup with automatic validation (validates at end of configure block)
22
+ - **Client** (`lib/activejob/temporal/client.rb`): Temporal gRPC client builder with target/namespace/credentials handling
23
+ - **RetryMapper** (`lib/activejob/temporal/retry_mapper.rb`): Introspects ActiveJob `retry_on`/`discard_on` and converts to Temporal retry policies
24
+ - **Payload** (`lib/activejob/temporal/payload.rb`): Job argument serialization/deserialization with size validation
25
+ - **SearchAttributes** (`lib/activejob/temporal/search_attributes.rb`): Generates metadata for Temporal UI filtering
26
+ - **Logger** (`lib/activejob/temporal/logger.rb`): Structured logging with context awareness
27
+ - **Cancel** (`lib/activejob/temporal/cancel.rb`): Job cancellation API and workflow termination logic
28
+
29
+ ## Development Commands
30
+
31
+ ### Testing
32
+
33
+ ```bash
34
+ # Run all tests (unit + integration)
35
+ bundle exec rake spec
36
+
37
+ # Unit tests only (faster feedback)
38
+ bundle exec rake spec:unit
39
+
40
+ # Integration tests only (requires Temporal running)
41
+ bundle exec rake spec:integration
42
+
43
+ # Run a specific test file
44
+ bundle exec ruby -Ilib -Ispec spec/unit/adapter_spec.rb
45
+
46
+ # Run a specific test example
47
+ bundle exec ruby -Ilib -Ispec spec/unit/adapter_spec.rb --name /example name/
48
+ ```
49
+
50
+ Tests are organized by type in `spec/unit/` and `spec/integration/`. Integration tests use real Temporal workers to validate end-to-end behavior. See `spec/support/temporal_test_server.rb` for test Temporal setup.
51
+
52
+ ### Linting and Code Quality
53
+
54
+ ```bash
55
+ # Check for Rubocop violations
56
+ bundle exec rake rubocop
57
+
58
+ # Auto-fix violations
59
+ bundle exec rake rubocop:autocorrect
60
+
61
+ # JSON output for CI
62
+ ./tools/lint.sh
63
+ ```
64
+
65
+ All files must have `# frozen_string_literal: true` at the top.
66
+
67
+ ### Documentation
68
+
69
+ ```bash
70
+ # Generate YARD documentation
71
+ bundle exec rake yard
72
+ ```
73
+
74
+ Use YARD annotations extensively:
75
+ - `@param` for arguments (include types)
76
+ - `@return` for return values
77
+ - `@raise` for exceptions
78
+ - `@example` for usage patterns
79
+ - `@note` for important caveats
80
+ - `@api private` for internal methods
81
+
82
+ ### Temporal Setup (Local Development)
83
+
84
+ ```bash
85
+ # Start Temporal server, UI, and PostgreSQL
86
+ docker-compose up
87
+
88
+ # Temporal UI: http://localhost:8080
89
+ # Server: localhost:7233
90
+ ```
91
+
92
+ ### Running a Worker
93
+
94
+ The `temporal-worker` executable uses smart Rails auto-detection:
95
+
96
+ #### From a Rails Application
97
+
98
+ ```bash
99
+ cd your-rails-app
100
+ ACTIVEJOB_TEMPORAL_TARGET=localhost:7233 \
101
+ ACTIVEJOB_TEMPORAL_NAMESPACE=default \
102
+ ACTIVEJOB_TEMPORAL_TASK_QUEUE=default \
103
+ bundle exec temporal-worker
104
+ ```
105
+
106
+ The worker auto-detects the Rails app in the current directory and loads your environment.
107
+
108
+ #### From the Gem Directory (Development)
109
+
110
+ ```bash
111
+ ACTIVEJOB_TEMPORAL_TARGET=localhost:7233 \
112
+ ACTIVEJOB_TEMPORAL_NAMESPACE=default \
113
+ ACTIVEJOB_TEMPORAL_TASK_QUEUE=default \
114
+ bin/temporal-worker
115
+ ```
116
+
117
+ When run from the gem directory (not a Rails app), it works without Rails.
118
+
119
+ #### Specifying a Custom Path
120
+
121
+ If you need to run the worker from a different directory, use `RAILS_ROOT`:
122
+
123
+ ```bash
124
+ RAILS_ROOT=/path/to/app \
125
+ bundle exec temporal-worker
126
+ ```
127
+
128
+ The worker will load the Rails app at that path.
129
+
130
+ #### Performance Tuning
131
+
132
+ Control concurrency for different environments:
133
+
134
+ ```bash
135
+ # High-throughput production
136
+ ACTIVEJOB_TEMPORAL_TARGET=temporal.prod:7233 \
137
+ ACTIVEJOB_TEMPORAL_NAMESPACE=production \
138
+ ACTIVEJOB_TEMPORAL_TASK_QUEUE=jobs \
139
+ ACTIVEJOB_TEMPORAL_MAX_CONCURRENT_ACTIVITIES=500 \
140
+ ACTIVEJOB_TEMPORAL_MAX_CONCURRENT_WORKFLOW_TASKS=50 \
141
+ bundle exec temporal-worker
142
+
143
+ # Low-resource development
144
+ ACTIVEJOB_TEMPORAL_MAX_CONCURRENT_ACTIVITIES=20 \
145
+ ACTIVEJOB_TEMPORAL_MAX_CONCURRENT_WORKFLOW_TASKS=2 \
146
+ bundle exec temporal-worker
147
+ ```
148
+
149
+ See `docs/worker_setup.md` for detailed tuning guidance.
150
+
151
+ ### Build and Release
152
+
153
+ ```bash
154
+ # Build gem
155
+ gem build activejob-temporal.gemspec
156
+
157
+ # Full validation (Rubocop + tests)
158
+ bundle exec rake default
159
+ ```
160
+
161
+ ## Code Conventions
162
+
163
+ ### Structure and Style
164
+
165
+ - **Module composition** over inheritance; use modules for mixins
166
+ - **Explicit requires** at top of files
167
+ - **Frozen string literals** on every file: `# frozen_string_literal: true`
168
+ - **Proper namespacing**: `ActiveJob::Temporal::Workflows::AjWorkflow`
169
+ - **Visibility markers**: Use `private` or `private_class_method` explicitly
170
+
171
+ ### Naming
172
+
173
+ - **Classes**: PascalCase (`TemporalAdapter`, `AjWorkflow`)
174
+ - **Methods**: snake_case (`build_workflow_id`, `from_job`)
175
+ - **Constants**: SCREAMING_SNAKE_CASE (used sparingly)
176
+
177
+ ### Error Handling
178
+
179
+ The gem uses a custom exception hierarchy rooted at `ActiveJob::Temporal::Error`:
180
+ - `ConfigurationError`: Invalid or missing configuration
181
+ - `WorkflowNotFoundError`: Job not found in Temporal
182
+ - `TemporalConnectionError`: Network or server issues
183
+
184
+ **Pattern**: Always provide human-readable error messages with context. Wrap external Temporal errors into domain-specific exceptions for cleaner error handling in client code.
185
+
186
+ ### Configuration Pattern
187
+
188
+ Configuration uses a DSL pattern with **automatic validation**:
189
+
190
+ ```ruby
191
+ ActiveJob::Temporal.configure do |config|
192
+ config.target = "localhost:7233"
193
+ config.namespace = "default"
194
+ # Additional settings...
195
+ # Validation happens automatically at the end of this block!
196
+ end
197
+ ```
198
+
199
+ Configuration is automatically validated at the end of the `configure` block. By default validation failures raise `ConfigurationError` immediately. `config.validation_level` can downgrade failures to warnings with `:warn` or skip validation with `:none`. You can also manually validate by calling `ActiveJob::Temporal.config.validate!` if you modify the config directly outside the block.
200
+
201
+ Environment variables with defaults are used for sensitive values (e.g., `ENV.fetch("TEMPORAL_TARGET", default)`).
202
+
203
+ ## Testing Patterns
204
+
205
+ ### Test Organization
206
+
207
+ - **Unit tests** (`spec/unit/`): Isolated component tests with mocking/stubbing
208
+ - **Integration tests** (`spec/integration/`): End-to-end tests using real Temporal
209
+ - **Fixtures** (`spec/fixtures/sample_jobs.rb`): Sample job classes with various retry/discard configurations
210
+
211
+ ### SimpleCov and Coverage
212
+
213
+ Tests use SimpleCov with branch coverage enabled. Target coverage is ≥90%. Run tests to generate coverage reports.
214
+
215
+ ### Test Isolation
216
+
217
+ Use `around` hooks and shared contexts to reset state between tests. Integration tests manage Temporal workers within test lifecycle.
218
+
219
+ ### Common Patterns
220
+
221
+ - Mock Temporal client responses for unit tests
222
+ - Use real workers in integration tests
223
+ - Test both success and failure paths (retries, discards)
224
+ - Validate error messages and exception types
225
+
226
+ ## Workflow Implementation Notes
227
+
228
+ **Determinism**: Workflows must be deterministic—no I/O, randomness, or system time calls in the main workflow logic. Use activities for these operations.
229
+
230
+ **Durable Timers**: For scheduled jobs, workflows use Temporal's durable sleep mechanism. This persists across workflow replays and cluster restarts.
231
+
232
+ **RetryPolicy Mapping**: The `RetryMapper` introspects ActiveJob's `retry_on` and `discard_on` declarations and converts them to Temporal native retry policies. Understand this mapping when debugging job retries.
233
+
234
+ ## Git Commit Conventions
235
+
236
+ Use **Conventional Commits** format: `<type>(<scope>): <description>`
237
+
238
+ **Types**:
239
+ - `feat`: New features
240
+ - `fix`: Bug fixes
241
+ - `docs`: Documentation changes
242
+ - `chore`: Maintenance, configuration, tooling
243
+ - `test`: Test additions/updates
244
+ - `refactor`: Code structure improvements (avoid in this project—prefer feature/fix commits)
245
+
246
+ **Common scopes**:
247
+ - `config`: Configuration handling
248
+ - `payload`: Job serialization
249
+ - `cancel`: Cancellation logic
250
+ - `adapter`: ActiveJob adapter
251
+ - `workflows`: Workflow implementations
252
+ - `activities`: Activity implementations
253
+ - `docs`: Documentation
254
+ - `yard`: Inline documentation
255
+ - `tasks`: Task tracking/checklists
256
+ - `ci`: CI/CD pipeline
257
+ - `release`: Release processes
258
+
259
+ **Example**: `feat(config): add worker performance tuning options`
260
+
261
+ Keep commits small and focused. A feature might consist of several commits: implementation, tests, documentation.
262
+
263
+ ## Architecture Decision Records
264
+
265
+ See `docs/adr/` for recorded architectural decisions and rationale behind major design choices.
266
+
267
+ ## Additional Resources
268
+
269
+ - **Configuration Reference**: `docs/configuration_reference.md`
270
+ - **Comparison Guide**: `docs/comparison.md` (choosing the right ActiveJob backend)
271
+ - **Worker Setup**: `docs/worker_setup.md`
272
+ - **Release Process**: `docs/publishing.md`, `docs/release_checklist.md`
273
+ - **README**: Comprehensive user guide and quick start
274
+ - **Temporal Docs**: https://docs.temporal.io/ (for workflow/activity patterns, API reference)
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,69 @@
1
+ # Contributing
2
+
3
+ ## Ruby Baseline
4
+
5
+ Use Ruby 4.0.3 for local development and validation. The repository targets Ruby 4+ and does not require installing Ruby 3.
6
+
7
+ See [Ruby Baseline](docs/ruby_baseline.md) for the source-of-truth files, CI coverage, and external tooling notes.
8
+
9
+ Run validation commands through the Ruby 4 toolchain:
10
+
11
+ ```sh
12
+ rvm 4.0.3 do bundle install
13
+ rvm 4.0.3 do bundle exec rake spec:unit
14
+ rvm 4.0.3 do bundle exec rubocop
15
+ rvm 4.0.3 do bundle exec rake build
16
+ ```
17
+
18
+ ## Mutation Testing
19
+
20
+ Mutation testing runs a scoped Mutant baseline against deterministic unit-level code:
21
+
22
+ ```sh
23
+ rvm 4.0.3 do bundle exec rake mutation
24
+ ```
25
+
26
+ The default subject list is intentionally small so the task stays fast and does not require a local Temporal server. Expand `.mutant.yml` as specs are hardened around additional code paths.
27
+
28
+ Mutant 0.16 supports Ruby 4, but its parser dependency may warn about an older parser version. Treat new Ruby syntax parse failures as a Mutant tooling limitation and keep the repository runtime baseline on Ruby 4.
29
+
30
+ ## Dependency Updates
31
+
32
+ Dependabot checks root Bundler dependencies weekly and opens up to five update pull requests at a time.
33
+
34
+ Development dependency minor and patch updates are grouped into a single pull request. Review dependency pull requests like any other change: confirm CI passes, scan the changelog for breaking behavior, and keep major updates separate unless the dependency explicitly documents compatibility.
35
+
36
+ ## Code Quality Tracking
37
+
38
+ Qlty, the successor to Code Climate Quality, reads the committed `.qlty/qlty.toml` analysis configuration.
39
+
40
+ The current configuration focuses maintainability tracking on the library code and excludes generated or packaged artifacts. RuboCop findings are monitored in Qlty while the existing GitHub Actions lint job remains the blocking local validation path.
41
+
42
+ Do not add a README maintainability badge until the repository has been added to Qlty and the generated project badge URL is available.
43
+
44
+ ## Changelog
45
+
46
+ Generate release changelog updates from merged GitHub pull requests and closed issues:
47
+
48
+ ```sh
49
+ CHANGELOG_GITHUB_TOKEN=... rvm 4.0.3 do bundle exec rake changelog:generate
50
+ ```
51
+
52
+ The task also accepts `GITHUB_TOKEN` when `CHANGELOG_GITHUB_TOKEN` is not set. Review the generated `CHANGELOG.md` before committing release notes.
53
+
54
+ Generated unreleased sections are rebuilt from GitHub issues and pull requests. Existing released sections stay in place so curated release notes are not lost.
55
+
56
+ ## Release Commits
57
+
58
+ Release automation is not enabled yet.
59
+
60
+ Use conventional commit prefixes where they describe the change:
61
+
62
+ - `feat:` for user-visible features
63
+ - `fix:` for bug fixes
64
+ - `docs:` for documentation-only changes
65
+ - `test:` for test-only changes
66
+ - `ci:` for GitHub Actions or CI changes
67
+ - `chore:` for maintenance that does not affect runtime behavior
68
+
69
+ Do not rely on commit messages to publish a release until [Publishing](docs/publishing.md) says release automation is enabled.
data/README.md CHANGED
@@ -18,11 +18,10 @@ Use a traditional ActiveJob backend when the work is short, simple, and does not
18
18
  ## Requirements
19
19
 
20
20
  - Ruby >= 4.0
21
- - Rails 8.1 through ActiveJob 8.1
21
+ - Rails >= 7.2 through ActiveJob 8.x
22
22
  - Temporal cluster, either self-hosted or [Temporal Cloud](https://temporal.io/cloud)
23
23
 
24
- CI validates Ruby 4.0.0 and the latest Ruby 4.0 patch against Rails 8.1.
25
- Temporal Ruby SDK 1.4.x compatibility is contract-tested against 1.4.0 and the latest 1.4.x release.
24
+ Temporal Ruby SDK 1.4.x compatibility is contract-tested against 1.4.0 and 1.4.1.
26
25
 
27
26
  ## Install
28
27
 
@@ -104,34 +103,36 @@ bundle exec temporal-worker
104
103
 
105
104
  Open Temporal UI and look for workflows named `ajwf:SendInvoiceJob:<job_id>`.
106
105
 
106
+ `require "activejob/temporal"` loads the Rails adapter, enqueue path, schedules, lookup APIs, and shared configuration. Worker-only runtime code is loaded by `temporal-worker`; custom worker boot code should require `activejob/temporal/worker_runtime` before registering workflows or activities.
107
+
107
108
  ## Common Capabilities
108
109
 
109
110
  | Need | API | Detailed guide |
110
111
  | --- | --- | --- |
111
- | Delay a single job | `MyJob.set(wait: 5.minutes).perform_later(...)` | [Usage Patterns](https://github.com/schovi/activejob-temporal/blob/main/docs/usage_patterns.md#scheduled-jobs) |
112
- | Register recurring cron work | `schedule cron: "0 2 * * *"` and `create_temporal_schedule` | [Recurring Jobs](https://github.com/schovi/activejob-temporal/blob/main/docs/recurring_jobs.md) |
113
- | Enqueue only when work exists | `perform_later_if(condition, *args)` | [Usage Patterns](https://github.com/schovi/activejob-temporal/blob/main/docs/usage_patterns.md#conditional-enqueueing) |
114
- | Enqueue many prepared jobs | `ActiveJob::Temporal.enqueue_batch(jobs)` | [Usage Patterns](https://github.com/schovi/activejob-temporal/blob/main/docs/usage_patterns.md#bulk-enqueueing) |
115
- | Run sequential jobs in one workflow | `set(chain: [NextJob])` | [Usage Patterns](https://github.com/schovi/activejob-temporal/blob/main/docs/usage_patterns.md#job-chaining) |
116
- | Start child ActiveJob workflows | `set(child_workflows: [ChildJob])` | [Usage Patterns](https://github.com/schovi/activejob-temporal/blob/main/docs/usage_patterns.md#child-workflows) |
117
- | Call external Temporal activities or workflows | `ActiveJob::Temporal.activity(...)`, `ActiveJob::Temporal.workflow(...)` | [Usage Patterns](https://github.com/schovi/activejob-temporal/blob/main/docs/usage_patterns.md#external-temporal-steps) |
118
- | Wait for separately enqueued jobs | `set(depends_on: parent_job)` | [Usage Patterns](https://github.com/schovi/activejob-temporal/blob/main/docs/usage_patterns.md#job-dependencies) |
119
- | Map ActiveJob retries to Temporal | `retry_on`, `discard_on` | [Retry Policy Guide](https://github.com/schovi/activejob-temporal/blob/main/docs/retry_policies.md) |
120
- | Park exhausted failures | `config.dead_letter_queue = "failed_jobs"` | [Configuration Reference](https://github.com/schovi/activejob-temporal/blob/main/docs/configuration_reference.md#dead-letter-queue) |
121
- | Tune activity timeouts | `temporal_options start_to_close_timeout: ...` | [Usage Patterns](https://github.com/schovi/activejob-temporal/blob/main/docs/usage_patterns.md#per-job-timeouts) |
122
- | Add throughput limits | `rate_limit 100, per: :second` | [Configuration Reference](https://github.com/schovi/activejob-temporal/blob/main/docs/configuration_reference.md#rate-limit-configuration) |
123
- | Cancel or inspect jobs | `cancel`, `cancel_all`, `status`, `running?` | [Usage Patterns](https://github.com/schovi/activejob-temporal/blob/main/docs/usage_patterns.md#cancellation-and-status) |
124
- | Pause, resume, query, or update workflow state | `signal`, `query`, `update` | [Usage Patterns](https://github.com/schovi/activejob-temporal/blob/main/docs/usage_patterns.md#signals-queries-and-updates) |
125
- | Add runtime middleware | `config.add_middleware MiddlewareClass` | [Middleware](https://github.com/schovi/activejob-temporal/blob/main/docs/middleware.md) |
126
- | Expose Prometheus metrics | `config.observability.use :prometheus` | [Metrics Guide](https://github.com/schovi/activejob-temporal/blob/main/docs/metrics.md) |
127
- | Encrypt job payloads | `encrypt_payload = true` | [Configuration Reference](https://github.com/schovi/activejob-temporal/blob/main/docs/configuration_reference.md#payload-encryption) |
128
- | Store large payloads externally | `payload_storage_adapter = MyPayloadStorage.new` | [Configuration Reference](https://github.com/schovi/activejob-temporal/blob/main/docs/configuration_reference.md#payload-size-limits) |
112
+ | Delay a single job | `MyJob.set(wait: 5.minutes).perform_later(...)` | [Usage Patterns](docs/usage_patterns.md#scheduled-jobs) |
113
+ | Register recurring cron work | `temporal_schedule cron: "0 2 * * *"` and `create_temporal_schedule` | [Recurring Jobs](docs/recurring_jobs.md) |
114
+ | Enqueue only when work exists | `perform_later_if(condition, *args)` | [Usage Patterns](docs/usage_patterns.md#conditional-enqueueing) |
115
+ | Enqueue many prepared jobs | `ActiveJob::Temporal.enqueue_batch(jobs)` | [Usage Patterns](docs/usage_patterns.md#bulk-enqueueing) |
116
+ | Run sequential jobs in one workflow | `set(chain: [NextJob])` | [Usage Patterns](docs/usage_patterns.md#job-chaining) |
117
+ | Start child ActiveJob workflows | `set(child_workflows: [ChildJob])` | [Usage Patterns](docs/usage_patterns.md#child-workflows) |
118
+ | Call external Temporal activities or workflows | `ActiveJob::Temporal.activity(...)`, `ActiveJob::Temporal.workflow(...)` | [Usage Patterns](docs/usage_patterns.md#external-temporal-steps) |
119
+ | Wait for separately enqueued jobs | `set(depends_on: parent_job)` | [Usage Patterns](docs/usage_patterns.md#job-dependencies) |
120
+ | Map ActiveJob retries to Temporal | `retry_on`, `discard_on` | [Retry Policy Guide](docs/retry_policies.md) |
121
+ | Park exhausted failures | `config.dead_letter_queue = "failed_jobs"` | [Configuration Reference](docs/configuration_reference.md#dead-letter-queue) |
122
+ | Tune activity timeouts | `temporal_options start_to_close_timeout: ...` | [Usage Patterns](docs/usage_patterns.md#per-job-timeouts) |
123
+ | Add throughput limits | `rate_limit 100, per: :second` | [Configuration Reference](docs/configuration_reference.md#rate-limit-configuration) |
124
+ | Cancel or inspect jobs | `cancel`, `cancel_all`, `status`, `running?` | [Usage Patterns](docs/usage_patterns.md#cancellation-and-status) |
125
+ | Pause, resume, query, or update workflow state | `signal`, `query`, `update` | [Usage Patterns](docs/usage_patterns.md#signals-queries-and-updates) |
126
+ | Add runtime middleware | `config.add_middleware MiddlewareClass` | [Middleware](docs/middleware.md) |
127
+ | Expose Prometheus metrics | `config.observability.use :prometheus` | [Metrics Guide](docs/metrics.md) |
128
+ | Encrypt job payloads | `encrypt_payload = true` | [Configuration Reference](docs/configuration_reference.md#payload-encryption) |
129
+ | Store large payloads externally | `payload_storage_adapter = MyPayloadStorage.new` | [Configuration Reference](docs/configuration_reference.md#payload-size-limits) |
129
130
 
130
131
  Baseline behavior also includes transaction-aware enqueueing through ActiveJob, GlobalID-compatible argument serialization, structured JSON logs, searchable `set(tags:)` metadata, and JSON payloads with opt-in MessagePack or Marshal envelopes.
131
132
 
132
133
  ## Configuration
133
134
 
134
- The full configuration surface lives in [Configuration Reference](https://github.com/schovi/activejob-temporal/blob/main/docs/configuration_reference.md). The machine-readable schema is [docs/config_schema.yaml](https://github.com/schovi/activejob-temporal/blob/main/docs/config_schema.yaml).
135
+ The full configuration surface lives in [Configuration Reference](docs/configuration_reference.md). The machine-readable schema is [docs/config_schema.yaml](docs/config_schema.yaml).
135
136
 
136
137
  The most common settings are:
137
138
 
@@ -148,23 +149,23 @@ ActiveJob::Temporal.configure do |config|
148
149
  end
149
150
  ```
150
151
 
151
- Workers can also read environment variables such as `ACTIVEJOB_TEMPORAL_TARGET`, `ACTIVEJOB_TEMPORAL_NAMESPACE`, `ACTIVEJOB_TEMPORAL_TASK_QUEUE`, `ACTIVEJOB_TEMPORAL_MAX_CONCURRENT_ACTIVITIES`, `ACTIVEJOB_TEMPORAL_METRICS_PORT`, and TLS certificate settings. See [Worker Setup](https://github.com/schovi/activejob-temporal/blob/main/docs/worker_setup.md) for the worker-focused list.
152
+ Workers can also read environment variables such as `ACTIVEJOB_TEMPORAL_TARGET`, `ACTIVEJOB_TEMPORAL_NAMESPACE`, `ACTIVEJOB_TEMPORAL_TASK_QUEUE`, `ACTIVEJOB_TEMPORAL_MAX_CONCURRENT_ACTIVITIES`, `ACTIVEJOB_TEMPORAL_METRICS_PORT`, and TLS certificate settings. See [Worker Setup](docs/worker_setup.md) for the worker-focused list.
152
153
 
153
154
  ## Documentation
154
155
 
155
- Start with [docs/README.md](https://github.com/schovi/activejob-temporal/blob/main/docs/README.md) for the complete documentation map.
156
+ Start with [docs/README.md](docs/README.md) for the complete documentation map.
156
157
 
157
158
  High-use guides:
158
159
 
159
- - [Usage Patterns](https://github.com/schovi/activejob-temporal/blob/main/docs/usage_patterns.md)
160
- - [Configuration Reference](https://github.com/schovi/activejob-temporal/blob/main/docs/configuration_reference.md)
161
- - [Worker Setup](https://github.com/schovi/activejob-temporal/blob/main/docs/worker_setup.md)
162
- - [Troubleshooting](https://github.com/schovi/activejob-temporal/blob/main/docs/troubleshooting.md)
163
- - [Performance Tuning](https://github.com/schovi/activejob-temporal/blob/main/docs/performance_tuning.md)
164
- - [Comparison Guide](https://github.com/schovi/activejob-temporal/blob/main/docs/comparison.md)
165
- - [Security](https://github.com/schovi/activejob-temporal/blob/main/docs/security.md)
160
+ - [Usage Patterns](docs/usage_patterns.md)
161
+ - [Configuration Reference](docs/configuration_reference.md)
162
+ - [Worker Setup](docs/worker_setup.md)
163
+ - [Troubleshooting](docs/troubleshooting.md)
164
+ - [Performance Tuning](docs/performance_tuning.md)
165
+ - [Comparison Guide](docs/comparison.md)
166
+ - [Security](docs/security.md)
166
167
 
167
- See [examples/basic_rails_app](https://github.com/schovi/activejob-temporal/tree/main/examples/basic_rails_app) for a Docker Compose Rails app with Temporal, Temporal UI, search attribute setup, workers, seeded GlobalID records, and tests.
168
+ See [examples/basic_rails_app](examples/basic_rails_app/) for a Docker Compose Rails app with Temporal, Temporal UI, search attribute setup, workers, seeded GlobalID records, and tests.
168
169
 
169
170
  ## Contributing
170
171
 
@@ -195,4 +196,4 @@ MIT. See [LICENSE](LICENSE).
195
196
 
196
197
  This project follows [Semantic Versioning](https://semver.org/). See [CHANGELOG](CHANGELOG.md) for release history.
197
198
 
198
- Current development version: 0.1.0. Release commits must be tagged before publishing to RubyGems.
199
+ Current version: 0.1.0
@@ -19,16 +19,13 @@ Gem::Specification.new do |spec|
19
19
  spec.required_ruby_version = ">= 4.0"
20
20
 
21
21
  spec.metadata["homepage_uri"] = spec.homepage
22
- spec.metadata["source_code_uri"] = "#{spec.homepage}/tree/main"
23
- spec.metadata["documentation_uri"] = "#{spec.homepage}/blob/main/docs/README.md"
22
+ spec.metadata["source_code_uri"] = spec.homepage
24
23
  spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md"
25
24
  spec.metadata["rubygems_mfa_required"] = "true"
26
25
 
27
26
  spec.files = Dir.chdir(__dir__) do
28
27
  `git ls-files -z`.split("\x0").reject do |f|
29
- f.start_with?("spec/", "docs/", "examples/", "tmp/", "tools/", "gemfiles/", ".codemachine/", ".github/") ||
30
- f == "CLAUDE.md" ||
31
- f == "CONTRIBUTING.md" ||
28
+ f.start_with?("spec/", "docs/", "examples/", "tmp/", "tools/", ".codemachine/", ".github/") ||
32
29
  f.match?(%r{^(\.|docker-compose\.yml|coverage/|Gemfile|Rakefile)})
33
30
  end
34
31
  end
@@ -36,23 +33,22 @@ Gem::Specification.new do |spec|
36
33
  spec.executables = ["temporal-worker"]
37
34
  spec.require_paths = ["lib"]
38
35
 
39
- spec.add_dependency "activejob", ">= 8.1", "< 9"
40
- spec.add_dependency "activemodel", ">= 8.1", "< 9"
36
+ spec.add_dependency "activejob", ">= 7.2", "< 9"
37
+ spec.add_dependency "activemodel", ">= 7.2", "< 9"
41
38
  spec.add_dependency "concurrent-ruby", "~> 1.1"
42
39
  spec.add_dependency "globalid", ">= 0.3"
43
- spec.add_dependency "listen", "~> 3.9"
44
- spec.add_dependency "temporalio", ">= 1.4.0", "< 1.5"
40
+ spec.add_dependency "temporalio", ">= 1.4.0", "< 1.7"
45
41
 
46
42
  spec.add_development_dependency "benchmark-ips", "~> 2.14"
47
- spec.add_development_dependency "bundler-audit", "~> 0.9"
48
43
  spec.add_development_dependency "github_changelog_generator", "~> 1.18"
44
+ spec.add_development_dependency "listen", "~> 3.9"
45
+ spec.add_development_dependency "minitest", ">= 5.11", "< 7"
49
46
  spec.add_development_dependency "msgpack", "~> 1.8"
50
- spec.add_development_dependency "mutant-rspec", "~> 0.16"
47
+ spec.add_development_dependency "mutant-minitest", "~> 0.16"
51
48
  spec.add_development_dependency "prometheus-client", "~> 4.2"
52
49
  spec.add_development_dependency "rake", "~> 13.2"
53
- spec.add_development_dependency "rspec", "~> 3.12"
54
50
  spec.add_development_dependency "rubocop", "~> 1.50"
55
51
  spec.add_development_dependency "simplecov", "~> 0.22"
56
52
  spec.add_development_dependency "simplecov-lcov", "~> 0.9"
57
- spec.add_development_dependency "yard", "~> 0.9", ">= 0.9.42"
53
+ spec.add_development_dependency "yard", "~> 0.9"
58
54
  end