8. Share a type across handlers
Every tool returns the same error shape and the same applicant record. You don't want to redeclare them in each handler.
Problem. Every tool returns the same error shape and the same applicant record. You don't want to redeclare them in each handler.
Solution. Put plain RBS in sig/shared/ (no comment markers โ these are real .rbs files), then # @rbs import them.
# sig/shared/applicant.rbs
type applicant = {
id: String,
name: String,
current_stage: String,
applied_at: String
}# sig/shared/error.rbs
# `code` is an open String: every domain returns its own failure codes
# (recruiting uses "applicant_not_found"/"stage_transition_invalid"/...,
# the database and socket recipes below add "query_failed"/"host_not_allowed").
# Narrow it per domain when you want the schema to enumerate them โ see Result.
type error = {
success: false,
error: { code: String, message: String, hint: String }
}# In any handler:
# @rbs import applicant
# @rbs import error
# @rbs type success = { success: true, applicant: applicant }
# @rbs type output = success | errorResult. The compiler loads the .rbs files, merges their types into the handler's type map, and a handler's own @rbs type wins on name conflict. Shared types define shapes only โ keep @requires on the handler, since authorization is a local policy decision, not a property of the type. Leaving code an open String keeps the shared shape honest across domains; if you want one domain's schema to enumerate its codes, narrow it locally with a string-literal union, which compiles to a JSON Schema enum:
# @rbs type error_code = "applicant_not_found" | "stage_transition_invalid" | "already_at_stage"
# @rbs type error = { success: false, error: { code: error_code, message: String, hint: String } }Collected from COOKBOOK.md in the repository. Edit it there, not here.