@@ -103,6 +103,96 @@ bundle exec rake rbs
103103
104104This follows the inline convention used by ` cardmagic/classifier ` .
105105
106+ ### Actor-specific dispatch signatures
107+
108+ Typed applications can opt into ` SolidObjects::ActorSignatures ` to check ordinary
109+ ` schedule ` , ` transmit ` , and effect callback names inside actor methods. Add ` rbs `
110+ and ` steep ` to the application's development dependencies. This tool is loaded
111+ explicitly and is not required by workers or ordinary Ruby applications.
112+
113+ Declare application operation types first, including inherited operations and
114+ block-defined ` message ` operations. Inline RBS can generate this input, or keep
115+ handwritten declarations in a separate directory such as ` sig/actors ` :
116+
117+ ``` rbs
118+ class ChatRun < SolidObjects::Actor
119+ def recover_if_stuck: (generation: Integer) -> nil
120+ def fail_turn: (effect_id: String, arguments: Hash[String, untyped], error: Hash[String, untyped]) -> nil
121+ def start: () -> nil
122+ end
123+ ```
124+
125+ After loading the application's actor classes, generate a separate output file:
126+
127+ ``` ruby
128+ require " solid_objects/actor_signatures"
129+
130+ Rails .application.reloader.wrap do
131+ signatures = SolidObjects ::ActorSignatures .generate(
132+ actors: [ChatRun ],
133+ signatures: [Rails .root.join(" sig/actors" ).to_s]
134+ )
135+ FileUtils .mkdir_p(Rails .root.join(" sig/generated" ))
136+ File .write(Rails .root.join(" sig/generated/solid_objects.rbs" ), signatures)
137+ end
138+ ```
139+
140+ Run that script with ` bin/rails runner ` during development or CI. Outside Rails,
141+ require the actor definitions and call ` generate ` directly. The generator returns
142+ a string and does not write files, execute actor operations, start workers, or run
143+ migrations. Rails boot follows the application's normal loading configuration;
144+ the explicit actor list resolves its autoloaded classes within the reloader boundary.
145+ Keep generated output out of the input signature paths, and regenerate after a
146+ message is renamed or removed. Output is deterministic; handwritten signatures
147+ remain separate.
148+
149+ Load the gem and both application signature directories in ` Steepfile ` :
150+
151+ ``` ruby
152+ target :actors do
153+ library " solid_objects"
154+ signature " sig/actors"
155+ signature " sig/generated"
156+ check " app/actors"
157+ configure_code_diagnostics(Diagnostic ::Ruby .strict)
158+ end
159+ ```
160+
161+ The usual Ruby code now passes Steep without a dispatcher cast:
162+
163+ ``` ruby
164+ schedule(at: Time .now, key: " watchdog" ).recover_if_stuck(generation: 1 )
165+ transmit.recover_if_stuck(generation: 1 )
166+ emit :run_model , generation: 1 , on_failure: :fail_turn
167+ ```
168+
169+ Misspelled operations/callbacks, queries, attributes, private methods, infrastructure
170+ methods, and incorrect keyword arguments fail the strict check. Both string and
171+ symbol callback literals work. Staging returns ` nil ` even when an operation's own
172+ return type differs. Effect and commit-action names remain global registry names;
173+ registry contract inference is separate work.
174+
175+ Reflection supplies message names only. Values come from declared RBS signatures;
176+ missing signatures and positional/block arguments are rejected. Block-defined
177+ messages require explicit method declarations in RBS; annotate their block-local
178+ values separately when checking the block body. Generic actor classes currently
179+ require application-owned dispatcher signatures. The generator preserves method
180+ overloads and method type parameters.
181+
182+ Deliberately dynamic names can use Ruby's explicit dynamic dispatch:
183+
184+ ``` ruby
185+ schedule(at: Time .now).public_send(operation_name, generation: generation)
186+ public_send(:emit , :run_model , on_failure: callback_name, generation: generation)
187+ ```
188+
189+ Those calls opt out of name/argument checking and retain the existing runtime
190+ validation. Ordinary calls on the generated dispatcher have no string-name fallback.
191+ ` send_to ` , ` Reference#async ` , direct calls, and queries retain their existing
192+ signatures and runtime behavior; this generator does not provide complete reference
193+ typing or Sorbet/Tapioca actor-specific RBI generation. The corresponding TypeScript
194+ work is tracked in [ solid-objects-js #46 ] ( https://github.com/cardmagic/solid-objects-js/issues/46 ) .
195+
106196## Formatting and security
107197
108198``` bash
0 commit comments