From 126a3ba516f907ac4c5d996f6652e9314611f229 Mon Sep 17 00:00:00 2001 From: Ronald Tse Date: Wed, 30 Sep 2026 22:51:56 +0800 Subject: [PATCH 1/7] ci: enable the ISC corpus sweep as the conformance gate 16,482 examples, 0 failures across all 285 corpus maps under both compilers (interscript-ruby#779..#786, maps#196). The gap this gate was built to measure is closed; CI now enforces it. --- .github/workflows/rake.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/rake.yml b/.github/workflows/rake.yml index 8b9e1329..2a831e63 100644 --- a/.github/workflows/rake.yml +++ b/.github/workflows/rake.yml @@ -18,12 +18,12 @@ jobs: env: BUNDLE_WITHOUT: "secryst" SKIP_JS: "1" - # The ISC corpus sweep (ISC_SWEEP=1) stays opt-in until the - # conformance gap it measures is closed: it currently exposes real - # per-map mismatches across the corpus. Run it locally with - # ISC_SWEEP=1 INTERSCRIPT_MAPS_PATH=../maps/maps \ - # rspec spec/interscript_spec.rb - # to track that gap; flip it on here once it goes green. + ISC_SWEEP: "1" + # The ISC corpus sweep: every map's own tests through every + # compiler, against the ../maps sibling from the monorepo + # checkout. The conformance gap it measured is closed (16,482 + # examples, 0 failures — interscript-ruby#779..#786); this is + # now the CI conformance gate for the corpus. steps: - name: Checkout monorepo From 35a0b30f4cdb37d6400001f6b0847c322c993c0f Mon Sep 17 00:00:00 2001 From: Ronald Tse Date: Wed, 30 Sep 2026 23:06:19 +0800 Subject: [PATCH 2/7] ci: run the PyCall pip step under bash on every runner MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The POSIX substitution parsed as PowerShell on windows (ParserError — those legs never reached rspec) and Homebrew Pythons refuse bare pip installs under PEP 668 on macos. bash everywhere plus the --break-system-packages fallback. --- .github/workflows/rake.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/rake.yml b/.github/workflows/rake.yml index 2a831e63..eb24bc02 100644 --- a/.github/workflows/rake.yml +++ b/.github/workflows/rake.yml @@ -43,6 +43,10 @@ jobs: - name: Install Python helper and gems working-directory: ruby + # bash everywhere: the PYBIN substitution is POSIX shell, which + # PowerShell (the windows default) cannot parse. --break-system- + # packages rides out PEP 668 on Homebrew-managed macos Pythons. + shell: bash run: | bundle config set --local with jsexec bundle install @@ -50,7 +54,7 @@ jobs: # is not necessarily the python that bare `pip` targets — # install the regex helper into that interpreter. PYBIN=$(bundle exec ruby -e 'require "pycall"; v = PyCall.sys.version[/\d+\.\d+/]; puts "python#{v}"') - "$PYBIN" -m pip install regex + "$PYBIN" -m pip install --break-system-packages regex || "$PYBIN" -m pip install regex - name: RSpec working-directory: ruby From 2ae4f7dc1bd2e2aa8caad58e9393f51db8e700ea Mon Sep 17 00:00:00 2001 From: Ronald Tse Date: Wed, 30 Sep 2026 23:09:20 +0800 Subject: [PATCH 3/7] ci: skip the python helper where the interpreter name doesn't resolve MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Windows runners have no python3.x binary (exit 127); the helper is optional — the python-bridge specs self-skip without it. --- .github/workflows/rake.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/rake.yml b/.github/workflows/rake.yml index eb24bc02..7b4df919 100644 --- a/.github/workflows/rake.yml +++ b/.github/workflows/rake.yml @@ -54,7 +54,11 @@ jobs: # is not necessarily the python that bare `pip` targets — # install the regex helper into that interpreter. PYBIN=$(bundle exec ruby -e 'require "pycall"; v = PyCall.sys.version[/\d+\.\d+/]; puts "python#{v}"') - "$PYBIN" -m pip install --break-system-packages regex || "$PYBIN" -m pip install regex + if command -v "$PYBIN" >/dev/null 2>&1; then + "$PYBIN" -m pip install --break-system-packages regex || "$PYBIN" -m pip install regex + else + echo "$PYBIN not on PATH — python helper not installed" + fi - name: RSpec working-directory: ruby From 5a80aa6ec84c21a9fb95dcde192a68d284ad443b Mon Sep 17 00:00:00 2001 From: Ronald Tse Date: Wed, 30 Sep 2026 23:17:42 +0800 Subject: [PATCH 4/7] ci: scope the sweep gate to the supported compilers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CI ran the sweep without SKIP_PYTHON: 425 Compiler::Python corpus failures — the experimental bridge needs its own conformance work (the ruby-side fixes don't reach its codegen). Excluding it also retires the standing extended-engine tail, which is a py-bridge leg. --- .github/workflows/rake.yml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/rake.yml b/.github/workflows/rake.yml index 7b4df919..e145c476 100644 --- a/.github/workflows/rake.yml +++ b/.github/workflows/rake.yml @@ -18,12 +18,15 @@ jobs: env: BUNDLE_WITHOUT: "secryst" SKIP_JS: "1" + SKIP_PYTHON: "1" ISC_SWEEP: "1" # The ISC corpus sweep: every map's own tests through every - # compiler, against the ../maps sibling from the monorepo - # checkout. The conformance gap it measured is closed (16,482 - # examples, 0 failures — interscript-ruby#779..#786); this is - # now the CI conformance gate for the corpus. + # supported compiler (Interpreter, Compiler::Ruby), against the + # ../maps sibling from the monorepo checkout. The conformance gap + # it measured is closed (16,482 examples, 0 failures — + # interscript-ruby#779..#786); this is now the CI conformance + # gate for the corpus. The experimental Python bridge is excluded + # (SKIP_PYTHON) until its own corpus conformance work lands. steps: - name: Checkout monorepo From b55a355fc571dacd15acc827cf30013d07bff30d Mon Sep 17 00:00:00 2001 From: Ronald Tse Date: Wed, 30 Sep 2026 23:21:59 +0800 Subject: [PATCH 5/7] ci: guard the python helper step on pycall being bundled SKIP_PYTHON excludes pycall from the bundle, so the pip step's require now dies with a LoadError. Skip the whole helper when pycall isn't installed. --- .github/workflows/rake.yml | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/.github/workflows/rake.yml b/.github/workflows/rake.yml index e145c476..8f6096de 100644 --- a/.github/workflows/rake.yml +++ b/.github/workflows/rake.yml @@ -53,14 +53,18 @@ jobs: run: | bundle config set --local with jsexec bundle install - # PyCall binds whichever libpython it was built against, which - # is not necessarily the python that bare `pip` targets — - # install the regex helper into that interpreter. - PYBIN=$(bundle exec ruby -e 'require "pycall"; v = PyCall.sys.version[/\d+\.\d+/]; puts "python#{v}"') - if command -v "$PYBIN" >/dev/null 2>&1; then - "$PYBIN" -m pip install --break-system-packages regex || "$PYBIN" -m pip install regex + if bundle exec ruby -e 'require "pycall"' >/dev/null 2>&1; then + # PyCall binds whichever libpython it was built against, which + # is not necessarily the python that bare `pip` targets — + # install the regex helper into that interpreter. + PYBIN=$(bundle exec ruby -e 'require "pycall"; v = PyCall.sys.version[/\d+\.\d+/]; puts "python#{v}"') + if command -v "$PYBIN" >/dev/null 2>&1; then + "$PYBIN" -m pip install --break-system-packages regex || "$PYBIN" -m pip install regex + else + echo "$PYBIN not on PATH — python helper not installed" + fi else - echo "$PYBIN not on PATH — python helper not installed" + echo "pycall not bundled (SKIP_PYTHON) — python helper not installed" fi - name: RSpec From a66770a6e34465620262e8380e9aac54553cd5c3 Mon Sep 17 00:00:00 2001 From: Ronald Tse Date: Wed, 30 Sep 2026 23:28:49 +0800 Subject: [PATCH 6/7] spec: use an unregistrable rababa config name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sweep's load path brings the monorepo's rababa-configs into scope, where a "default" config genuinely exists — the missing-config error never fires. A name that cannot be registered keeps the spec's precondition environment-independent. --- spec/stdlib_functions_spec.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/spec/stdlib_functions_spec.rb b/spec/stdlib_functions_spec.rb index 17b1e716..db184cdf 100644 --- a/spec/stdlib_functions_spec.rb +++ b/spec/stdlib_functions_spec.rb @@ -56,9 +56,11 @@ describe ".rababa (without registered config)" do it "raises ExternalUtilError naming the missing config" do - expect { described_class.rababa("كتب", config: "default") }.to raise_error( + # A name that cannot be registered: the sweep's load path puts the + # monorepo's rababa-configs in scope, where "default" exists. + expect { described_class.rababa("كتب", config: "no-such-config") }.to raise_error( Interscript::ExternalUtilError, - /No rababa config registered under 'default'/ + /No rababa config registered under 'no-such-config'/ ) end end From 42ec659e5149b6c0c99d00184ffe83eaf43decd1 Mon Sep 17 00:00:00 2001 From: Ronald Tse Date: Thu, 1 Oct 2026 00:31:40 +0800 Subject: [PATCH 7/7] fix: memoize the rababa diacritizer per config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A single global cache meant the first config built served every later call — the missing-config error could never fire once any rababa map had run (the CI sweep does), and two configs in one process silently shared one diacritizer. Keyed per config; reset_cache clears the map. --- lib/interscript/stdlib/functions/rababa_adapter.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/interscript/stdlib/functions/rababa_adapter.rb b/lib/interscript/stdlib/functions/rababa_adapter.rb index 1cf79d77..d700572d 100644 --- a/lib/interscript/stdlib/functions/rababa_adapter.rb +++ b/lib/interscript/stdlib/functions/rababa_adapter.rb @@ -1,7 +1,7 @@ class Interscript::Stdlib module Functions class RababaAdapter - @rababa_diacritizer = nil + @rababa_diacritizers = {} @mutex = Mutex.new class << self @@ -18,7 +18,7 @@ def reverse(output, config: nil) end def reset_cache - @mutex.synchronize { @rababa_diacritizer = nil } + @mutex.synchronize { @rababa_diacritizers.clear } end private @@ -26,7 +26,7 @@ def reset_cache def diacritizer_for(config_key) require_rababa! @mutex.synchronize do - @rababa_diacritizer ||= build_diacritizer(config_key) + @rababa_diacritizers[config_key] ||= build_diacritizer(config_key) end end