jobcompat 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,268 @@
1
+ # jobcompat competitive analysis
2
+
3
+ - Research date: 2026-09-23 (Asia/Tokyo)
4
+ - Product hypothesis: **Breaking-change detector for queued background jobs**
5
+ - Initial scope: Ruby source, native Sidekiq jobs, Git revision comparison
6
+ - Repository state at research start: the project directory was not a Git repository and contained no product files; only local tooling state existed.
7
+
8
+ ## Executive conclusion
9
+
10
+ No mature direct competitor was found that statically derives native Sidekiq producer and consumer contracts from two Git revisions and checks both rolling-deployment directions. This is a bounded research conclusion, not proof that no such project exists.
11
+
12
+ The problem itself is real and documented by primary sources. Sidekiq stores the job class and an `args` array, then splats that array into `perform`; GitLab explicitly documents all three mixed-version situations that jobcompat proposes to check. The gap is therefore not problem discovery, but automated, revision-aware enforcement for ordinary native Sidekiq code.
13
+
14
+ Recommendation: **go**, with a deliberately narrow v0.1. The differentiator should be phrased as:
15
+
16
+ > A Git-aware static compatibility check for native Sidekiq job arity across rolling deployments.
17
+
18
+ Do not claim to prove runtime safety, queue emptiness, payload types, Hash schemas, feature-flag behavior, or arbitrary Ruby metaprogramming.
19
+
20
+ ## Methodology
21
+
22
+ ### Sources and evidence policy
23
+
24
+ Research prioritized, in order:
25
+
26
+ 1. upstream framework repositories and official documentation;
27
+ 2. GitHub repositories and their public metadata API;
28
+ 3. package registries (RubyGems, npm, PyPI);
29
+ 4. GitLab-hosted OSS results;
30
+ 5. general web search for discovery only.
31
+
32
+ Material claims below link to primary or upstream sources. GitHub stars and `pushed_at` values were read from the public GitHub repository API on 2026-09-23. Stars are a volatile popularity signal, not a quality score. A package endpoint returning HTTP 404 is evidence only that the exact package was not found at that time; it is not a reservation or trademark clearance.
33
+
34
+ ### Query families searched
35
+
36
+ The following requested terms were searched individually or in grouped exact/OR queries against GitHub and general web search:
37
+
38
+ ```text
39
+ sidekiq compatibility checker
40
+ sidekiq breaking change checker
41
+ background job compatibility checker
42
+ background job contract checker
43
+ job payload compatibility
44
+ queue payload compatibility
45
+ queued job compatibility
46
+ worker signature compatibility
47
+ sidekiq worker compatibility
48
+ sidekiq argument compatibility
49
+ sidekiq deployment compatibility
50
+ async contract checker
51
+ job contract linter
52
+
53
+ sidekiq typed arguments
54
+ typed sidekiq jobs
55
+ sidekiq sorbet
56
+ sidekiq argument validation
57
+ sidekiq schema
58
+ sidekiq linter
59
+ rubocop sidekiq
60
+ rspec sidekiq
61
+ sidekiq static analysis
62
+ BullMQ schema
63
+ BullMQ Zod
64
+ Celery argument validation
65
+ Celery task signature
66
+ queue schema registry
67
+ ```
68
+
69
+ Additional searches covered Prism AST node documentation, Sidekiq Job Format and scheduling APIs, GitLab Sidekiq update compatibility, AsyncAPI diffing, Kafka-compatible schema registries, Ruby maintenance status, and the proposed positioning phrase.
70
+
71
+ GitLab-specific searches for Sidekiq compatibility checkers, background-job contracts, queue-payload compatibility, and Sidekiq argument linters returned GitLab's own documentation and issues, but no standalone direct checker.
72
+
73
+ RubyGems-, npm-, and PyPI-oriented keyword searches were also run for the relevant ecosystems (Sidekiq/Ruby, BullMQ/Node, and Celery/Python). They surfaced the adjacent projects summarized below, but no package whose documented feature set combined native Sidekiq source extraction, Git revision comparison, and both mixed-version deployment directions. Registry search is not exhaustive, so this remains a bounded result.
74
+
75
+ ### Registry and name checks
76
+
77
+ Exact public endpoints checked on 2026-09-23:
78
+
79
+ | Registry | Exact endpoint | Result |
80
+ | --- | --- | --- |
81
+ | RubyGems | `https://rubygems.org/api/v1/gems/jobcompat.json` | HTTP 404 |
82
+ | npm | `https://registry.npmjs.org/jobcompat` | HTTP 404 |
83
+ | PyPI | `https://pypi.org/pypi/jobcompat/json` | HTTP 404 |
84
+ | GitHub repository search | `jobcompat in:name` | 3 fuzzy-name results; no exact `jobcompat` repository name in the returned set |
85
+
86
+ The GitHub fuzzy results were unrelated job-matching projects: `RubyWoodsDev/JobCompatablity`, `Candace352/JobCompatibility`, and `Candace352/JobCompatibilityChecker`.
87
+
88
+ ## Primary-source validation of the problem
89
+
90
+ ### Sidekiq's persisted contract
91
+
92
+ Sidekiq's [Job Format](https://github.com/sidekiq/sidekiq/wiki/Job-Format) documents a payload containing a class name and an `args` array, and states that `args` is splatted into the job class's `perform` method. Its [Best Practices](https://github.com/sidekiq/sidekiq/wiki/Best-Practices) explains that `perform_async` arguments are JSON-persisted in Redis. The [Basics](https://github.com/sidekiq/sidekiq/wiki/The-Basics) distinguishes the client that enqueues the serialized job from the server that later instantiates the class and calls `perform`.
93
+
94
+ This establishes the core contract used by jobcompat v0.1:
95
+
96
+ ```text
97
+ serialized class name + positional args array
98
+ -> worker instance #perform(*args)
99
+ ```
100
+
101
+ Sidekiq's [Scheduled Jobs](https://github.com/sidekiq/sidekiq/wiki/Scheduled-Jobs) confirms that `perform_in(interval, *args)` and `perform_at(timestamp, *args)` reserve the first argument for scheduling. [Advanced Options](https://github.com/sidekiq/sidekiq/wiki/Advanced-Options) confirms `Job.set(queue: ...).perform_async(...)`.
102
+
103
+ Sidekiq's strict argument checking, introduced in 6.4 and made strict in 7, checks whether argument values are JSON-safe. It does not compare worker arity or Git revisions; see the upstream [Changes](https://github.com/sidekiq/sidekiq/blob/main/Changes.md).
104
+
105
+ ### GitLab's rolling-deployment model
106
+
107
+ GitLab's [Sidekiq Compatibility across Updates](https://docs.gitlab.com/development/sidekiq/compatibility_across_updates/) names the same three situations:
108
+
109
+ 1. an old application version publishes a job executed by an upgraded Sidekiq node;
110
+ 2. a job queued before an upgrade executes after the upgrade;
111
+ 3. a new application node publishes a job executed by an old Sidekiq node.
112
+
113
+ It recommends a multi-release sequence: first add an optional worker argument, later start producing it, and only later make it required. It also treats adding, removing, and renaming worker classes as rollout concerns. This is strong evidence for the proposed A/B/C compatibility model and for JC002/JC004/JC005.
114
+
115
+ GitLab is guidance, not a reusable checker. Its value to jobcompat is validation of deployment semantics and remediation wording.
116
+
117
+ ## Competitor and adjacent-tool matrix
118
+
119
+ Legend: “revision comparison” means comparison of two versions of the relevant contract, not merely version-control integration. “Old → new” and “new → old” refer to producer/consumer compatibility.
120
+
121
+ | Name | URL | Category | Framework | Core feature | Static analysis | Revision comparison | Old producer → new consumer | New producer → old consumer | Rolling aware | CI-oriented | Activity at 2026-09-23 | Stars | License | Difference from jobcompat |
122
+ | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | ---: | --- | --- |
123
+ | GitLab Sidekiq compatibility guidance | [docs](https://docs.gitlab.com/development/sidekiq/compatibility_across_updates/) | adjacent | Sidekiq | Operational rules and staged migrations across updates | no | no | described manually | described manually | yes | no standalone gate | current docs accessible | n/a | GitLab documentation terms | Strongest validation of the problem, but provides no reusable AST/Git checker |
124
+ | Sidekiq strict arguments | [upstream](https://github.com/sidekiq/sidekiq), [changes](https://github.com/sidekiq/sidekiq/blob/main/Changes.md) | adjacent | Sidekiq | Reject JSON-unsafe enqueue arguments | no; runtime enqueue validation | no | no | no | no | usable in tests/runtime, not a revision gate | pushed 2026-09-23 | 13,561 | LGPL-3.0 | Validates serialization safety, not class/arity evolution |
125
+ | Sidekiq::Sorbet | [GitHub](https://github.com/akodkod/sidekiq-sorbet) | adjacent | Sidekiq + Sorbet DSL | Typed `T::Struct` arguments, enqueue-time validation, custom `run_*` API | type generation plus runtime validation | no | no | no | no | potentially | pushed 2026-04-27 | 1 | MIT | Requires a different DSL and does not compare deployed revisions or native `perform_async` callsites |
126
+ | RuboCop | [GitHub](https://github.com/rubocop/rubocop) | adjacent | Ruby | General single-tree static analysis and linting | yes | no | no | no | no | yes | pushed 2026-09-22 | 12,904 | MIT | Extensible analysis platform, but no discovered Sidekiq rolling-contract rule; jobcompat is cross-revision and domain-specific |
127
+ | AsyncAPI Diff | [GitHub](https://github.com/asyncapi/diff), [CLI](https://github.com/asyncapi/cli/blob/master/docs/usage.md) | adjacent | AsyncAPI | Diff declared async API documents and classify breaking changes | yes, on specifications | yes, document-to-document | schema-dependent | schema-dependent | not deployment-topology aware | yes | pushed 2026-08-06 | 28 | Apache-2.0 | Requires an explicit AsyncAPI document; does not infer native Sidekiq contracts or mixed-version fleet directions |
128
+ | BullMQ-Zod | [GitHub](https://github.com/AprilNEA/bullmq-zod) | adjacent | BullMQ | Zod-backed compile-time/runtime payload validation | no revision static analysis | no | no | no | no | potentially | pushed 2025-01-21 | 2 | MIT | Schema wrapper for another framework, not a Git diff or rolling-deploy checker |
129
+ | Celery argument checking | [official tasks docs](https://docs.celeryq.dev/en/latest/userguide/tasks.html), [GitHub](https://github.com/celery/celery) | adjacent | Celery | Call-time task signature argument checking; optional typing/validation | runtime/call-time | no | no | no | no | potentially | pushed 2026-09-23 | 28,913 | BSD-3-Clause | Confirms adjacent demand, but does not compare revisions or Sidekiq source |
130
+ | Karapace Schema Registry | [GitHub](https://github.com/Aiven-Open/karapace) | adjacent | Kafka/schema registry | Store schemas and enforce configured schema compatibility | schema analysis | yes, schema versions | yes for configured schema mode | yes for configured schema mode | protocol compatibility, not app rollout topology | yes | pushed 2026-09-21 | 636 | Apache-2.0 | Mature schema-registry pattern, but requires declared schemas and targets event streams rather than native Sidekiq method calls |
131
+
132
+ ### Excluded from the matrix
133
+
134
+ The following were reviewed but excluded because they do not materially address contract compatibility:
135
+
136
+ - Sidekiq queue UIs, retry tooling, schedulers, uniqueness gems, and job-iteration libraries;
137
+ - generic background-job processors;
138
+ - gems that only serialize or encrypt arguments;
139
+ - RSpec Sidekiq helpers that assert enqueue behavior in one revision;
140
+ - unrelated repositories whose names happen to include “job compatibility.”
141
+
142
+ This avoids manufacturing a large competitor set from unrelated projects.
143
+
144
+ ## Direct competitor assessment
145
+
146
+ ### Direct competitor found?
147
+
148
+ **No direct competitor was found in the searched sources.** Specifically, no discovered project combined all of:
149
+
150
+ - native Sidekiq worker and producer discovery from ordinary Ruby source;
151
+ - base/head Git snapshot comparison;
152
+ - old producer → new consumer analysis;
153
+ - new producer → old consumer analysis;
154
+ - current head producer → head consumer analysis;
155
+ - CI-oriented deterministic findings.
156
+
157
+ This statement is limited by public indexing, query quality, private/internal tools, abandoned unindexed projects, and future releases.
158
+
159
+ ### Mature OSS solving substantially the same problem?
160
+
161
+ **None found.** The mature adjacent solutions solve different layers:
162
+
163
+ - Sidekiq checks JSON serialization at enqueue time;
164
+ - GitLab documents safe deployment practice;
165
+ - typed wrappers change the programming model and validate one version;
166
+ - AsyncAPI/schema registries compare explicit schemas rather than infer application contracts;
167
+ - general linters analyze one source tree.
168
+
169
+ ### Strongest adjacent competitors
170
+
171
+ 1. **GitLab's compatibility guidance** is the strongest conceptual substitute. Teams can enforce it through review discipline without installing a tool.
172
+ 2. **Sidekiq::Sorbet and other typed wrappers** can prevent some producer mistakes, but adoption requires code changes and still does not model old/new fleets.
173
+ 3. **AsyncAPI Diff/schema registries** demonstrate a mature schema-compatibility category. They become stronger substitutes if a team already declares job payload schemas outside Ruby code.
174
+
175
+ ## Differentiation
176
+
177
+ The defensible v0.1 differentiation is the intersection of four choices:
178
+
179
+ 1. **Revision-aware:** compare immutable Git snapshots, not only the current tree.
180
+ 2. **Deployment-aware:** model both directions of a mixed-version rolling deploy.
181
+ 3. **Source-derived:** work with existing native Sidekiq classes and enqueue calls, without requiring schema adoption or application boot.
182
+ 4. **Conservative evidence:** errors require a concrete structural incompatibility; unsupported/dynamic evidence remains a warning or tool error, never a pass.
183
+
184
+ The most distinctive rule is JC002: a new producer arity accepted by the head worker but rejected by the base worker. Single-tree linters and ordinary tests commonly miss this rollout window.
185
+
186
+ ## Market and product risks
187
+
188
+ | Risk | Consequence | v0.1 response |
189
+ | --- | --- | --- |
190
+ | Ruby metaprogramming hides workers/calls | False negatives or false removal errors | Narrow supported syntax; a tracked class still present but unrecognized blocks JC004 and yields JC007 |
191
+ | Repository callsites do not prove queued data | False confidence or noisy errors | JC001 requires a known base callsite; JC006 warns on unproven contract narrowing and explicitly avoids inferring an empty queue |
192
+ | Feature flags make an unsafe-looking call operationally safe | JC005/JC002 false positive | Exact rule+worker suppression with mandatory reason |
193
+ | Tests/examples look like production producers | False errors | Default-exclude `test/**`, `spec/**`, `features/**`, and `examples/**` |
194
+ | Dynamic receivers are common | JC007 noise | Warning only; never infer a worker; path exclusion is available |
195
+ | Native Sidekiq users may use `Sidekiq::Client.push` or bulk APIs | False negatives | Explicit v0.1 non-goal and roadmap item |
196
+ | Full-repository parsing may be slow | Poor CI adoption | Parse selected blobs once; use a bounded, lazy presence-only pass over excluded tracked Ruby only for potential class-absence errors, returning JC007 if proof cannot finish |
197
+ | “Compatibility” could be mistaken for value/type compatibility | Overclaiming | Name v0.1 capability “positional arity compatibility” in README and output |
198
+ | Package name may be claimed before release | Rename cost | Reserve GitHub/RubyGems shortly before implementation/release; do not state availability as guaranteed |
199
+
200
+ ## Package and project name due diligence
201
+
202
+ `jobcompat` is concise, easy to type, and communicates compatibility better than a Sidekiq-specific name. It leaves room for future adapters without promising them in v0.1.
203
+
204
+ At the research date, exact RubyGems, npm, and PyPI API lookups returned 404, and GitHub's name search returned no exact repository named `jobcompat`. This supports continuing with the name, but does **not** establish legal clearance, future availability, organization-name availability, domain availability, or trademark safety.
205
+
206
+ The proposed phrase:
207
+
208
+ > Your API has a schema. Your database has migrations. What protects your queued jobs?
209
+
210
+ returned no exact matching product phrase in the sampled web searches. This is not a trademark search. Use it as draft positioning and perform a final general/trademark review before a public launch campaign.
211
+
212
+ Contingency names, not separately cleared:
213
+
214
+ - `queuecompat`
215
+ - `jobcontract`
216
+ - `queuediff`
217
+ - `asynccompat`
218
+ - `jobguard`
219
+
220
+ ## Final differentiation assessment
221
+
222
+ - **Direct competitor:** none found in the bounded search.
223
+ - **Mature equivalent OSS:** none found.
224
+ - **Market gap:** credible and specifically supported by Sidekiq payload semantics and GitLab deployment guidance.
225
+ - **Differentiation:** clear if jobcompat remains the Git-aware, mixed-version, native-Sidekiq arity checker.
226
+ - **Go / reconsider:** **go for v0.1**. Reconsider only if implementation discovery shows that ordinary producer receivers cannot be resolved with acceptable precision, or if a newly found tool already performs the same three-direction Git analysis.
227
+
228
+ ## Source index
229
+
230
+ ### Required primary sources
231
+
232
+ - [Sidekiq repository](https://github.com/sidekiq/sidekiq)
233
+ - [Sidekiq Job Format](https://github.com/sidekiq/sidekiq/wiki/Job-Format)
234
+ - [Sidekiq Best Practices](https://github.com/sidekiq/sidekiq/wiki/Best-Practices)
235
+ - [Sidekiq Basics](https://github.com/sidekiq/sidekiq/wiki/The-Basics)
236
+ - [Sidekiq Scheduled Jobs](https://github.com/sidekiq/sidekiq/wiki/Scheduled-Jobs)
237
+ - [Sidekiq Advanced Options](https://github.com/sidekiq/sidekiq/wiki/Advanced-Options)
238
+ - [Sidekiq changes, including strict args](https://github.com/sidekiq/sidekiq/blob/main/Changes.md)
239
+ - [Sidekiq related projects](https://github.com/sidekiq/sidekiq/wiki/Related-Projects)
240
+ - [GitLab Sidekiq Compatibility across Updates](https://docs.gitlab.com/development/sidekiq/compatibility_across_updates/)
241
+ - [Prism official documentation](https://ruby.github.io/prism/)
242
+ - [Prism Ruby API](https://ruby.github.io/prism/rb/docs/ruby_api_md.html)
243
+ - [Prism node schema](https://github.com/ruby/prism/blob/main/config.yml)
244
+
245
+ ### Version and dependency sources
246
+
247
+ - [Ruby maintenance branches](https://www.ruby-lang.org/en/downloads/branches/)
248
+ - [Sidekiq gemspec](https://github.com/sidekiq/sidekiq/blob/main/sidekiq.gemspec)
249
+ - [Prism on RubyGems](https://rubygems.org/gems/prism)
250
+ - [`parser` README recommending native Prism for Ruby 3.3+](https://github.com/whitequark/parser/blob/master/README.md)
251
+ - [`parser` on RubyGems](https://rubygems.org/gems/parser/)
252
+
253
+ ### Adjacent projects
254
+
255
+ - [Sidekiq::Sorbet](https://github.com/akodkod/sidekiq-sorbet)
256
+ - [RuboCop](https://github.com/rubocop/rubocop)
257
+ - [AsyncAPI Diff](https://github.com/asyncapi/diff)
258
+ - [AsyncAPI CLI diff command](https://github.com/asyncapi/cli/blob/master/docs/usage.md)
259
+ - [BullMQ Job Data](https://docs.bullmq.io/guide/jobs/job-data)
260
+ - [BullMQ-Zod](https://github.com/AprilNEA/bullmq-zod)
261
+ - [Celery Tasks](https://docs.celeryq.dev/en/latest/userguide/tasks.html)
262
+ - [Karapace](https://github.com/Aiven-Open/karapace)
263
+
264
+ ### Git snapshot design sources
265
+
266
+ - [`git rev-parse`](https://git-scm.com/docs/git-rev-parse)
267
+ - [`git ls-tree`](https://git-scm.com/docs/git-ls-tree)
268
+ - [`git cat-file`](https://git-scm.com/docs/git-cat-file)