Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
9bfd78d
Preserve polymorphic owner types in through-association joins
dchuk Oct 4, 2026
a1b00e0
Load the ActiveJob runtime before `thread_state` in the ruby boot
MatheusRich Oct 4, 2026
93d2b16
Split a rebound method parameter onto a fresh local
thomasklemm Oct 4, 2026
fb6e158
Keep query keys and **rest forwarding on the wire
thomasklemm Oct 4, 2026
d0b415d
Gzip Campfire HTML the way Rails' Rack::Deflater does
thomasklemm Oct 4, 2026
4b806ea
Time Campfire's five HTTP routes against Rails, emit, and the ports
thomasklemm Oct 4, 2026
a538732
Gate Campfire seed copies and scrape CSRF as identity
thomasklemm Oct 4, 2026
686b951
Ground Hash and untyped Array compact_blank
thomasklemm Oct 5, 2026
39721a5
Lower turbo_stream collection option form
thomasklemm Oct 5, 2026
965d6b6
Take Relation#last_n as a SQL tail, not the whole history
thomasklemm Oct 5, 2026
8e64e63
Skip gzip on no-body statuses and unique wrk POST ids
thomasklemm Oct 5, 2026
d6f737e
Call the five-route Campfire HTTP bench a suite, not DHH
thomasklemm Oct 5, 2026
cbe6686
Cache gzip of identical Campfire HTML bodies
thomasklemm Oct 5, 2026
496d8be
Honor gzip q=0, reverse multi-column order, pin untyped ceilings
thomasklemm Oct 5, 2026
432dbb8
Merge pull request #424 from thomasklemm/thomasklemm/campfire-dhh-htt…
thomasklemm Oct 5, 2026
37da50b
Merge remote-tracking branch 'upstream/main' into thomasklemm/campfir…
thomasklemm Oct 5, 2026
a03fbed
Keep unsafe turbo_stream, splat, and rebind rewrites on the dynamic path
thomasklemm Oct 5, 2026
c41186f
Restore **rest only against defs that emit as **rest
thomasklemm Oct 5, 2026
cc2d138
Merge pull request #426 from thomasklemm/thomasklemm/campfire-spinel-…
thomasklemm Oct 5, 2026
89e6ca8
Gzip Campfire HTML outside the lock; skip fragment dups on CRuby
thomasklemm Oct 5, 2026
644d00f
Freeze MemoryStore strings so read_str cannot mutate the store
thomasklemm Oct 5, 2026
da1c6c1
Merge remote-tracking branch 'upstream/main' into thomasklemm/gzip-ca…
thomasklemm Oct 5, 2026
4dc7c21
Merge pull request #432 from thomasklemm/thomasklemm/gzip-cache-digest
thomasklemm Oct 5, 2026
7910a30
`super` in a model's own password writer reaches `has_secure_password…
bunnykong Oct 5, 2026
daf3ba3
README: mention the contributors' chat room (#416)
rubys Oct 5, 2026
444932b
Struct, Mutex and JSON::ParserError resolve again in emitted Ruby (#427)
bunnykong Oct 5, 2026
053cad3
Preserve relation receivers for find_by and find_by! (#431)
dchuk Oct 5, 2026
01cf474
A controller under `ActionController::API` dispatches (#338)
bunnykong Oct 5, 2026
c697f1e
`owner.<has_many>.new(attrs)` builds with the foreign key, like `buil…
alextakitani Oct 5, 2026
e1ad7ad
A model callback's `if:` / `unless:` becomes a guard instead of dropp…
alextakitani Oct 5, 2026
2b2deff
jbuilder: a collection with a block is an array of objects (#355)
eddygarcas Oct 5, 2026
4402133
jbuilder: `json.partial! @record` renders the record's partial (#356)
eddygarcas Oct 5, 2026
f5fb25a
Spinel refreshes prepared statement recency on cache hits (#380)
bunnykong Oct 5, 2026
756f948
Drain the blocking server's request body by bytes (#414)
gregmolnar Oct 5, 2026
5e151ad
Honor Vips.block_untrusted in find_load on libvips 8.14
thomasklemm Oct 5, 2026
05d120b
Merge remote-tracking branch 'upstream/main' into thomasklemm/vips-fi…
thomasklemm Oct 5, 2026
a407124
Install ruby-vips in the unit job for the find_load contract
thomasklemm Oct 5, 2026
b30f711
Honor Vips.block_untrusted in find_load on libvips 8.14 (#436)
thomasklemm Oct 5, 2026
bf844be
Relation answers reorder, skip_preloading! and preload_associations
rubys Oct 5, 2026
f21f1e5
A class method runs against the relation it is reached from
rubys Oct 5, 2026
c8646ce
A send whose selector every caller names dispatches statically
rubys Oct 5, 2026
b5e75a4
Account for the paging changes in two CI baselines
rubys Oct 5, 2026
734a5eb
Support small Ruby expression shapes (#197)
tim-tischler Oct 5, 2026
98b665e
Name a bundled gem in the Gemfile when the app requires it
MatheusRich Oct 5, 2026
9dfd6cf
Merge pull request #443 from MatheusRich/fix-csv-gemfile
rafaelfranca Oct 5, 2026
f1c3e6c
Run the Rails 8.2-era test shapes campfire's preview branch writes
rubys Oct 5, 2026
12bf080
Named binds reach the query, and records compare as Set members
rubys Oct 5, 2026
413a395
Parameters after a rest are popped off it
rubys Oct 5, 2026
bc7dcf8
Query assertions and SQL notifications run in emitted tests
rubys Oct 5, 2026
9d85f9f
offset(n).exists? looks past n; tests can turn caching on
rubys Oct 5, 2026
4f5a123
Model SELECTs quote identifiers the way Rails does
rubys Oct 5, 2026
db291e9
A view directory with a hyphen emits valid Ruby
Oct 5, 2026
f874088
Slim views are ingested and emitted
Oct 5, 2026
309e17c
An interpolation's percent literal keeps its braces inside the body
Oct 5, 2026
ea5e1e1
Merge pull request #440: Slim views are ingested and emitted
rubys Oct 5, 2026
32576fc
Merge pull request #447: A view directory with a hyphen emits valid Ruby
rubys Oct 5, 2026
45ca74c
Meta PR: YJIT-friendly Relation, view, ActionText, and Cache runtime …
thomasklemm Oct 5, 2026
f6c6997
CI ladder: idle drafts, ci:draft / ci:spinel, four unit shards (#453)
thomasklemm Oct 5, 2026
aaf447e
Skip CI gates when plan cancelled or needs cancelled
cursoragent Oct 5, 2026
eee7981
Fix attr_accessor vs def: last definition wins for typed ivars
cursoragent Oct 5, 2026
5fd0fb1
Clear Campfire preview Spinel blockers beside the ivar fix
cursoragent Oct 5, 2026
600982f
Keep model **kwrest beside keywords (valid Ruby/Spinel)
cursoragent Oct 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 26 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ on:
branches: [main]
pull_request:
branches: [main]
# Drafts default to fixture + unit; ci:full also expands drafts.
# Label/draft/head transitions cancel superseded PR runs via concurrency.
# Drafts stay dark until ci:draft / ci:spinel / ci:full. Ready PRs use the
# normal path planner; label and draft transitions cancel superseded runs.
types: [opened, synchronize, reopened, ready_for_review, converted_to_draft, labeled, unlabeled]
workflow_call:
inputs:
Expand Down Expand Up @@ -165,6 +165,7 @@ jobs:
# -------------------------------------------------------------------
generate-fixture:
needs: plan
if: ${{ contains(fromJSON(needs.plan.outputs.jobs), 'generate-fixture') }}
runs-on: ubuntu-latest
timeout-minutes: 30
env:
Expand Down Expand Up @@ -536,15 +537,16 @@ jobs:
# ingest / analyze / IR round-trip / per-target emit snapshot tests.
# -------------------------------------------------------------------
unit:
needs: generate-fixture
needs: [generate-fixture, plan]
if: ${{ contains(fromJSON(needs.plan.outputs.jobs), 'unit') && needs.generate-fixture.result == 'success' }}
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
# A failed shard must not cancel coverage or diagnostic output from others.
fail-fast: false
max-parallel: 3
max-parallel: 4
matrix:
shard: [0, 1, 2]
shard: [0, 1, 2, 3]
env:
# Share first-party split DWARF instead of copying it into every test
# executable. Integration sidecars are freed with their finished batch;
Expand Down Expand Up @@ -595,8 +597,17 @@ jobs:
# `scripts/campfire-test-bcrypt.rb`, which `require "bcrypt"`. A
# runner without the gem fails those tests; a dev box that has it
# hides the gap.
#
# `tests/vips_loader_policy.rs` boots an emitted variant app and
# asks `Vips.vips_foreign_find_load` after the app's loader policy.
# That require is ruby-vips over the system libvips: without both,
# the test dies on `cannot load such file -- vips` while a machine
# that already has the gem hides the gap. CRuby binds through FFI,
# so libvips42 is enough; headers are the compiled Spinel job.
- name: System libvips for the emitted ruby-vips processor
run: sudo apt-get update -qq && sudo apt-get install -y -qq libvips42
- name: Install gems used by emitted Ruby and Campfire harness tests
run: gem install sqlite3 bcrypt --no-document
run: gem install sqlite3 bcrypt ruby-vips --no-document
# Bounded build → run → free waves: every --all-targets identity still
# goes through Cargo (lib, bins, each integration target). Finished
# integration executables and their own split-DWARF sidecars are freed
Expand Down Expand Up @@ -3454,8 +3465,13 @@ jobs:
compact-required:
# compare and browser-smoke-typescript stay in needs so a full/main run
# can still require them. Unselected PR skips must not fail this gate.
# Do not use always()/cancelled() here: always() still schedules the job
# on cancel-in-progress (run 37366559376: plan cancelled, this gate sat
# QUEUED and held validation-push-refs/heads/main). Require a successful
# plan and no cancelled need so superseded runs skip immediately instead
# of queuing. Custom if still runs when other needs fail or skip.
needs: [plan, generate-fixture, unit, build-roundhouse, store-check, compare, compare-ruby, browser-smoke-typescript, campfire-conformance, campfire-compare]
if: always()
if: ${{ needs.plan.result == 'success' && !contains(needs.*.result, 'cancelled') }}
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
Expand All @@ -3475,7 +3491,9 @@ jobs:
ci-summary:
name: CI summary
needs: [plan, generate-fixture, unit, build-roundhouse, store-check, compare, compare-ruby, browser-smoke-typescript, campfire-conformance, campfire-compare, compact-required, compare-extra, compare-jruby, build-wasm, browser-smoke-ide, build-site, smoke, writebook-inventory, build-spinel, framework-tests-spinel, build-campfire-compare-spinel, campfire-compare-spinel, campfire-db-differential-spinel, toolchain-spinel, compare-spinel, smoke-spinel, build-campfire-archive, smoke-campfire, smoke-campfire-docker, archive-results, assemble-site]
if: always()
# Same cancel/concurrency rule as compact-required: skip when plan did not
# succeed or any need was cancelled, so the job is never left QUEUED.
if: ${{ needs.plan.result == 'success' && !contains(needs.*.result, 'cancelled') }}
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,12 @@ Include a repro, a regression test, and what you verified. Start with
see [CI for contributors](docs/ci/README.md) to request full/fresh validation
and interpret advisory results. PR validation never deploys Pages.

Contributors also have a chat room. It is Campfire, compiled by
Roundhouse and running on Spinel, so everyone in it is also testing
it. The invite link isn't posted publicly; if you have opened an issue
or pull request and would like to join, say so there and you'll get an
invite privately.

## License

Dual-licensed under either of
Expand Down
9 changes: 5 additions & 4 deletions bench/campfire/suite-causes.json
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,8 @@
"owner": "gem",
"match": "undefined method '(write_to_buffer|add|cast|black)' for unknown",
"match_test": "VipsLoaderPolicyTest#",
"note": "The loader-policy test does not ship fixture images; it MAKES one per format with libvips' arithmetic API and writes it to a buffer with the format's suffix. The ruby family runs the real ruby-vips gem, so on that lane these tests reach the policy question (`vips-policy-not-applied-in-tests`). The compiled lane runs spinel-ruby-vips, the spin-index package this project wrote for what campfire's SERVER calls (`new_from_buffer`, `thumbnail_image`, `write_to_buffer`, the loader lookup and the two `block` policies) — `Image.black`, `#add` and `#cast` are not on it, the chain answers unknown and its last call is what the test reports. Every one of the seventeen stops here, before the file's first assertion about a loader. Serving it is three methods in the package (spinel-ruby-vips, matz/spin-index), not the emit; until then the compiled lane says nothing about the policy.\n\nTHE WHOLE FILE IS BEHIND THIS NOW (seventeen tests, not five): `tempfile-stdlib` was the wall in front of it and is served, so every test in the file reaches the synthesis chain and stops.\n\nIT IS OUT OF THE PACKAGE'S DECLARED SUBSET, which is why owner is gem rather than compiler and why this is not a small fix. spinel-ruby-vips says so in its own README: \"no VipsImage handle ever crosses the FFI boundary\" — an Image there is a VALUE (the source bytes, the header dimensions, at most one pending thumbnail) and every entry point is a complete load-to-encode operation. `Image.black` has no source bytes, and `#add`/`#cast` are band arithmetic, which the README lists among what the port does not provide.\n\nWHAT SERVING IT WOULD LOOK LIKE, so the next person does not have to re-derive it: the value could carry a pending SYNTHESIS the way it already carries a pending thumbnail — `black` records (w, h), `add` a constant, `cast` a band format, and `write_to_buffer` calls one new C entry point that does vips_black + vips_linear + vips_cast + vips_image_write_to_buffer in a single call. That keeps the no-handle rule the package is built on. It is a package change plus a publish and a spin-index PR, and CI pulls the PUBLISHED package, so the number here does not move until that lands.\n\nWHAT IT WOULD BUY, stated plainly: the loader policy these tests measure is already verified on the interpreted lane (16 of 16, and the policy itself was a live server gap fixed the same day). This is the compiled lane being able to ask the same question, which is worth having and is not new coverage of the app."
"note": "The loader-policy test does not ship fixture images; it MAKES one per format with libvips' arithmetic API and writes it to a buffer with the format's suffix. That chain (`Image.black(8, 8).add(128).cast(\"uchar\").write_to_buffer(\".png\")`) used to stop the compiled lane because spinel-ruby-vips had no live VipsImage handle. The package now provides those methods, so every test in the file reaches the policy question.\n\nTHE CONSTRUCTION WALL IS DOWN. A later suite that still fails this file is asking find_load, not asking for black/add/cast — see `vips-policy-not-applied-in-tests`.",
"at_zero": "served 2026-10-05: spinel-ruby-vips grew Image.black / #add / #cast / write_to_buffer, so the policy file reaches find_load instead of stopping on unknown. Remaining find_load nils on libvips 8.14 are vips-policy-not-applied-in-tests, not this construction wall."
},
{
"slug": "tempfile-stdlib",
Expand All @@ -142,12 +143,12 @@
},
{
"slug": "vips-policy-not-applied-in-tests",
"title": "The libvips loader policy was applied NOWHERE on the ruby lane — not the test process, and not the server either",
"title": "find_load on libvips 8.14 still names a blocked Magick/Svg loader after the app's Vips.block_untrusted policy is applied",
"owner": "compiler",
"match": "assert_(nil|match) failed",
"match_test": "VipsLoaderPolicyTest#test_denies_",
"note": "campfire's config/initializers/vips.rb (`Vips.block_untrusted(true)`, `Vips.block(\"VipsForeignLoadOpenslide\", true)`) is lifted at ingest onto the Application reopen and applied at the bottom of runtime/active_storage_processor.rb, at load, once — which is what the initializer does.\n\nMIS-TRIAGED UNTIL 2026-09-22, and the mis-triage is the lesson. This cause read \"not applied IN TESTS\", owner harness, on the reasoning that the emitted test_helper never required that file while the server's boot.rb did. The first half was true. The second was read off boot.rb's own COMMENT — \"After config/application — it applies the app's lifted loader policy at load\" — which the CRuby overlay's boot.rb contradicted forty lines up: it required the processor at line 129 and config/application at line 200. So the policy read the framework default `false` and applied NOTHING, on the SERVER, while `Rails.application.vips_block_untrusted` answered true the whole time. An emitted campfire decoded every exotic format its own initializer blocks. Measured, not inferred: a full `require_relative \"boot\"` then `Vips.vips_foreign_find_load` on a BMP answered \"VipsForeignLoadMagick7File\" where it must answer nil.\n\nServed 2026-09-22: the overlay's require moved below config/application (the spinel scaffold's boot.rb had it right all along — only the overlay was wrong), and the harness requires the same file after app/models so a test process measures the server that exists. 16 of 17; the seventeenth calls Minitest's `skip` when the local libvips has no niftiload, which is `test-harness-surface`.",
"at_zero": "served 2026-09-22 (16/17, the last one is `skip`); the rule stays as the tripwire for the policy going unapplied again — and it is worth keeping loud, because the failure it catches is a security control that is configured, reported as on, and off."
"note": "campfire's config/initializers/vips.rb (`Vips.block_untrusted(true)`, `Vips.block(\"VipsForeignLoadOpenslide\", true)`) is lifted at ingest onto the Application reopen and applied at the bottom of runtime/active_storage_processor.rb, at load, once — which is what the initializer does.\n\nTHAT HALF WAS SERVED 2026-09-22 (boot order). The remaining four fails on the compiled lane were a different question: `vips_foreign_find_load` on libvips 8.14 still RETURNS `VipsForeignLoadMagickFile` / `VipsForeignLoadSvgFile` after `vips_block_untrusted_set(TRUE)`, even though `Image.new_from_file` / magickload / svgload raise \"operation is blocked\". 8.15+ skips BLOCKED classes in `vips_foreign_map`, so find_load answers nil. Direct `openslideload` etc. were already refused (those tests pass). The processor wraps find_load through `VipsExt.sp_vips_find_load` (not wrapping the Ruby method in place: that re-enters on the spinel package) and hides Magick/Svg/Pdf/… prefixes plus any class `Vips.block` named.\n\nThe wrap is the Rails-app contract, not a Campfire rewrite: any initializer that sets that policy is entitled to the same find_load answer load would give.",
"at_zero": "served 2026-10-05: the processor wraps Vips.vips_foreign_find_load through VipsExt so a blocked loader is not selected (8.15+ already skips BLOCKED classes in vips_foreign_map; 8.14 does not). 15/15 on this orb's libvips 8.14.1 after the wrap, two skips for loaders the host never built."
},
{
"slug": "test-harness-surface",
Expand Down
54 changes: 42 additions & 12 deletions docs/ci/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,30 @@ The workflows and their tests own implementation details, not this handbook.

## What runs

Ready PRs run a Ruby floor: fixture preparation, unit tests, Store analysis,
the CRuby comparison against Rails, and Campfire conformance/comparison.
Three unit shards cover all package test targets in bounded batches; ignored
integrations need selected toolchain lanes. Framework and toolchain suites
also run inside comparison jobs, not necessarily as standalone checks.
Coverage is a ladder. The planner (`scripts/ci-plan.py`) chooses jobs from
the PR draft state, labels, and changed paths:

| State | What runs |
|---|---|
| **Draft**, no CI label | Nothing selected — build and review without runners |
| **Draft** + `ci:draft` | Fixture preparation and unit shards only |
| **Draft or ready** + `ci:spinel` | Ruby floor plus the full Spinel suite; no other language SDKs |
| **Draft or ready** + `ci:full` | Full validation (all targets, WASM, Writebook, Spinel) |
| **Ready** (non-draft), no special label | Path-selected coverage on the Ruby floor |

Ready PRs without a special label run a Ruby floor: fixture preparation, unit
tests, Store analysis, the CRuby comparison against Rails, and Campfire
conformance/comparison. Four unit shards cover all package test targets in
bounded batches; ignored integrations need selected toolchain lanes. Framework
and toolchain suites also run inside comparison jobs, not necessarily as
standalone checks.

That floor is the merge claim for ordinary analyzer, lowerer, and runtime
work: the Ruby shape runs, and Campfire still matches Rails. Crystal, Go,
Swift, Kotlin, C#, Elixir, Python, JRuby, Rust, TypeScript, WASM, Writebook,
and Spinel do **not** start on that path unless the diff owns them or a
maintainer applies `ci:full`. Extra-language failures after merge are a
main ledger, not a reason to block the next Ruby PR.
maintainer applies `ci:full` / `ci:spinel`. Extra-language failures after merge
are a main ledger, not a reason to block the next Ruby PR.

Selected lanes start once their inputs are ready, without waiting for unit
tests to pass. Campfire consumes an independently built same-run debug compiler.
Expand All @@ -36,18 +48,25 @@ includes both sides of a rename, and expands only when the trees cannot be
identified. A newer main than the event's `base.sha` is not unknown input.
See the run's **plan** job for its selected jobs and reasons.

Drafts default to fixture preparation and unit tests only. `ci:full` overrides
that floor and runs full validation while the PR is still a draft.
Documentation-only PRs still receive checks; changes to the rendered user
Drafts stay idle until `ci:draft`, `ci:spinel`, or `ci:full` is applied.
Marking a PR ready-for-review leaves the draft idle path and runs the normal
ready planner. `ci:full` / `ci:spinel` also work while the PR is still a draft.
Stacked labels prefer the broader lane: `ci:full` > `ci:spinel` > `ci:draft`.
Documentation-only ready PRs still receive checks; changes to the rendered user
guide also select site/browser coverage.

Pushes to canonical `main` run full validation and cancel a superseded SHA
on the same ref. Extra-target red on that run is follow-up work on main,
not a merge gate for later Ruby PRs. The four-hour scheduled cycle remains
the publication and floating-pin catch-up.

## Request full or fresh validation
## Request full, Spinel, or fresh validation

- **Slim draft CI:** apply `ci:draft` on a draft PR (fixture + unit only).
- **Spinel-focused CI:** apply `ci:spinel` on a draft or ready PR. Runs the
Ruby floor plus every Spinel job; skips Crystal/Go/Swift/… SDKs, WASM, and
Writebook. Prefer this over `ci:full` when only the native/Ruby-family lane
matters.
- **More coverage:** ask a maintainer to apply `ci:full` to a ready or draft PR. The
label triggers a full run of the current PR merge tree and keeps full
coverage on later pushes. A comment requesting it is not itself a trigger.
Expand All @@ -64,7 +83,18 @@ the publication and floating-pin catch-up.
Superseded PR runs cancel. Push-to-main full runs also cancel a superseded
SHA; the scheduled full-ci lock does not. Neither dependency-cache hits nor
restored fixture source are test results; check the job summary for any
explicitly reused execution evidence.
explicitly reused execution evidence. The compact and summary gates run only
when `plan` succeeded and no needed job was cancelled. That skips them on
cancel-in-progress instead of leaving `always()` gates QUEUED (which holds
the concurrency lock ahead of the replacement run).

A draft with no CI label looks almost empty on purpose (`plan` + gates only;
everything else skipped). Marking the same PR ready does **not** keep that
idle selection: path policy applies, and changes under `.github/` or
`scripts/ci-plan.py` expand to full validation. If the Checks tab still shows
only cancelled jobs after `ready_for_review`, look for a stuck superseded run
on the `validation-pull_request-<n>` concurrency group rather than assuming
the ready planner selected nothing.

## Read results honestly

Expand Down
21 changes: 18 additions & 3 deletions docs/pipeline/runtime.md
Original file line number Diff line number Diff line change
Expand Up @@ -814,7 +814,14 @@ process keeps one record per digest scheme. Purging a blob purges its
variant records, their image blobs and their files first. The app's
libvips loader policy (`Vips.block_untrusted(true)`, `Vips.block(op,
true)` in an initializer) is lifted at ingest onto the `Rails
::Application` reopen and applied when the processor loads. `variable?`
::Application` reopen and applied when the processor loads. The
processor also wraps `Vips.vips_foreign_find_load` so a blocked
loader is not selected: libvips 8.15+ skips BLOCKED classes in
`vips_foreign_map`, but 8.14 still names Magick/Svg after
`block_untrusted` even though load itself raises. The wrap reaches
the C finder through `VipsExt` (the spinel package's binding, or an
FFI stand-in on the gem). Wrapping the Ruby finder in place re-enters
the wrapper on the spinel package. `variable?`
is Rails' content-type question, answered from `variable_content_types`
minus what the app's initializer subtracts (ingest lifts
`config.active_storage.variable_content_types -= %w[…]` onto the same
Expand Down Expand Up @@ -2908,8 +2915,16 @@ header. Grouped by cause, largest first:
which every Rails response carries. Without `X-Frame-Options` any
site can frame a signed-in campfire page. The one to close first.
- **The app's own `config.ru` middleware is not applied.** campfire's
`config.ru` says `use Rack::Deflater`; no response of ours is gzipped
(65) and none varies on `Accept-Encoding` (71).
`config.ru` says `use Rack::Deflater`. The CRuby overlay now wraps
`Main.run_rack` in `GzipCache` (Static stays outside so `/cable`
hijack is never compressed; CSS/JS stay identity; identical HTML is
not deflated on every request). spinel tep gzips inline bodies when
`Accept-Encoding` includes gzip, from a cache keyed by SHA-256 of
the identity body (CRuby keys by the body itself — MRI's string
hash is cheaper than SHA-256 here). Gzip runs outside the lock on
both lanes.
Re-run `scripts/campfire-http-shape` before treating the 65
Content-Encoding misses as current.
- **Rails' `Rack::ETag` / `Rack::ConditionalGet` are absent**: no weak
ETag on a 200 (52), no `Cache-Control: max-age=0, private,
must-revalidate`, so a revisit is 200 where Rails answers 304 (10-14).
Expand Down
Loading
Loading