From 80047cf994815acaf11f093dd3de3f229a422f28 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Date: Sat, 12 Sep 2026 21:30:06 -0400 Subject: [PATCH 1/2] tc.rb: distinguish 'tapioca could not run' from stale RBIs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The verify step discarded tapioca's stderr and reported every failure as 'RBI files are out of date — run dev rbi'. In a shell without the project env (agent/CI shells get no cd-triggered shadowenv hook), the rbenv shim can't resolve tapioca at all — and the misleading message sends the reader chasing RBI regeneration instead of shell activation. Now an exit-127/command-not-found failure names the real problem and the two correct invocations (dev tc, shadowenv exec). Co-authored-by: Cursor --- bin/tc.rb | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/bin/tc.rb b/bin/tc.rb index 02ef045..16ad6e7 100755 --- a/bin/tc.rb +++ b/bin/tc.rb @@ -45,7 +45,17 @@ class RbiOutOfDateError < StandardError; end unless CLI::UI.spinner("Verifying gem RBIs are in sync...") do Dir.chdir(DEV_ROOT) do - _, _, status = Open3.capture3("bundle", "exec", "tapioca", "gem", "--verify") + _, err, status = Open3.capture3("bundle", "exec", "tapioca", "gem", "--verify") + + # A tapioca that could not run at all (rbenv shim miss, exit 127) is + # an environment problem, not stale RBIs — say so, or the error sends + # the reader chasing `dev rbi` when the fix is shell activation. + if !status.success? && (status.exitstatus == 127 || err.include?("command not found")) + raise RbiOutOfDateError, + "tapioca could not run (#{err.strip.lines.first&.strip}). This is a shell-environment " \ + "problem, not stale RBIs — run through `dev tc` or `shadowenv exec -- bin/tc.rb` " \ + "so the project Ruby activates." + end unless status.success? raise RbiOutOfDateError, From 10b847d84e9b982c1f0ab4fe29f1c2cd53eb8d0d Mon Sep 17 00:00:00 2001 From: Jean-Philippe Date: Sat, 12 Sep 2026 22:04:02 -0400 Subject: [PATCH 2/2] tc: only tapioca's own out-of-date verdict means stale RBIs The first fix special-cased exit 127 / command-not-found, but bundler missing-gem errors, ruby-pin mismatches, and tapioca crashes would still misreport as stale RBIs. Invert the check: positively detect tapioca's verdict on stderr ('RBI files are out-of-date'); every other failure surfaces its real output, with the shadowenv activation hint kept for the command-not-found shape. Verified live: activated run green; removed-RBI run reports stale with tapioca's reason; bare half-activated shell reports the rbenv shim error with the activation hint. Co-authored-by: Cursor --- bin/tc.rb | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/bin/tc.rb b/bin/tc.rb index 16ad6e7..d9bb7ad 100755 --- a/bin/tc.rb +++ b/bin/tc.rb @@ -45,21 +45,28 @@ class RbiOutOfDateError < StandardError; end unless CLI::UI.spinner("Verifying gem RBIs are in sync...") do Dir.chdir(DEV_ROOT) do - _, err, status = Open3.capture3("bundle", "exec", "tapioca", "gem", "--verify") - - # A tapioca that could not run at all (rbenv shim miss, exit 127) is - # an environment problem, not stale RBIs — say so, or the error sends - # the reader chasing `dev rbi` when the fix is shell activation. - if !status.success? && (status.exitstatus == 127 || err.include?("command not found")) - raise RbiOutOfDateError, - "tapioca could not run (#{err.strip.lines.first&.strip}). This is a shell-environment " \ - "problem, not stale RBIs — run through `dev tc` or `shadowenv exec -- bin/tc.rb` " \ - "so the project Ruby activates." - end + out, err, status = Open3.capture3("bundle", "exec", "tapioca", "gem", "--verify") unless status.success? + # Only tapioca's own verdict ("RBI files are out-of-date", printed to + # stderr) means stale RBIs. Any other failure — rbenv shim miss, + # bundler missing gems, ruby-pin mismatch, tapioca crash — is a + # tooling problem: surface the real output instead of misdiagnosing + # it as stale RBIs and sending the reader chasing `dev rbi`. + if err.include?("out-of-date") + raise RbiOutOfDateError, + "RBI files are out of date. Run: dev rbi\nThen commit the updated sorbet/rbi/gems/ files.\n\n#{err.strip}" + end + + hint = if err.include?("command not found") + "\nThis looks like a shell-activation problem — run through `dev tc` or " \ + "`shadowenv exec -- bin/tc.rb` so the project Ruby activates." + else + "" + end raise RbiOutOfDateError, - "RBI files are out of date. Run: dev rbi\nThen commit the updated sorbet/rbi/gems/ files." + "tapioca gem --verify failed (exit #{status.exitstatus}), but not because RBIs " \ + "are stale — fix the underlying error:\n#{[out, err].map(&:strip).reject(&:empty?).join("\n")}#{hint}" end end end