Skip to content

chore(deps): update actions/setup-go action to v7 - #537

Merged
tannevaled merged 1 commit into
mainfrom
renovate/actions-setup-go-7.x
Sep 23, 2026
Merged

tannevaled merged 1 commit into
mainfrom
renovate/actions-setup-go-7.x

Conversation

@tannevaled

Copy link
Copy Markdown
Contributor

This PR contains the following updates:

Package Type Update Change
actions/setup-go action major v6 → v7

Release Notes

actions/setup-go (actions/setup-go)

v7.0.0

Compare Source

What's Changed
New Contributors

Full Changelog: actions/setup-go@v6...v7.0.0


Configuration

📅 Schedule: (UTC)

  • Branch creation
    • At any time (no schedule defined)
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR has been generated by Mend Renovate CLI.

@tannevaled

Copy link
Copy Markdown
Contributor Author

Rebased onto main.

The windows lane was failing on the coverage gate, not on setup-go:

coverage: 99.9%
##[error]coverage 99.9% < 100%

A pull_request run uses the workflow file from the PR head, and this branch was cut on 2026-08-31 — before #542 restricted the 100% gate to the POSIX lanes, and before #623 raised the test timeout from 50m to 90m. So the lane was enforcing a gate main no longer enforces there, on a suite that has since outgrown the old timeout.

Nothing in the dependency bump was at fault. Rebasing picks up both workflow changes; the diff is still the same six lines of setup-go@v6 → @v7.

@tannevaled
tannevaled force-pushed the renovate/actions-setup-go-7.x branch from a86acc4 to 725af53 Compare September 23, 2026 13:42
@tannevaled
tannevaled merged commit 718171d into main Sep 23, 2026
7 checks passed
@tannevaled
tannevaled deleted the renovate/actions-setup-go-7.x branch September 23, 2026 15:03
tannevaled added a commit that referenced this pull request Sep 23, 2026
…isagreed with #blocks

Two product defects the Windows lane found, and one test that asserted one
platform's strerror.

**`realpath_rec` built `/C:`.** `realpathResolve` started the walk at `"/"` and
split the whole path on `/`, so on Windows the volume letter became an ordinary
component and every `File.realpath` / `File.realdirpath` produced
`/C:/Users/...` — a path no such filesystem has. It broke `TestFileIO` and
`TestFileRealdirpath` on windows-latest, and it would have broken
`File.realpath` for every Windows user, not only the tests.

MRI does not start at the root either: `rb_check_realpath_emulate` runs the
path through `skipprefixroot` first and threads the resulting **prefixlen**
through `realpath_rec` (ruby/ruby v3_4_0 file.c:4324, and the
`if (*prefixlenp < RSTRING_LEN(*resolvedp))` guard in its `..` branch). That
prefixlen is not decoration: it is what stops `..` walking above a root that is
more than one character long. rbgo now does the same — `realpathRoot` splits off
the volume (`filepath.VolumeName`, empty on POSIX, `C:` or `//host/share` on
Windows), the walk starts there, `realpathParent` truncates no further, and a
symlink target that names a root of its own restarts from that root rather than
from `/`.

On POSIX the change is a no-op by construction — `filepath.VolumeName` returns
"" there, so the root is `"/"` and nothing is consumed — which is asserted
rather than assumed (`TestRealpathRoot`). The multi-character-root behaviour is
covered on the POSIX lanes too, by passing the prefix length directly to the
primitives instead of waiting for a platform where `VolumeName` returns
something.

**`File::Stat#inspect` printed `blocks=0` where `#blocks` answers `nil`.** On
Windows there is no POSIX stat behind the object, so `#blocks` returns nil (as
MRI does where HAVE_STRUCT_STAT_ST_BLOCKS is undefined) — but the native
`Inspect` formatted the raw field. I had written the member values a second time
instead of reading the accessors' rule, and they drifted at the one member that
can be nil. `blocksValue` is now that rule, in one place, read by both. This is
the defect, not the test: a member-by-member comparison is exactly what should
catch it, and it did.

**`TestRealpathSyscallErr` asserted "Not a directory".** The class is portable;
the message is the platform's strerror, and Windows words it "The system cannot
find the path specified." There is no Windows MRI on this host to witness what
MRI prints there, so the text is asserted only where it was witnessed (MRI 4.0.5
on darwin) rather than relaxed to whatever the lane returns — see the errno-text
issue. The class assertion still runs everywhere.

Merged origin/main (#628, #537) and re-measured against it, whole corpus, per
file: **21 815 -> 21 895, and not one spec file lost an example.**
core/file 618 -> 629, core/filetest 45 -> 88, core/exception 149 -> 174.
`gofmt` clean; `go vet ./...` passes for darwin, linux/amd64, linux/arm64,
windows/amd64 and wasip1/wasm, which compiles the tests too; coverage identical
to main function for function, new code at 100%.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
tannevaled added a commit that referenced this pull request Sep 23, 2026
#634 was red on test (windows-latest) alone, on three subtests of
TestFeatureProvided. The matching is the right side; the test was wrong.

Which side, established before touching anything:

  * load.c v3_4_0 loaded_feature_path decides the prefix boundary with a bare
    `name[plen-1] != '/'` and scans back for the extension with
    `*e != '.' && *e != '/'`. load.c carries NO separator conditional anywhere
    — grep for isdirsep, FILE_ALT_SEPARATOR and _WIN32 over the file finds
    zero. The "and a backslash on Windows" rule is file.c's, whose
    `#define isdirsep(x) ((x) == '/' || (x) == FILE_ALT_SEPARATOR)` drives
    is_explicit_relative — a different function, already modelled by
    isExplicitRelative with os.IsPathSeparator and documented there as
    platform-dependent. So the loaded-features matching is '/'-only on every
    platform, featureProvided/loadedFeaturePath already are, and teaching them
    about backslashes would have been the divergence.

  * The divergence was then LOCATED rather than inferred. featureProvided has
    exactly one platform-dependent step: expandedLoadPath runs each $LOAD_PATH
    entry through featurePath (filepath.Abs + ToSlash). Reproduced on this
    host by feeding it a path darwin does not call absolute:

        lp="/lib"  expanded=["/lib"]                          -> true
        lp="lib"   expanded=["<cwd>/internal/vm/lib"]         -> false
        lp=<t.TempDir()>  expanded=[<same>]                   -> true

    "/lib" on Windows is the middle case: rooted, but with no volume name, so
    filepath.IsAbs is false and Abs joins it onto the working directory. The
    fixture's "/lib/foo.rb" entry then has no matching prefix.

So the eight prefixed cases now build on t.TempDir(), which both sides of
MRI's own comparison have anyway — $LOADED_FEATURES entries come from
rb_file_expand_path and the load path from get_expanded_load_path, both
expanded and absolute. The premise is asserted in one line
(featurePath(dir) == dir) so a platform where it fails says so directly
instead of surfacing as eight confusing mismatches.

This is deliberately NOT a runtime.GOOS skip: that would stop exercising the
matching on the one platform where separators are interesting. It keeps
exercising it, and gains a case that pins the rule there — an entry whose
prefix ends in a BACKSLASH must not match, on either platform. That one is
read from the source, not witnessed: there is no Windows MRI on this host, and
the comment says so.

Worth recording: only the three cases expecting a match actually failed. The
five expecting NO match passed on Windows for the wrong reason — the prefix
never matched at all, so the rule each of them names was never reached. All
eight test what they mean now.

Also merges origin/main (#628, #537). Re-measured from scratch against it:
language/ + core/ 21810 -> 21899 (+89, two clean runs a side); language/
2222 -> 2273; core/kernel 1952 -> 1978; core/module 866 -> 872;
predefined_spec 73 -> 124. Every file that moves still moves up.

go build, go vet, full go test, gofmt, GOOS=wasip1 build and a
GOOS=windows vet of internal/vm all green; the set of functions below 100%
coverage is identical to origin/main's in this environment.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
tannevaled added a commit that referenced this pull request Sep 23, 2026
…e_function (#634)

* vm: model the predefined globals the way MRI's variable table does

Every special global went through one untyped slot: `$/ = 1` stored the
Integer, `$: = []` replaced the load path, `$@ = [...]` wrote a global nobody
read, and `$VERBOSE` read back nil. Give each name the setter MRI gives it.

Read from ruby/ruby v3_4_0:

  * variable.c rb_gvar_readonly_setter + rb_define_hooked_variable ("if
    (!setter) setter = rb_gvar_readonly_setter"), which is what makes every
    rb_define_virtual_variable(name, getter, 0) read-only, and the message
    "%s is a read-only variable" naming the spelling written through.
    Read-only per eval.c Init_eval ($!), re.c Init_Regexp ($&, $`, $', $+),
    load.c Init_load ($:/$LOAD_PATH/$-I, $"/$LOADED_FEATURES), io.c Init_IO
    ($<, $FILENAME, $*), process.c Init_process ($?), ruby.c ruby_prog_init
    ($-W) and rb_define_readonly_boolean ($-a, $-l, $-p).
  * io.c deprecated_str_setter and string.c rb_str_setter / rb_fs_setter /
    rb_fs_check: "value of $/ must be String", "value of $; must be String
    or Regexp", each warning under the deprecated category on a non-nil
    assignment.
  * io.c must_respond_to / stdout_setter / stderr_setter: "$stdout must have
    write method, NilClass given", with rb_obj_class (not rb_obj_classname).
  * io.c argf_lineno_setter (NUM2INT, so $. takes #to_int and a Float
    truncates) and set_LAST_READ_LINE.
  * eval.c errat_getter/errat_setter: $@ is rb_get_backtrace(errinfo), and
    assigning it with no rescued exception raises ArgumentError "$! not set"
    while otherwise routing through Exception#set_backtrace.
  * re.c match_setter (Check_Type T_MATCH) so `$~ = md` moves the backref and
    the derived $&, $`, $', $+, $1.. follow it.
  * ruby.c verbose_setter (RTEST(val) ? Qtrue : val), opt_W_getter, and the
    fact that ruby_verbose/ruby_debug start at Qfalse, so $VERBOSE and $DEBUG
    read false rather than nil.
  * error.c deprecation_warning_enabled(): a non-nil $VERBOSE *and*
    Warning[:deprecated], which is off by default — so the warnings stay
    silent on an ordinary run, exactly as MRI is.

The shared-slot spellings ($-0 for $/, $-F for $;, $-I for $:, $-v/$-w for
$VERBOSE, $-d for $DEBUG, $> for $stdout) now resolve through one
canonicalGvar on both the read and the write side, so they cannot drift.

language/predefined_spec.rb: 73 -> 122 passing, 88 failures+errors -> 39.
No other file in language/, core/kernel or core/module moves.

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

* vm: RUBY_RELEASE_DATE/RUBY_REVISION, and set_backtrace from Locations

Two gaps left by the previous commit's sweep of the predefined names.

RUBY_RELEASE_DATE and RUBY_REVISION were simply missing, so
Object.const_defined?(:RUBY_RELEASE_DATE) was false and every reference
raised NameError. version.h at ruby/ruby tag v3_4_1 composes
RUBY_RELEASE_DATE from RUBY_RELEASE_YEAR/MONTH/DAY and takes RUBY_REVISION
from the generated revision.h — the tagged commit itself, which for v3_4_1 is
48d4efcb85000e1ebae42004e963b5d0cedddcf2 dated 2024-12-25. Both are frozen
Strings, which core/builtin_constants asserts separately from their class.

Exception#set_backtrace rejected an Array of Thread::Backtrace::Location with
a TypeError. error.c v3_4_0 exc_set_backtrace tries
rb_location_ary_to_backtrace FIRST and only falls back to rb_check_backtrace's
String-or-Array-of-String rules when the value is not a location array, so
copying one exception's backtrace_locations into another is legal and sets
both #backtrace and #backtrace_locations.

language/predefined_spec.rb: 122 -> 124 passing.
core/builtin_constants/builtin_constants_spec.rb: 13 -> 17 (0 failures left).

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

* vm: Kernel#trace_var / #untrace_var

Neither existed, so the global-assignment hook was missing entirely and both
names were absent from Kernel.private_instance_methods / Kernel.public_methods.

Read from ruby/ruby v3_4_0 variable.c: rb_f_trace_var (which takes the block
through rb_block_proc() when no command is given — hence the ArgumentError
"tried to create Proc object without a block" — and delegates to
rb_f_untrace_var on an explicit nil command), rb_f_untrace_var (NameError
"undefined global variable $x" for a name with no entry; returns the removed
commands as an Array), and rb_gvar_set_entry, which runs the trace list AFTER
the variable's setter and walks it from the head that rb_f_trace_var pushes
onto — so hooks fire most-recently-registered first, and never fire at all
when the setter raised. A String command is Ruby source, evaluated by
rb_trace_eval/rb_eval_cmd_kw rather than called.

Witnessed against ruby 4.0.5 for each case: registration order (LIFO),
untrace_var's return values with and without a command, the NameError, and
trace_var's nil return.

The hook table hangs off the Kernel module as a lazily created ivar rather
than a VM field, so a program that never traces a global allocates nothing
and Kernel gains no instance variable.

core/kernel/trace_var_spec.rb: 0 -> 6 passing (the file's whole content).
core/kernel/untrace_var_spec.rb: 0 -> 2. No other file moves.

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

* vm: rb_feature_p for require, and the pinned tests the globals work moved

require decided "already loaded?" by absolute path alone. MRI decides it by
NAME, before it touches the filesystem: load.c v3_4_0 search_required calls
rb_feature_p first, which looks in $LOADED_FEATURES for an entry equal to
either "#{name}#{e}" or "#{load_path[j]}/#{name}#{e}" for an acceptable
(possibly empty) extension e, with loaded_feature_path deciding whether an
entry's prefix really is a load-path directory. That is why a feature stays
loaded once when $LOAD_PATH is rearranged under it, and why an entry a
program pushed by hand — ruby/spec pushes "./load_fixture.rb" — suppresses a
require of that same spelling although nothing ever expanded it. Ported here
including loaded_feature_path's backwards extension scan and its
plen/'/'-boundary rules.

core/kernel/require_spec.rb: 66 -> 68/69 passing (the file varies by a point
between runs; measured both alone and under the parallel sweep).

Four pinned tests were asserting behaviour the predefined-globals commit
replaced; each is updated to the value MRI 4.0.5 actually produces, verified
case by case:

  * $VERBOSE now READS false rather than nil (ruby.c ruby_prog_init binds it
    to ruby_verbose, which starts at Qfalse; `ruby -e 'p $VERBOSE'` prints
    false). rbgo's rb_warn gate still keys on the raw unset slot, so the
    warning under test stays silent — noted in the test.
  * `$LOAD_PATH = 42` is a NameError, so loadPathDirs' non-Array arm is now
    reached by emptying the list instead; the NameError is asserted too.
  * `$ERROR_INFO = …` is a NameError naming the alias, which is what MRI
    reports once "English" is loaded.
  * A $stdout that never answered #write can no longer be assigned, so the
    three curStdout/nativePuts fallback tests bind an object that answers
    #write and then remove the method — the branch stays covered and the
    comments say plainly that MRI raises NoMethodError where rbgo tolerates.

Read-only-ness now follows an English alias while the NameError names the
spelling written through, as rb_gvar_readonly_setter's QUOTE_ID does.

Full `go test ./...` green.

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

* vm: whitebox tests for the predefined globals, trace_var and rb_feature_p

Every branch the three preceding commits introduced now has a test, and each
expectation is the answer ruby 4.0.5 gives — the read-only NameErrors and the
spelling each one names, the typed setters' TypeErrors and their accepting
arms, $~ moving the derived match globals, $@ reading and writing the rescued
exception's backtrace, $VERBOSE/$DEBUG/$-W's values and the verbose_setter's
truthy collapse, $= reading false and the deprecation warnings appearing only
under Warning[:deprecated], trace_var's LIFO firing and untrace_var's return
values, and the rb_feature_p match table.

Three simplifications fell out of chasing the last uncovered lines, which is
what the exercise is for:

  * untrace_var returned nil for a defined-but-untraced global where MRI
    returns the empty Array rb_f_untrace_var builds from an empty trace list
    (`$tv = nil; p untrace_var(:$tv)` prints [] on 4.0.5). Fixed, with the
    with-a-command case still nil.
  * doRequire's old "an entry spelled exactly like the bare name counts as
    provided" check is dead now that featureProvided runs first and subsumes
    it; removed rather than left as unreachable code.
  * loadedFeaturePath now counts the separator in its prefix length instead of
    reproducing loaded_feature_path's decrement, which removes the caller's
    off-by-one and the defensive re-check that guarded it.

go test -coverpkg over ./internal/... leaves every function this branch adds
or edits at 100.0%.

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

* vm: Kernel#Complex is complex.c's nucomp_convert, step for step

Complex() was a two-line shape check that rejected most of what MRI accepts:
Complex(Complex(3,4), Complex(5,6)) was "can't convert Complex into Complex",
an object answering #to_c was refused, a nil argument reported its class
instead of "nil", an embedded NUL came out as "invalid value for convert()"
rather than "string contains null byte", and exception: false swallowed
errors MRI lets through.

Rewritten as complex.c v3_4_0 nucomp_convert in that function's own order,
because each step rewrites the argument the next one reads: the nil check, the
STRICT String parse (string_to_c_strict — in BOTH argument positions, so
Complex(1, "b") is an ArgumentError, not a TypeError), the k_exact_zero_p
unwrapping of a Complex argument, the lone-Complex short circuit, the #to_c
conversion of a non-Numeric (rb_convert_type, with both of its messages), the
a1 + a2*i rule for non-real Numerics, and finally nucomp_s_new with
nucomp_real_check's "not a real".

exception: false is not a blanket rescue and is no longer implemented as one.
MRI applies it at exactly three points — the nil check, the String parse, and
a second argument that is not an Integer/Float/Rational — and lets everything
else propagate, so `Complex(:sym, 0, exception: false)` really does raise
TypeError on 4.0.5. Confirmed against it.

The subtle one: complex.c's INTERNAL f_real_p is not Complex#real?. For a
Complex it is nucomp_real_p, i.e. f_zero_p(imag), which calls Complex(1, 0.0)
real although Complex#real? returns false for every Complex. That is the whole
reason Complex(Complex(1, 0.0), 2) is (1+2i) with an Integer imaginary part
while Complex(1, 0.0) + Complex(0, 2) is (1+2.0i) — the first never reaches
the f_add path. Both witnessed on 4.0.5; a fRealP helper now carries the rule.

A 22-case probe table now agrees with ruby 4.0.5 byte for byte.
core/kernel/Complex_spec.rb: 54 -> 60 passing. The 6 that remain need mspec's
real mock_numeric (the vendored shim's is a plain mock), and MRI scores 62 on
this file under the same shim — so the file is finished as far as it can go.
No file in core/complex, core/rational, core/numeric, core/integer or
core/float moves.

Two pinned Go tests were asserting the old behaviour and are updated to the
value MRI gives: Complex(1, "b") is ArgumentError, and a lone non-real Numeric
comes back unchanged rather than raising (covered as a value case instead).

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

* vm: module_function's type guard, Object fallback and define_method mirror

Four gaps in Module#module_function, each read off vm_method.c v3_4_0
rb_mod_modfunc / rb_mod_define_method and witnessed on ruby 4.0.5:

  * rb_mod_modfunc opens with `if (!RB_TYPE_P(mod, T_MODULE)) rb_raise(
    rb_eTypeError, "module_function must be called for modules")`. rbgo
    happily ran it on a Class.
  * Class UNDEFINES it (rb_undef_method(rb_cClass, "module_function")), so it
    is absent from Class.private_instance_methods(true) although Module still
    carries it; rbgo listed it.
  * the named-method search is `me = search_method(m, id, 0); if (!me) me =
    search_method(rb_cObject, id, 0);` — a name the module does not carry is
    looked up on Object, which is what makes `Module.new { module_function
    :require }` work. rbgo raised NameError.
  * rb_mod_define_method reads the same scope visibility a `def` does, so
    under the toggle a define_method'd name is private as an instance method
    and public on the module's singleton. rbgo applied the toggle only in
    OpDefineMethod, so define_method (with a block, a Method or an
    UnboundMethod) escaped it.

The Class tombstone exposed a defensive default in filterVisibility: a
candidate name that resolves to no method counted as PUBLIC, so :module_function
— reaching the walk from Module's table and then resolving to nothing on a
module receiver — started appearing in Module#methods. MRI lists what the
receiver can actually be sent, so an unresolvable candidate is now dropped.
`K23Mod.methods.include?(:module_function)` is false again, as on 4.0.5.

core/module/module_function_spec.rb: 20 -> 26 passing (MRI scores 28 on it
under the same shim; the two left need per-FRAME scope visibility, which rbgo
keeps per module object — an eval'd `module_function` therefore leaks to the
enclosing body).

core/module/autoload_spec.rb swings between 57 and 0 between runs on this
branch as it does on main — issue #615, re-run and it comes back.

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

* vm: Kernel#warn is rb_warn_m

Four divergences, all read off error.c v3_4_0 rb_warn_m / rb_warn_category and
witnessed against ruby 4.0.5 in one probe table:

  * rb_warn_m wraps its WHOLE body in `if (!NIL_P(ruby_verbose) && argc > 0)`,
    so $VERBOSE = nil makes Kernel#warn a no-op — nothing written, nothing
    validated, Warning.warn not called. rbgo wrote regardless. ($VERBOSE false
    still warns; only nil, the -W0 level, silences.)
  * the message is assembled by rb_io_puts, not by joining #to_s: an Array
    argument writes one line per element, nested arrays flatten, and an empty
    Array writes nothing. rbgo inspected it — `warn(["a", "b"])` came out as
    `["a", "b"]`. ioPutsValueRec already models rb_io_puts, so it is reused
    rather than reimplemented.
  * with a level, the message carries a "path:lineno: warning: " prefix, or the
    bare "warning: " when rb_ec_backtrace_location_ary yields no location.
    rbgo has no line numbers at all, so no frame can ever supply a path: the
    no-location form is the truthful answer for every level, and it is what MRI
    itself produces for a level past the bottom of the stack — the one uplevel
    case that now matches byte for byte.
  * the level goes through NUM2LONG, so a Float or a Rational truncates where
    rbgo raised TypeError; a Symbol is still "no implicit conversion of Symbol
    into Integer".

Also rb_warn_category's rb_warning_warn_arity(): a Warning.warn that takes
exactly one argument is called WITHOUT the category keyword, so a program that
overrides it with `def Warning.warn(message)` is not handed an argument it
cannot take.

core/kernel/warn_spec.rb: 18 -> 21 passing. The 4 left all want a real
"file:LINE: warning: " prefix; MRI scores 25 on the file under this shim and
its own 4 failures are a different set (the shim's category handling).

One pinned test asserted the old prefix-less uplevel output; it is updated,
with the comment saying what MRI produces there and why rbgo cannot yet.

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

* vm: Kernel#open redirects to #to_open

io.c v3_4_0 rb_f_open tests `rb_respond_to(argv[0], to_open)` FIRST, before it
treats the argument as a path at all. An object that answers #to_open is
REDIRECTED: open calls it with the remaining arguments (keywords included,
RB_PASS_CALLED_KEYWORDS) and hands back whatever it returns, whatever that is
— the open-uri protocol is built on this. rbgo went straight to the path
conversion and answered "no implicit conversion of MockObject into String".

With a block MRI runs `rb_ensure(rb_yield, io, io_close, io)`: the result is
yielded and then closed, and io_close is a CHECKED call, so a returned value
that does not answer #close (the specs return a Symbol) is left alone rather
than raising.

core/kernel/open_spec.rb: 9 -> 14 passing. MRI scores 15 on the file under this
shim; the 3 pipe-form errors are its too, and the one left over
(`open(path, "r", 0666, {flags: …})` must raise for a POSITIONAL Hash where the
keyword form is accepted) needs rbgo to tell a keyword bundle from a trailing
Hash at the call site.

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

* vm: the rb_feature_p FIXTURE was POSIX-shaped, not the matching

#634 was red on test (windows-latest) alone, on three subtests of
TestFeatureProvided. The matching is the right side; the test was wrong.

Which side, established before touching anything:

  * load.c v3_4_0 loaded_feature_path decides the prefix boundary with a bare
    `name[plen-1] != '/'` and scans back for the extension with
    `*e != '.' && *e != '/'`. load.c carries NO separator conditional anywhere
    — grep for isdirsep, FILE_ALT_SEPARATOR and _WIN32 over the file finds
    zero. The "and a backslash on Windows" rule is file.c's, whose
    `#define isdirsep(x) ((x) == '/' || (x) == FILE_ALT_SEPARATOR)` drives
    is_explicit_relative — a different function, already modelled by
    isExplicitRelative with os.IsPathSeparator and documented there as
    platform-dependent. So the loaded-features matching is '/'-only on every
    platform, featureProvided/loadedFeaturePath already are, and teaching them
    about backslashes would have been the divergence.

  * The divergence was then LOCATED rather than inferred. featureProvided has
    exactly one platform-dependent step: expandedLoadPath runs each $LOAD_PATH
    entry through featurePath (filepath.Abs + ToSlash). Reproduced on this
    host by feeding it a path darwin does not call absolute:

        lp="/lib"  expanded=["/lib"]                          -> true
        lp="lib"   expanded=["<cwd>/internal/vm/lib"]         -> false
        lp=<t.TempDir()>  expanded=[<same>]                   -> true

    "/lib" on Windows is the middle case: rooted, but with no volume name, so
    filepath.IsAbs is false and Abs joins it onto the working directory. The
    fixture's "/lib/foo.rb" entry then has no matching prefix.

So the eight prefixed cases now build on t.TempDir(), which both sides of
MRI's own comparison have anyway — $LOADED_FEATURES entries come from
rb_file_expand_path and the load path from get_expanded_load_path, both
expanded and absolute. The premise is asserted in one line
(featurePath(dir) == dir) so a platform where it fails says so directly
instead of surfacing as eight confusing mismatches.

This is deliberately NOT a runtime.GOOS skip: that would stop exercising the
matching on the one platform where separators are interesting. It keeps
exercising it, and gains a case that pins the rule there — an entry whose
prefix ends in a BACKSLASH must not match, on either platform. That one is
read from the source, not witnessed: there is no Windows MRI on this host, and
the comment says so.

Worth recording: only the three cases expecting a match actually failed. The
five expecting NO match passed on Windows for the wrong reason — the prefix
never matched at all, so the rule each of them names was never reached. All
eight test what they mean now.

Also merges origin/main (#628, #537). Re-measured from scratch against it:
language/ + core/ 21810 -> 21899 (+89, two clean runs a side); language/
2222 -> 2273; core/kernel 1952 -> 1978; core/module 866 -> 872;
predefined_spec 73 -> 124. Every file that moves still moves up.

go build, go vet, full go test, gofmt, GOOS=wasip1 build and a
GOOS=windows vet of internal/vm all green; the set of functions below 100%
coverage is identical to origin/main's in this environment.

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

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
tannevaled added a commit that referenced this pull request Sep 23, 2026
…and the stat surface (+79) (#629)

* filetest: FileTest is File's own predicates, not a second copy (+42 in core/filetest)

MRI defines its one-path file predicates once and installs each on BOTH
FileTest and File through the same C function:

    #define define_filetest_function(name, func, argc) do {        \
        rb_define_module_function(rb_mFileTest, name, func, argc); \
        rb_define_singleton_method(rb_cFile, name, func, argc);    \
    } while(false)

(ruby/ruby v3_4_0 file.c:6635, and its 26 call sites in Init_File at
file.c:7456-7485). rbgo instead had two implementations: a complete one on
File and a partial hand-written one on FileTest carrying 13 of the 26 names.
The 13 missing were blockdev?, chardev?, empty?, executable_real?, grpowned?,
identical?, owned?, pipe?, readable_real?, setgid?, setuid?, socket?,
sticky?, world_readable?, world_writable? and writable_real? — every one of
them a NoMethodError, which is what core/filetest was mostly failing on.

registerFileTest now re-exports File's own *Method records for exactly the
names in define_filetest_function's list. Sharing the record (rather than
copying a closure) is what makes FileTest.method(:zero?) == FileTest.method
(:empty?) hold, which core/filetest/zero_spec.rb asserts.

That exposed a second half of the same drift: File's predicates did not all
check arity. MRI's rb_file_directory_p and friends take argc 1, and
core/filetest/shared/exist.rb asserts the ArgumentError for both receivers.
oneArg now guards the four shared closures (access, realAccess, statTest,
worldPerm) and the four inline predicates, so File.zero? no longer reaches an
index-out-of-range panic with no arguments.

MRI 4.0 removed the deprecated exists? spelling: 4.0.5 reports
FileTest.respond_to?(:exists?) == false, and it is not in
define_filetest_function's list. TestFileTest asserted the pre-4.0 spelling;
it now asserts the NoMethodError, witnessed against the host MRI.

core/filetest: 45 -> 87 passing. core/file and core/exception unchanged
(618 / 149). No spec file lost an example.

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

* errno+exception: SystemCallError.new is a constructor, not a stub (+23 in core/exception)

SystemCallError had no #initialize at all: every argument was discarded, so
SystemCallError.new(Errno::EINVAL::Errno) was a message-less generic
SystemCallError, #errno was read off the class rather than the call, and
Errno::EINVAL.new.message said "Errno::EINVAL" where MRI says "Invalid
argument". This implements MRI's syserr_initialize (ruby/ruby v3_4_0
error.c:3104) whole, including the part that surprises: on the generic class a
lone Integer argument IS the errno, and when the number has a registered class
the receiver's class is REWRITTEN in place (RBASIC_SET_CLASS), which is what
makes SystemCallError.new(22).instance_of?(Errno::EINVAL) true.

Also from error.c: syserr_errno (3153) — #errno returns the value AS GIVEN, so
new("x", 2.9).errno is 2.9 even though the class lookup truncated it to 2 —
and syserr_eqq (3168), the .=== that matches by error NUMBER, so
`rescue Errno::EINVAL` catches a SystemCallError that was never given that
class.

The Errno table was rebuilt against Init_syserr (error.c:4204) and set_syserr
(error.c:3064), which have two rules rbgo did not follow:

  - ONE CLASS PER NUMBER, not per name. A second name for a number that
    already has a class becomes another constant on that same class. That is
    why Errno::EWOULDBLOCK.equal?(Errno::EAGAIN) holds.
  - A name the platform does not define is STILL A CONSTANT — MRI's
    undefined_error binds it to Errno::NOERROR. So Errno.constants is the full
    known_errors list on every platform.

rbgo had 28 names and no NOERROR; it now has MRI's 158, which is what
system_call_error_spec.rb leans on when it takes Errno.constants.size as an
errno number no class can claim. Numbers come from Go's portable syscall
package for the 76 names it defines on every GOOS rbgo builds for (darwin,
linux, windows, wasip1); the remaining names take the undefined_error path.
That is exact on a platform whose libc does not define them and reports 0
where MRI reports a real number on one that does (macOS has EAUTH at 80) —
named in the PR, not papered over. EWOULDBLOCK is registered as EAGAIN's
second name, which every platform rbgo builds for agrees with.

Messages come from syscall.Errno.Error(), whose table is strerror's with a
lower-case lead; errnoStrerror restores the letter and replaces Go's "errno N"
placeholder with the C library's wording. Witnessed byte-for-byte against MRI
4.0.5 over 45 expressions (class, message, errno, arity and coercion errors
for both receiver shapes).

TestProcessSysFailFallback asserted that errno 0 has no registered class. That
premise is now false, and it is false in MRI too (4.0.5:
SystemCallError.new(0).class == Errno::NOERROR), so the case was moved to a
number no name claims and errno 0 given its own assertion.

core/exception: 149 -> 172. errno_spec 1 -> 8, io_error_spec 2 -> 6,
system_call_error_spec 11 -> 22, case_compare_spec 4 -> 5. core/file and
core/filetest unchanged (618 / 87). No spec file lost an example.

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

* file: realpath/realdirpath resolve like realpath_rec, not like EvalSymlinks

Both were filepath.EvalSymlinks with an error guess bolted on. MRI resolves
them with one recursive walk, realpath_rec (ruby/ruby v3_4_0 file.c:4324),
driven by rb_check_realpath_emulate (file.c:4437) and reached through
rb_check_realpath_internal (file.c:4554). EvalSymlinks differs in ways the
specs notice:

  - It refuses a path whose LAST component is a dangling symlink. MRI's
    realdirpath answers the link's TARGET there: realpath_rec follows the link
    and then tolerates the absent name (file.c:4390). rbgo answered the link
    itself.
  - A missing component under a symlink was reported against the symlink, not
    against the resolved name the walk actually failed on.
  - Its errors carry no errno, so ENOTDIR (a path walking through a regular
    file) came out as ENOENT.

realpathResolve is realpath_rec: the component loop, ".."-truncation, the
loopcheck table that both detects a cycle (ELOOP) and answers a link already
resolved, the fallback at file.c:4383, and the
`mode == RB_REALPATH_STRICT || !last || *unresolved_firstsep` rule that decides
whether an absent final component is the answer or an error. The last clause is
why the trailing separator is threaded separately: MRI walks the path as
WRITTEN, so realdirpath("dir/absent/") raises where realdirpath("dir/absent")
does not, and expanding the path (which the base-directory argument requires)
drops it.

The two error WORDINGS are not the same, and now differ correctly: File.realpath
reaches realpath(3) first, so ENOENT and ELOOP name rb_check_realpath_internal
and the whole argument, while ENOTDIR falls through to the emulation and names
realpath_rec and the failing component. File.realdirpath is the emulation, so it
always names realpath_rec.

Verified byte-for-byte against MRI 4.0.5 over 25 expressions (dangling links,
links into missing directories, cycles, relative and absolute link targets,
"..", trailing separators, a regular file used as a directory) — identical.

core/file/realdirpath_spec.rb: 3 -> 4.

The other 11 realpath/realdirpath failures are NOT VM defects: the shim's
SPEC_TMP_BASE is "/tmp", which on darwin is a symlink to /private/tmp, so every
expectation of the form `File.realpath(link).should == @file` compares a
resolved path with an unresolved one. MRI 4.0.5 fails them the same way — it
also answers /private/tmp there. They need SPEC_TMP_BASE moved off a symlinked
root in scripts/conformance/rubyspec/spec_helper.rb, which this branch does not
own.

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

* filestat+exception: real atime/ctime/birthtime, File#stat, rb_stat_inspect, #inspect through #to_s

Four defects, all in this branch's files.

1. File::Stat#atime and #ctime REPORTED THE MODIFICATION TIME. Not an
   approximation — the same number, so File.utime(2000, 2001, f) came back as
   (2001, 2001) and core/file/lutime_spec.rb was reading a value rbgo could not
   have got right. MRI reads st_atime / st_ctime off struct stat (ruby/ruby
   v3_4_0 file.c rb_file_s_atime, stat_atime). Go's syscall.Stat_t has them, but
   under names that differ per platform — Atim/Ctim on Linux, Atimespec/
   Ctimespec on the BSDs and macOS — so shared code cannot name one and
   compile. statTimeField reads whichever the platform carries by reflection,
   which keeps the rule in the shared file where the POSIX lanes cover it
   instead of splitting it across four build-tagged copies. A stat struct with
   neither still falls back to the modification time, which is what rbgo
   reported everywhere before.

2. File::Stat#birthtime raised NotImplementedError unconditionally. It now
   answers where the platform has a creation time (st_birthtime on the BSDs and
   macOS) and keeps MRI's NotImplementedError — same message — where there is
   none, which is MRI's own HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC guard and what
   MRI does on Linux.

3. File::Stat#inspect was the placeholder "#<File::Stat>". It is now
   rb_stat_inspect (ruby/ruby v3_4_0 file.c:1080): the member list in that
   order, dev and rdev in hex, mode in octal with a leading zero, birthtime only
   where it exists. It is written as the value's native Inspect, not as a
   dispatch through the Ruby accessors, because rb_stat_inspect calls the C
   member functions directly — a subclass overriding #uid does not change what
   #inspect prints — and because Kernel#p reaches the same rendering, so `p st`
   and `st.inspect` cannot drift. Two existing tests asserted the placeholder;
   they now assert the real shape, witnessed against MRI 4.0.5.

4. File#stat and File#lstat did not exist, which is what core/file/stat/
   comparison_spec.rb was failing on before it could compare anything. Added in
   registerFileStat beside File.stat/File.lstat. They take no argument
   (ArgumentError otherwise) and refuse a closed stream with IOError, but NOT a
   read-only one — MRI's rb_io_stat works there, so ioCheckOpen is the wrong
   guard.

Also Exception#inspect: it read the stored @message where MRI's exc_inspect
(error.c:1838) goes through rb_obj_as_string(exc), i.e. dispatches #to_s. A
subclass that overrides #to_s therefore changes what #inspect reports, and a
#to_s returning a non-String falls back to the object's identity form. rbgo
already had objAsString with exactly those semantics; #inspect now uses it
rather than a second reading of the same rule.

Verified byte-for-byte against MRI 4.0.5: #inspect over 7 to_s shapes, the
atime/lutime/utime triple, File#stat/#lstat over a symlink, and
File::Stat#inspect built member by member the way the spec builds it.

core/file 618 -> 624, core/exception 172 -> 174, core/filetest unchanged (87).
lutime_spec 6 -> 8, stat/inspect_spec 0 -> 1, stat/comparison_spec 0 -> 2,
stat_spec 4 -> 5, lstat_spec 4 -> 5, exception/inspect_spec 3 -> 5.

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

* file: the stat predicates take an IO, not only a path (rb_stat)

MRI resolves the argument of its whole stat-predicate family through one
function, rb_stat (ruby/ruby v3_4_0 file.c:1304):

    tmp = rb_check_convert_type_with_id(file, T_FILE, "IO", idTo_io);
    if (!NIL_P(tmp)) { GetOpenFile(tmp, fptr); result = fstat_without_gvl(fptr, st); }
    else { FilePathValue(file); result = stat_without_gvl(RSTRING_PTR(file), st); }

so File.directory?(io), File.size(io) and FileTest.directory?(anything_with_
to_io) all work. rbgo coerced every one of them with pathArg, which is
FilePathValue alone, so an IO or a #to_io object was "no implicit conversion of
X into String".

statPathArg now does what rb_stat does, and is applied to exactly the seventeen
methods that call rb_stat in file.c — directory?, pipe?, socket?, blockdev?,
chardev?, setuid?, setgid?, sticky?, owned?, grpowned?, exist?, file?,
zero?/empty?, size?, world_readable?, world_writable?, identical? and
File.size. NOT to readable?/writable?/executable? and the *_real? family, which
go through rb_eaccess, nor to symlink?/ftype, which lstat a path. (An open File
still resolves there, through File#to_path — MRI answers true for
File.readable?(io), and so does rbgo; the difference is which rule gets it
there.)

A #to_io that answers something other than an IO is not silently ignored:
rb_check_convert_type_with_id raises "can't convert Object to IO (Object#to_io
gives Integer)", which statPathArg reproduces.

Verified byte-for-byte against MRI 4.0.5 over 14 expressions, including
File.directory?(STDIN) and an open directory handle.

core/file 624 -> 627, core/filetest 87 -> 88. directory_spec 7 -> 8 on both
receivers, size_spec 18 -> 20.

Coverage: every function in internal/vm is at the same percentage as on main,
with the new code at 100%.

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

* tests: the errno texts are the PLATFORM's, not darwin's — guard what has no witness

The arch lanes (linux amd64/arm64) went red on one assertion:

    errnoStrerror(92) = "Protocol not available", want "Illegal byte sequence"

92 is EILSEQ on darwin and ENOPROTOOPT on Linux. The test hard-coded the
NUMBER and darwin's text for it, which asserts one platform's <errno.h> on
every lane — exactly the mistake this lane has paid for before.

TestErrnoStrerror now separates the two things it was conflating:

  - The RULE, which is portable: for an errno the platform's own table names,
    errnoStrerror is that text with its leading letter restored. Checked for
    every name in errnoNumbers, on every platform.
  - The TEXTS, which are not: the literals are MRI 4.0.5's output on darwin,
    the only platform with an MRI witness on this host, so they are skipped
    elsewhere rather than loosened to whatever the lane happens to print.

Two more platform assumptions found by reading, before the remaining lanes
reported:

  - The "unknown errno" wording only applies where the platform leaves Go's
    "errno N" placeholder. Windows' FormatMessage names every number, so there
    is nothing to substitute and nothing to assert.
  - TestSyserrInitializeShapes asserts strerror texts (darwin and glibc agree on
    the three it uses). Windows has no POSIX errno numbers at all — Go spells
    them APPLICATION_ERROR+n — so it skips there, naming the missing witness.
  - TestFileStatInspect guessed birthtime's presence from runtime.GOOS, which
    was wrong for Windows (Sys() is a Win32FileAttributeData whose CreationTime
    is a Filetime, not a Sec-bearing timespec). It now asks the object, which is
    also what core/file/stat/inspect_spec.rb does.
  - TestFileStatAtimeIsNotMtime skips on Windows for the same reason: atime
    falls back to mtime there, MRI on Windows reports a real one, and there is
    no Windows MRI here to witness the right fix.

`go vet` now passes for darwin, linux/amd64, linux/arm64, windows/amd64 and
wasip1/wasm — which compiles the tests, not only the package. Coverage is
unchanged: identical to main, function for function.

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

* file+filestat: the realpath walk assumed a POSIX root, and #inspect disagreed with #blocks

Two product defects the Windows lane found, and one test that asserted one
platform's strerror.

**`realpath_rec` built `/C:`.** `realpathResolve` started the walk at `"/"` and
split the whole path on `/`, so on Windows the volume letter became an ordinary
component and every `File.realpath` / `File.realdirpath` produced
`/C:/Users/...` — a path no such filesystem has. It broke `TestFileIO` and
`TestFileRealdirpath` on windows-latest, and it would have broken
`File.realpath` for every Windows user, not only the tests.

MRI does not start at the root either: `rb_check_realpath_emulate` runs the
path through `skipprefixroot` first and threads the resulting **prefixlen**
through `realpath_rec` (ruby/ruby v3_4_0 file.c:4324, and the
`if (*prefixlenp < RSTRING_LEN(*resolvedp))` guard in its `..` branch). That
prefixlen is not decoration: it is what stops `..` walking above a root that is
more than one character long. rbgo now does the same — `realpathRoot` splits off
the volume (`filepath.VolumeName`, empty on POSIX, `C:` or `//host/share` on
Windows), the walk starts there, `realpathParent` truncates no further, and a
symlink target that names a root of its own restarts from that root rather than
from `/`.

On POSIX the change is a no-op by construction — `filepath.VolumeName` returns
"" there, so the root is `"/"` and nothing is consumed — which is asserted
rather than assumed (`TestRealpathRoot`). The multi-character-root behaviour is
covered on the POSIX lanes too, by passing the prefix length directly to the
primitives instead of waiting for a platform where `VolumeName` returns
something.

**`File::Stat#inspect` printed `blocks=0` where `#blocks` answers `nil`.** On
Windows there is no POSIX stat behind the object, so `#blocks` returns nil (as
MRI does where HAVE_STRUCT_STAT_ST_BLOCKS is undefined) — but the native
`Inspect` formatted the raw field. I had written the member values a second time
instead of reading the accessors' rule, and they drifted at the one member that
can be nil. `blocksValue` is now that rule, in one place, read by both. This is
the defect, not the test: a member-by-member comparison is exactly what should
catch it, and it did.

**`TestRealpathSyscallErr` asserted "Not a directory".** The class is portable;
the message is the platform's strerror, and Windows words it "The system cannot
find the path specified." There is no Windows MRI on this host to witness what
MRI prints there, so the text is asserted only where it was witnessed (MRI 4.0.5
on darwin) rather than relaxed to whatever the lane returns — see the errno-text
issue. The class assertion still runs everywhere.

Merged origin/main (#628, #537) and re-measured against it, whole corpus, per
file: **21 815 -> 21 895, and not one spec file lost an example.**
core/file 618 -> 629, core/filetest 45 -> 88, core/exception 149 -> 174.
`gofmt` clean; `go vet ./...` passes for darwin, linux/amd64, linux/arm64,
windows/amd64 and wasip1/wasm, which compiles the tests too; coverage identical
to main function for function, new code at 100%.

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

* test: stop asserting POSIX-only shapes on the windows lane

Two assertions failed on windows, and neither was the VM being wrong.

File::Stat#inspect printed `blocks=nil, blksize=0` where the layout regexp
demanded digits. That IS MRI's answer: #blocks is nil where
HAVE_STRUCT_STAT_ST_BLOCKS is undefined, which is every Windows build, and
rb_stat_inspect prints that nil. blocksValue already models it; only the test
insisted on a number no Windows stat can produce. The regexp now accepts either
shape, with the reason beside it.

TestFileRealdirpath compared File.realdirpath against Go's
filepath.EvalSymlinks. On POSIX the two canonicalisers agree; on Windows
EvalSymlinks additionally expands an 8.3 short name (C:/Users/RUNNER~1 ->
C:/Users/runneradmin) and realpath_rec does not, so the test was measuring Go's
normalisation rather than ours. Whether MRI expands it there is unwitnessed —
there is no Windows MRI on this host — so the full equality is asserted where
the two agree by construction, and windows asserts what holds either way:
absolute, forward-slashed, naming the file. The case is not skipped; realdirpath
is still exercised on that lane. The open question is issue #636.

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

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
tannevaled added a commit that referenced this pull request Sep 23, 2026
Four units:

  #628  language (compiler): defined?(super) — 18 examples behind one opcode —
        the defined? tags frozen, array/hash literal probing, the two method
        probes MRI distinguishes, block auto-splat through #to_ary, and the
        pattern-matching deconstruct contract   (language 2158 -> 2222)
  #634  predefined globals, trace_var/untrace_var, rb_feature_p, Kernel#Complex,
        Module#module_function, Kernel#warn and #open
                                   (predefined_spec 73 -> 124 passing,
                                    core/kernel +26, core/module +6)
  #629  FileTest re-exported from File's own records, the Errno table from 28
        names to MRI's 158, SystemCallError#initialize/#errno/.===, the real
        realpath/realdirpath walk, File::Stat#atime/#ctime (both had returned
        mtime), #inspect, File#stat/#lstat
                                   (core/file 618 -> 629, core/filetest 45 -> 88,
                                    core/exception 149 -> 174)
  #537  ci: actions/setup-go v7 (unblocked by rebasing; it had been red for
        three weeks on a coverage gate main no longer enforces on that lane)

Measured with scripts/conformance/rubyspec/run.sh against the pinned corpus at
87b1631, 16-way, TIMEOUT=25, three consecutive runs: 21983, 21982, 21981. The
floor is set eleven below the lowest, the same margin rule as the last two
bumps: enough for the run-to-run jitter, not enough to hide the loss of a whole
spec file. #615 remains open and can still cost a file when it fires.

This bump is VM conformance throughout — unlike the previous one, none of it
comes from the harness or from a dependency making new files visible.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
tannevaled added a commit that referenced this pull request Sep 24, 2026
Four units, and one of them was a dependency away:

  #639  regexp search anchoring and encoding, Refinement#import_methods,
        pack/unpack
  #645  THE LINE MAP. __LINE__ evaluated to 0 everywhere because the ISeq
        carried no line table — 101 examples across five directories were
        blocked on it, and three earlier agents each reported it as the wall
        they could not pass. The wall was one repository down: the parser's
        AST carried no position information at all, 55 node types with no
        line field, and the lexer discarded every token's line at the AST
        boundary. go-ruby-parser v0.3.0 adds ast.Program.Lines; this PR
        builds MRI's compressed insns_info/positions table on it.
        language/line_spec 0/6 -> 6/6, backtraces carry real lines, frame
        labels name blocks the way MRI does.
  #640  Kernel#sleep with the fiber scheduler (sleep_spec 0 -> 15, one
        example had been hanging the whole file), File.open's mode/flags/perm
        contract driven by the open(2) flag set rather than a six-way mode
        string, IO::Buffer.map                     (+90 across core/file,
                                                    core/io, core/kernel)
  #537, #527 already in the previous floor's tree

Measured with scripts/conformance/rubyspec/run.sh against the pinned corpus at
87b1631, 16-way, TIMEOUT=25, three consecutive runs: 22153, 22152, 22152. The
floor is set twelve below the lowest, the margin rule the last three bumps
used: enough for the run-to-run jitter, not enough to hide the loss of a spec
file. #615 remains open and still costs a file when it fires.

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