Class: Bundler::Spinel::Verifier
- Inherits:
-
Object
- Object
- Bundler::Spinel::Verifier
- Defined in:
- lib/bundler/spinel/verifier.rb
Overview
The ‘verified` rung: differential testing. Runs a smoke program that exercises the gem once under CRuby and once compiled by Spinel, and compares stdout. This is the only signal that catches Spinel’s silent miscompiles (local-var-name collapse, Int-0-as-nil) — they emit no warning and exit 0, so the cheap probe can’t see them, but a differential run does: CRuby and the miscompiled binary diverge.
match, behaviour smoke -> verified (CRuby and Spinel agree on real use)
match, require-only -> loaded (loads+runs identically; logic untested)
mismatch -> rejected (reason: miscompile, with a short diff)
no build -> rejected (reason: build-error / run-error)
The smoke is the unit of trust. The require-only default smoke only proves the gem loads identically (‘loaded`); pass `–smoke FILE` (a snippet that drives the gem’s API and prints deterministic output) to actually verify behaviour (‘verified`). Verification is only as good as the smoke — which is why it’s opt-in and human-supplied. (A ‘loaded` gem can still silently miscompile in logic the require-only smoke never ran — observed in practice.)
Constant Summary collapse
- HARNESS =
"__spinel_verify.rb".freeze
Instance Method Summary collapse
-
#initialize(engine, ledger) ⇒ Verifier
constructor
A new instance of Verifier.
-
#verify(gem_name, version, dir, smoke: nil, full: false) ⇒ Object
full: when true, the harness force-requires every .rb under the gem’s lib/ (not just the entrypoint) before the smoke body.
Constructor Details
#initialize(engine, ledger) ⇒ Verifier
Returns a new instance of Verifier.
26 27 28 29 |
# File 'lib/bundler/spinel/verifier.rb', line 26 def initialize(engine, ledger) @engine = engine @ledger = ledger end |
Instance Method Details
#verify(gem_name, version, dir, smoke: nil, full: false) ⇒ Object
full: when true, the harness force-requires every .rb under the gem’s lib/ (not just the entrypoint) before the smoke body. This defeats the ‘autoload`/lazy-`require` masking that lets a constant-only smoke pass `verified` while the gem’s real surface (client/transport/serialization) never compiled — the qdrant-ruby spike (spinelgems#4). The verdict vocabulary is unchanged; the probe is tagged ‘verify-full` so the whole-surface signal stays distinguishable in the ledger from the entrypoint-only `verify`.
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/bundler/spinel/verifier.rb', line 39 def verify(gem_name, version, dir, smoke: nil, full: false) @engine.ensure! harness = File.join(dir, HARNESS) File.write(harness, harness_source(gem_name, dir, smoke, full)) ruby_out, ruby_err, ruby_ok = run_ruby(harness, dir) spin_out, spin_err, spin_ok = run_spinel(harness) verdict, reasons = classify(ruby_ok, spin_ok, ruby_out, spin_out, spin_err, behavior: !smoke.nil?) # Tag *why* (the spinelgems#4 usability rubric) so a failure says what it'd # take, not just "rejected". Prepended to reasons as `rubric:<tag>`. unless verdict == "verified" || verdict == "loaded" || verdict == "clean" reasons = ["rubric:#{rubric(ruby_ok, ruby_err, spin_ok, spin_err, ruby_out, spin_out, gem_name)}"] + reasons end @ledger.record(@ledger.build( gem: gem_name, version: version, rev: @engine.rev, verdict: verdict, reasons: reasons, probe: full ? "verify-full" : "verify" )) ensure File.delete(harness) if harness && File.exist?(harness) end |