Lobsters conformance

Upstream lobsters' own RSpec suite, run against a Roundhouse emit of the same checkout. Most failures name a Rails construct not yet modeled; some are answers that came out wrong, and a few are this harness's own gaps. That makes their tests the worklist generator — so the ranked causes below, not the pass count, are what this page is for.

lobsters cb2b28371dbf · roundhouse 544377934c32 · 2026-10-31T10:23:38Z
226/369 examples pass26 of 111 spec filesemit 1,085 files, 0 errors
226 passing · 143 failing · 1 error(s) outside examples
What this measures. Not whether Roundhouse compiles lobsters — the Ruby target is gap-tolerant and answers that with a zero whether or not the output behaves. Their suite asserts behavior example by example, so a failure here names a construct that was emitted but is wrong or missing. The emit is produced fresh from the same checkout the specs come from, and runs on Ruby 4.0.9, the version their .ruby-version pins.

Current lobsters, not a snapshot. This tracks whatever upstream HEAD is and records its SHA; it deliberately does not pin. A benchmark must freeze its input or its numbers stop being comparable — but this answers a present-tense question, and pinning it would only manufacture a second fossil beside the ruby-bench capture the benchmark page measures.

Each run stands alone. There is no history here and no comparison to previous runs: numbers move when their code moves as well as when ours does, so a delta would attribute upstream's changes to us.

Scope

26 of 111 spec files in their suite are run here — spec/models. Request, feature, mailbox and routing specs are not run. Model specs exercise the compiler surface most densely; the request tier is next, and the capybara/mailbox tier is ledgered rather than attempted.

Todos, by failures blocked

Occurrences churn; causes are the unit of work, so each carries a stable slug you can still refer to after another forty upstream commits. Owner says who can act: most of this is Roundhouse's own modeling debt.

Counts are how many examples are currently stopped by each cause, not how many will pass when it lands: an example is attributed to the first exception it raised, so clearing one cause can reveal another behind it.

BlockedCauseOwner
35
Ran, answered wrong
expectation-mismatch
roundhouse
No exception — the emitted code executed and produced a different answer than Rails does. Each needs individual diagnosis; they cluster by message no better than by coin flip, so this bucket is a count, not a cause. Keep it visible: it is where correctness bugs hide once the crash-shaped failures clear.
5 of 35 examples
  • Comment extracts links from markdown
  • Invitation has a valid factory
  • ModNote validates the length of markeddown_note
  • ModNote validates the presence of markeddown_note
  • Search can search titles for stories
32
Symbol used where an Integer index is expected
symbol-into-integer
roundhouse
Undiagnosed. Shape suggests a Hash emitted as an Array (or a symbol-keyed read lowered to an index) — needs a reduction before it can be ranked honestly.
5 of 32 examples
  • Comment hat can't be worn if user doesn't have that hat
  • Comment hat can be one of the user's hats
  • InvitationRequest has a limit on the email field
  • InvitationRequest has a limit on the memo field
  • InvitationRequest has a limit on the name field
5
errors is an Array, not an ActiveModel::Errors
errors-object-api
roundhouse
The runtime errors collection is Array[String]; attribute identity is lost at push, so errors[:attr] and errors.messages have nothing to answer with.
5 examples
  • ModNote validates the length of note
  • ModNote validates the presence of note
  • Moderation validates the length of action
  • Moderation validates the length of reason
  • Moderation validates a moderation linked to exactly one object
1
travel_to unavailable to specs
harness-time-helpers
this harness
ActiveSupport::Testing::TimeHelpers is a spec-side helper stock Rails mixes into example groups. Ours to provide in the shim, not compiler debt.
1 example
  • User allows totp codes from the surrounding time steps
1
Nested constant not emitted
nested-constant-missing
roundhouse
Constants nested inside a model class (Hat::Mail, Notification::Notifiable) are dropped rather than emitted alongside the class.
1 example
  • Hat santizes email addresses
69
Not yet triaged
unclassified
untriaged
No rule in bench/lobsters/spec-causes.json matches these. Triage, then add a rule.
10 of 69 examples
  • Category logs modification in moderation log logs on update
    NoMethodError: undefined method 'update' for nil
  • CommentRedirectCache caches the destination url, no newline
    NoMethodError: undefined method 'comment_redirect_cache_dir=' for an instance of Rails::FrameworkConfig
  • CommentRedirectCache .delete is idempotent
    NoMethodError: undefined method 'comment_redirect_cache_dir=' for an instance of Rails::FrameworkConfig
  • Comment speed limit limits within 2 minutes
    ActiveRecord::StatementInvalid: no such column: comments.id:
  • Comment speed limit limits longer with flags
    ActiveRecord::RecordInvalid: Validation failed: Story must exist
  • Comment speed limit has an extra message if author flagged a parent
    ActiveRecord::RecordInvalid: Validation failed: Story must exist
  • Comment speed limit doesn't limit slow responses
    ActiveRecord::RecordInvalid: Validation failed: Story must exist
  • Domain origin takes a selector and replacement to generate an origin identifier
    NoMethodError: undefined method 'starts_with?' for an instance of String
  • Domain origin creates a bare-domain origin for bare and trailing slash URLs
    NoMethodError: undefined method 'starts_with?' for an instance of String
  • Domain origin inserts start-and-end-of-line anchors to
    NoMethodError: undefined method 'starts_with?' for an instance of String

Rules matching nothing this run — either cleared, or the failure's message changed and is now counted as untriaged: relation-pick, model-class-first, update-attribute, apppath-concat, kernel-bigdecimal, nil-arith-coercion, dirty-saved-change-alias, has-attribute-predicate, scope-not-on-relation, binary-column-text-escape, searchparser-parslet-dsl, keystore-upsert.

By spec file

FileExamplesPassFail
spec/models/story_spec.rb814734
spec/models/search_spec.rb33924
spec/models/domain_spec.rb26620
spec/models/link_spec.rb14014
spec/models/user_spec.rb22814
spec/models/comment_spec.rb25187
spec/models/invitation_request_spec.rb734
spec/models/mod_note_spec.rb404
spec/models/invitation_spec.rb413
spec/models/moderation_spec.rb303
spec/models/notification_spec.rb16133
spec/models/tag_spec.rb1073
spec/models/comment_redirect_cache_spec.rb202
spec/models/message_spec.rb422
spec/models/vote_spec.rb13112
spec/models/category_spec.rb651
spec/models/hat_spec.rb541
spec/models/mod_mail_spec.rb101
spec/models/story_image_spec.rb211
spec/models/comment_stat_spec.rb110
spec/models/hat_request_spec.rb330
spec/models/keystore_spec.rb440
spec/models/mastodon_app_spec.rb330
spec/models/search_parser_spec.rb78780
spec/models/story_text_spec.rb110
spec/models/username_spec.rb110

What the harness compensates for

The shim that boots the emit in place of Rails does these things on the emit's behalf. Listed because a pass rate achieved by propping up the subject is not a pass rate about the subject.

CompensationKindWhy
require "net/http" in the shimemit gapapp/models/sponge.rb references Net:: constants but the emit never requires net/http.
Numeric duration builders in the shimby designSpecs and factories are untranspiled plain Ruby; the emit path rewrites n.days at transpile time, so the shared runtime never reopens Numeric.

Errors outside examples

A spec file or hook that failed to run at all. These are not counted as failures above, so the denominator understates by however many examples they would have contributed.

An error occurred in an `after(:context)` hook.
Failure/Error: @stories.each(&:destroy!)

ActiveRecord::StatementInvalid:
  no such column: links.story_id:
  SELECT "links"."id" AS id, "links"."from_comment_id" AS from_comment_id, "links"."from_story_id" AS from_story_id, "links"."normalized_url" AS normalized_url, "links"."title" AS title, "links"."to_comment_id" AS to_comment_id, "links"."to_story_id" AS to_story_id, "links"."url" AS url FROM links WHERE (links."story_id" = ?)
                                                                                                                                                                                                                                                                                                                  ^
# /home/rubys/roundhouse/build/lobsters-specs/emit/runtime/db.rb:1278:in 'Db.raise_statement_error'
# /home/rubys/roundhouse/build/lobsters-specs/emit/runtime/db.rb:1347:in 'Db.prepare'
# /home/rubys/roundhouse/build/lobsters-specs/emit/app/models/link.rb:527:in 'Link._hydrate_all'
# /home/rubys/roundhouse/build/lobsters-specs/emit/runtime/active_record/relation.rb:1535:in 'ActiveRecord::Relation#load_records'
# /home/rubys/roundhouse/build/lobsters-specs/emit/runtime/active_record/relation.rb:1694:in 'ActiveRecord::Relation#loaded_records'
# /home/rubys/roundhouse/build/lobsters-specs/emit/runtime/active_record/relation.rb:1701:in 'ActiveRecord::Relation#each'
# /home/rubys/roundhouse/build/lobsters-specs/emit/app/models/story.rb:2759:in 'Story#before_destroy'
# /home/rubys/roundhouse/build/lobsters-specs/emit/runtime/active_record/connection.rb:1899:in 'block (2 levels) in ActiveRecord::Base#destroy'
# /home/rubys/roundhouse/build/lobsters-specs/emit/runtime/active_record/connection.rb:1918:in 'block in ActiveRecord::Base#_halted_by_abort'
# /home/rubys/roundhouse/build/lobsters-specs/emit/runtime/active_record/connection.rb:1917:in 'Kernel#catch'
# /home/rubys/roundhouse/build/lobsters-specs/emit/runtime/active_record/connection.rb:1917:in 'ActiveRecord::Base#_halted_by_abort'
# /home/rubys/roundhouse/build/lobsters-specs/emit/runtime/active_record/connection.rb:1899:in 'block in ActiveRecord::Base#destroy'
# /home/rubys/roundhouse/build/lobsters-specs/emit/runtime/active_record/connection.rb:936:in 'ActiveRecord::Base.transaction'
# /home/rubys/roundhouse/build/lobsters-specs/emit/runtime/active_record/connection.rb:1898:in 'ActiveRecord::Base#destroy'
# /home/rubys/roundhouse/build/lobsters-specs/emit/runtime/active_record/connection.rb:1911:in 'ActiveRecord::Base#destroy!'
# /home/rubys/lobsters/spec/models/search_spec.rb:62:in 'Array#each'
# /home/rubys/lobsters/spec/models/search_spec.rb:62:in 'block (2 levels) in <top (required)>'
# ------------------
# --- Caused by: ---
# SQLite3::SQLException:
#   no such column: links.story_id:
#   SELECT "links"."id" AS id, "links"."from_comment_id" AS from_comment_id, "links"."from_story_id" AS from_story_id, "links"."normalized_url" AS normalized_url, "links"."title" AS title, "links"."to_comment_id" AS to_comment_id, "links"."to_story_id" AS to_story_id, "links"."url" AS url FROM links WHERE (links."story_id" = ?)
#                                                                                                                                                                                                                                                                                                                   ^
#   /home/rubys/roundhouse/build/lobsters-specs/emit/runtime/db.rb:1387:in 'Db.prepare_plain'

Emit diagnostics

From the transpile that produced the tree under test: 1,085 files, 0 errors, 2,009 warnings. Warnings are a modeling-debt ledger rather than a failure — the Ruby target emits through its gaps — so they are not comparable to the spec failures above.

DiagnosticCount
gradual_untyped1,043
unresolved_type457
lower_residue380
unconsumed_initializer52
unsupported32
incompatible_binop15
missing_preload14
send_dispatch_failed9
ivar_unresolved7
Artifact: summary.json — every number on this page comes from it.