Skip to content

README: drop two limitations the repository already disproved, add two it does not - #801

Merged
tannevaled merged 2 commits into
mainfrom
docs/stale-limitations
Oct 9, 2026
Merged

tannevaled merged 2 commits into
mainfrom
docs/stale-limitations

Conversation

@tannevaled

Copy link
Copy Markdown
Contributor

Re-checked, as the section promises

What does not work yet carries a dated re-check line. This is that pass, on
fc8ca1a (darwin/arm64) against MRI 4.0.7 on the same host.

claim in the README measured now
GOOS=js GOARCH=wasm does not build go build ./cmd/rbgo exit 0; ./cmd/wasm exit 0
the browser target has no CI lane ci.yml has wasm (js, browser), green on every PR
$stderr is not a separate stream rbgo -e '$stdout.print "O"; $stderr.print "E"' → O on stdout, E on stderr; warn → stderr

The browser entry is the one worth pausing on. #684 fixed the build and
added the lane
— the commit subject says so: "fix the js/wasm build, and make
the guard that caught it reachable"
. So this repository has been shipping a
green gate and a document saying that gate does not exist, in two places:
the limitation, and the Platforms section, whose table omitted the lane
entirely while the prose underneath said it had none.

A dated limitation going stale is ordinary, and the section says when it was
last checked. A limitation that a gate in the same tree disproves on every
push is not: the answer was here the whole time and nothing read it.

The guard

readme_wasm_lane_test.go fails when ci.yml builds GOOS=js GOARCH=wasm and
the README says it cannot, naming both. It is narrow deliberately — it does not
try to validate the whole section against reality, which would mean running the
world; it checks the one class of claim that files already in the repository can
settle. If the lane is ever removed on purpose, the test skips with a note
rather than passing silently.

Proved in both directions, with a check that the control actually modified the
file — and it caught the first draft of this very commit, which quoted the
old sentence back verbatim in the removal note. The note is now a paraphrase.

Two entries added, both measured, both fail-open

$ rbgo -e 'require "timeout"; Timeout.timeout(0.3) { sleep 2 }; puts "returned"'
returned                 # MRI 4.0.7: Timeout::Error after 0.3s

Timeout.timeout never times out. The module, Timeout::Error and the
rescue path all exist; only the enforcement is missing, and
internal/vm/timeout.go:11 says so plainly — it needs pre-emptive interruption
of a running block. Every shape measured: sleep, a busy loop, and a custom
exception class all run to completion. The block is also yielded nil where MRI
yields the limit.

It is written as a paragraph rather than a table row because of its shape:
Timeout.timeout is the ordinary Ruby way to bound work that might hang, and
here it returns successfully, so code that relies on it is unprotected and
nothing says so. Filed separately.

$ rbgo -e 't = Thread.new { loop { $n = ($n||0)+1; sleep 0.01 } }; sleep 0.05
          b = $n; `sleep 1`; puts($n > b ? "ran" : "frozen")'
frozen                   # MRI 4.0.7: ran

A backtick command holds the interpreter lock: every other Ruby Thread is
stopped for its duration. Kernel#sleep yields correctly, which is the control
that makes this specific to the subprocess path rather than a general scheduler
fault. Found by a new type-aware scanner and then confirmed dynamically; filed
separately with the full census.

🤖 Generated with Claude Code

…o it does not

"What does not work yet" promised a dated re-check and got one. Two entries no
longer reproduce, and one of them had not for weeks:

  GOOS=js GOARCH=wasm go build ./cmd/rbgo   exit 0
  GOOS=js GOARCH=wasm go build ./cmd/wasm   exit 0
  rbgo -e '$stdout.print "O"; $stderr.print "E"'  ->  O on stdout, E on stderr

The browser one is the worse of the two. #684 fixed the three missing syscall
constants AND added the lane, so `wasm (js, browser)` has been building that
target green on every pull request while the README said it did not build and
had no lane -- in two separate places, one of them the Platforms table, which
omitted the lane entirely. The answer was in the tree the whole time.

readme_wasm_lane_test.go closes that class: if ci.yml builds the target and the
README says it cannot, the test fails and names both. It is narrow on purpose --
it does not try to validate the whole section against reality, only the claims
that files already in the repository can settle. Proved in both directions, and
it caught the first draft of this very commit, which quoted the old sentence
back verbatim.

Two entries added, both measured against MRI 4.0.7 on this host, and both
fail-open rather than merely missing:

  Timeout.timeout(0.3) { sleep 2 }   returns after 2s, no error (MRI raises at 0.3s)
  `sleep 1`                          freezes every other Ruby Thread (MRI: they run)

Timeout is stated as a paragraph rather than a table row because of the shape:
it is the ordinary Ruby way to bound work that might hang, and it returns
successfully, so code relying on it is unprotected and nothing says so.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… crash

Both rows in the "Otherwise" table were re-checked against MRI 4.0.7 along with
the rest of that section. Every other row still reproduces; these two do not.

  # encoding: ascii-8bit   rbgo prints ASCII-8BIT, same as MRI -- row removed
  BEGIN { puts "B" }       rbgo prints B then main -- works

The BEGIN/END row was the one worth catching. It said both "do not parse",
which reads as an ordinary gap: the feature is unavailable, use something else.
What actually happens now is that BEGIN works and END parses, registers, lets
the program finish, and then kills the process with an uncontained Go panic:

  $ echo 'END { puts "E" }' > p.rb && rbgo p.rb
  panic: runtime error: slice bounds out of range [:1] with capacity 0
  $ echo $?
  2

For an embedder that is worse than a gap: ruby.Run does not return an error, it
takes the host process down. Filed as #805 with the isolation -- at_exit and
BEGIN are fine, the ISeq and its environment disagree about the block's locals,
and the repanic in Run is deliberate policy rather than the defect.

A stale note that promises LESS than the code delivers is a nuisance. One that
promises a missing feature where the code delivers a process death points the
reader the wrong way about the risk, which is why this row is now explicit
about what happens rather than about what is absent.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@tannevaled
tannevaled merged commit 5005d45 into main Oct 9, 2026
9 checks passed
@tannevaled
tannevaled deleted the docs/stale-limitations branch October 9, 2026 12:55
tannevaled added a commit that referenced this pull request Oct 10, 2026
The badge has now been corrected three times for the same reason, and the third
time it was corrected and then UNDONE: on 2026-10-10 Renovate force-pushed its
branch over a commit that had fixed the badge and gone green on every lane,
reinstating 1.27.1 against a 1.27.2 directive. A number a human has to retype is
a number that comes back.

So it stops being retyped. shields.io's github/go-mod/go-version endpoint reads
the directive out of the repository -- verified against this one, where it
renders "Go: v1.27.1" -- which takes the badge out of the maintenance loop
entirely, the same way the release badge stopped naming a version in #801.

The guard gets STRICTER, not weaker, in three ways:

  A dynamic badge is not compared against a value, because it cannot disagree
  with one. Instead the test now fails if a LITERAL badge is left beside it --
  that would be a second, rottable copy, which is the thing being removed.

  The number of hand-written statements required drops from 2 to 1 only when the
  badge is dynamic. Demanding two would force somebody to add a rottable number
  back. It never drops to 0: a README that states the minimum nowhere would pass
  a check for "no WRONG statement" by saying nothing at all, and a reader
  deciding whether they can depend on the module needs the number on the page.

  No badge of either kind is still a hard failure.

Checked in four directions, and one of the four controls was itself wrong, which
is worth recording: setting the directive to a nonexistent 1.27.9 made `go test`
refuse to START, so the guard looked green when it had simply never run. The
file had changed -- which is what I had verified -- and the test had not
executed, which is what I had not. Redone with 1.27.2 it fails correctly:

  README states a Go minimum of 1.27.1 in "Go 1.27.1+"; go.mod requires 1.27.2

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant