Skip to content

👷 Validate the Rust host bridge in CI: guards, published gem, and dep-heads - #68

Merged
pboling merged 2 commits into
mainfrom
ci/rust-host-bridge-validation
Sep 14, 2026
Merged

pboling merged 2 commits into
mainfrom
ci/rust-host-bridge-validation

Conversation

@pboling

@pboling pboling commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Problem

The Rust host bridge specs added on 2026-09-10/11 (tree_haver RustTslp, ast-template and ast-merge-git RustHostProvider) require structuredmerge_host_prototype. That gem lives in the separate structuredmerge-rust repo (packages/ruby, a native rb_sys extension) and is not published. CI never provides it, so those suites fail whenever they are selected (for example #64, and main at c9f96be / 46d9dd8).

Policy

CI must reveal when common end-user paths are broken, and must not claim something works for end users when it does not. A gap we can close ourselves by installing a gem we control is not a failure; a gem that will not load for users on standard Rubies is.

Changes

Guard the common case (host not present)

Skip the ungated host specs when the host is unavailable, using the patterns other host specs already use:

  • tree_haver rust_tslp_language_matrix_spec.rb, rust_tslp_json5_contract_spec.rb: skip RustTslp.unavailable_reason unless RustTslp.available?
  • ast-template rust_host_provider_spec.rb: .available? example skips only when the host library cannot be loaded; host-backed examples are in a guarded context
  • ast-merge-git real_git_integration_spec.rb: explicit Rust selector examples skip unless RustHostProvider.available?

current and coverage: the published host gem

The 12 Gemfiles that carry the STRUCTUREDMERGE_RUST_DEV host block gain a CI-only switch: with STRUCTUREDMERGE_RUST_HOST_PUBLISHED=true (and no host already declared), they require the published structuredmerge_host_prototype (~> 0.2). Local bundles are unchanged.

  • Gem suite (current) sets the switch, so it represents what is available today: it fails at dependency resolution until the gem is released.
  • New Coverage job (changed-gem matrix) sets the switch and runs kettle-test with coverage on, using each gem's mise.toml thresholds, for maximum coverage once the gem is published. It fails like current until then.

dep-heads: what end users will install, built from structuredmerge-rust HEAD

  • New Rust host @ HEAD job clones structuredmerge-rust HEAD and builds the x86_64-linux platform gem exactly as release-ruby-host.yml does (Ruby 4.0, Alef, CARGO_FEATURE_LINK_RUBY=1), then uploads it.
  • New Deps @ HEAD job (changed-gem matrix, standard rv pre-built Ruby via setup-ruby-flash) installs that gem as a path gem in a CI-only dep_heads.gemfile: the gem's Gemfile plus gemfiles/modular/runtime_heads.gemfile, with HEAD declarations replacing the Gemfile's released declarations of the same gems (as the dep-heads appraisal does). KETTLE_FAMILY_BUNDLE_GEMFILE points spec subprocesses (for example the ast-merge-git Git drivers) at the same bundle.
  • Verify Rust host loads: for gems that declare the host, require "structuredmerge_host_prototype" must succeed under the dep-heads bundle before specs run, so the job fails instead of letting host specs skip.

Check now includes the three new jobs.

Current finding: release host gems do not load on rv Rubies

The first CI run showed the release-style build links the native library against libruby.so.4.0 (readelf -d: NEEDED libruby.so.4.0, from CARGO_FEATURE_LINK_RUBY=1, structuredmerge-rust f970e1c). rv pre-built Rubies do not ship a shared libruby, so loading fails with libruby.so.4.0: cannot open shared object file, and the host specs silently skipped while Deps @ HEAD reported success. The same gem loads in a shared-libruby Ruby (e.g. mise). mise run ruby-package (structuredmerge-rust current.yml and local builds) does not link libruby, which is why local validation passed.

With the verify step, Deps @ HEAD now fails for the host gems until structuredmerge-rust's release build produces a gem that loads on rv Rubies. That is the intended end-user signal; this PR does not change the Rust side.

Verification

  • actionlint .github/workflows/current.yml: clean; YAML parses.
  • Published switch (tree_haver): flag on → Could not find gem 'structuredmerge_host_prototype (~> 0.2)'; flag off → resolves without the host.
  • Local, no host (fixtures linked as in CI): tree_haver 87/0 failures (12 pending), ast-template 74/0 (4 pending), ast-merge-git 95/0 (12 pending); pending = host guards.
  • Local, dep-heads composition with a mise run ruby-package host gem (no libruby link): tree_haver 87/0, ast-template 74/0, ast-merge-git 95/0 failures, host specs run.
  • CI run 34885659911: Gem suite and Coverage fail only for the 12 host gems with Could not find gem 'structuredmerge_host_prototype (~> 0.2)'; Rust host @ HEAD built structuredmerge_host_prototype-0.2.0-x86_64-linux.gem from structuredmerge-rust e44a2e3; Deps @ HEAD skipped host specs (libruby) — addressed by the verify step above.

Expected CI now: Gem suite and Coverage fail for the host gems (published gem unavailable); Rust host @ HEAD passes; Deps @ HEAD fails for the host gems (release host gem does not load on rv Ruby) and passes for the rest.

🤖 Generated with Claude Code

https://claude.ai/code/session_01P8aveNcdgPobyMrq8DkD9r

pboling and others added 2 commits September 14, 2026 15:14
…-heads

The Rust host bridge specs require structuredmerge_host_prototype, a native
gem from the separate structuredmerge-rust repo that is not published, so
tree_haver, ast-template and ast-merge-git suites failed whenever selected.

- Skip host-dependent examples when the host is unavailable (common case).
- Add a CI-only STRUCTUREDMERGE_RUST_HOST_PUBLISHED switch to the 12 host
  Gemfiles that requires the published gem. The Gem suite (current) and a new
  Coverage job set it, so they represent what is available today and fail
  until the gem is released.
- Add Rust host @ HEAD (builds the x86_64-linux platform gem from
  structuredmerge-rust HEAD) and Deps @ HEAD (runs each changed gem against
  that host plus its runtime_heads dependencies) jobs, and include all three
  new jobs in Check.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P8aveNcdgPobyMrq8DkD9r
CI must reveal when common end-user paths are broken. Build the Rust host
exactly as structuredmerge-rust's release workflow does (Ruby 4.0,
CARGO_FEATURE_LINK_RUBY), and before running specs require it under the
dep-heads bundle on the standard rv Ruby. The release build currently links
libruby.so.4.0, which rv pre-built Rubies do not provide, so host specs were
silently skipping while the job passed; it now fails for gems that declare the
host until the release build produces a gem that loads there.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P8aveNcdgPobyMrq8DkD9r
@pboling
pboling merged commit 9a6acf0 into main Sep 14, 2026
63 of 100 checks passed
@pboling
pboling deleted the ci/rust-host-bridge-validation branch September 14, 2026 20:06
pboling added a commit that referenced this pull request Sep 15, 2026
The Rust host switch from #68 declared the gem in two separate blocks,
which RuboCop flags as Bundler/DuplicatedGem. Merge them into one
if/elsif (local path wins, otherwise the published gem when
STRUCTUREDMERGE_RUST_HOST_PUBLISHED=true) in the remaining 11 Gemfiles.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P8aveNcdgPobyMrq8DkD9r
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