Skip to content

Make rbs collection discoverable for coding agents that set up RBS/Steep #3093

Description

@pocke

Problem

When someone asks a coding agent (Claude Code, Copilot, Cursor, ...) to "set up RBS and Steep in this project", the agent very often ends up with a Steepfile that lists dependencies by hand:

target :lib do
  signature "sig"
  check "lib"

  library "pathname"
  library "json"
  library "rainbow"   # a gem, listed one by one
end

rbs collection exists exactly so that nobody has to maintain that list: it resolves RBSs from Gemfile.lock, and both rbs and Steep load the installed RBS files automatically. Agents rarely reach that setup on their own.

Agents are becoming a common entry point to RBS, so "what an agent does by default" is effectively part of our onboarding UX. Today the default is the worse path.

For reference, this is the setup we want an agent to produce:

  • Gems in Gemfile.lock are resolved by rbs collection install; nothing to list.
  • Standard libraries are covered either by declaring them in the Gemfile so that they appear in Gemfile.lock, or by listing them under gems: in rbs_collection.yaml.
  • library in a Steepfile is for setups that don't use rbs collection, or can't.

Why agents miss it

Three concrete places where the tools we ship point away from rbs collection:

  1. steep init template. The only hint about how to get types for dependencies is:

    #   # library "pathname"              # Standard libraries
    #   # library "strong_json"           # Gems

    rbs collection is not mentioned at all, so hand-written library lines read as the way to load RBSs for dependencies. An agent that reads the generated Steepfile learns exactly that. (This lives in soutaro/steep, so it needs an upstream PR; tracked here because the motivation is ours.)

  2. rbs collection init output. It prints one line:

    $ rbs collection init
    created: rbs_collection.yaml

    No next step, and no statement of the one fact that would stop an agent from enumerating gems: the gem list comes from Gemfile.lock. An agent that runs init and reads the output still doesn't know it should run install, nor that library lines for gems are unnecessary.

  3. The generated rbs_collection.yaml. Its comments explain sources, path and gems[].ignore, i.e. the knobs, but never the model: "your dependencies are read from Gemfile.lock; you don't list them here". The file is read by the agent right after it is written, so it is a high-value place to say this. The commented-out gems: example only shows ignore: true, so the other half — adding an entry to cover something that isn't in Gemfile.lock, a standard library for instance — stays invisible.

Bonus, related: rbs collection install without rbs_collection.yaml dies with a raw Errno::ENOENT backtrace (lib/rbs/cli.rb never rescues it). An agent that tries install first sees a crash and may conclude collection is broken and fall back to library.

Proposal

1. rbs collection init stdout

Something along these lines (wording to be discussed):

$ rbs collection init
created: rbs_collection.yaml

rbs collection installs RBS files for the gems in your Gemfile.lock.
You don't need to list your dependencies anywhere.

Next steps:
  $ echo "/.gem_rbs_collection/" >> .gitignore
  $ rbs collection install

init is run once per project, so a few extra lines cost nothing, but they should stay short and actionable.

2. Comments in the generated rbs_collection.yaml

Add a header that states the model before the knobs:

# rbs collection installs RBS files for the gems in your Gemfile.lock.
# Run `rbs collection install` to resolve them into rbs_collection.lock.yaml and install them.
# Your dependencies are not listed in this file; they are read from Gemfile.lock.

And show both sides of the gems: section, not only ignore:

# gems:
#   # RBS for a library that doesn't appear in Gemfile.lock, such as a standard library.
#   - name: pathname
#
#   # If you want to avoid installing rbs files for gems, you can specify them here.
#   - name: GEM_NAME
#     ignore: true

3. steep init template (upstream: soutaro/steep)

Mention collection where library is explained, and say what library is actually for:

#   # RBSs for the gems in Gemfile.lock are loaded from rbs collection automatically.
#   # Set it up with `rbs collection init` and `rbs collection install`.
#   #
#   # library "pathname"              # Load an RBS without rbs collection

4. Friendly error for rbs collection install without a config

Replace the raw backtrace with a message that names the fix (rbs collection init), in the same style as RBS::Collection::Config::CollectionNotAvailable.

5. Documentation

docs/collection.md opens with "Requirements" and a full walkthrough. A short quickstart block at the top ("new project? run these two commands") gives agents and humans that fetch the doc the answer in the first screen.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions