Skip to content

ci: validate every entry when the validator itself changes - #1285

Merged
MDA2AV merged 1 commit into
mainfrom
ci/validate-all-frameworks
Aug 24, 2026
Merged

ci: validate every entry when the validator itself changes#1285
MDA2AV merged 1 commit into
mainfrom
ci/validate-all-frameworks

Conversation

@MDA2AV

@MDA2AV MDA2AV commented Aug 23, 2026

Copy link
Copy Markdown
Owner

Opens a full validation sweep over every enabled entry, and fixes the gap that made one impossible.

The gap

validate.yml triggers on scripts/validate.sh, then builds its framework list from changed frameworks/** paths:

changed=$(git diff --name-only origin/$BASE...HEAD | grep '^frameworks/' | cut -d'/' -f2 | sort -u)

A PR that only touches the validator matches the trigger and then validates nothing — the list is empty and both jobs are skipped by if: ... != '[]'.

That is not hypothetical. It happened when the static staleness probe landed in #1267, and again when it was hardened in #1284. Both changed what every entry is checked against; neither ran a single entry.

The change

  • A validator change (scripts/validate.sh, scripts/validate-ws.py, or this workflow) now covers every enabled entry.
  • The workflow file joins the trigger paths, since editing it can change what validation means just as much as editing the script. That also makes this PR self-demonstrating: it validates everything.
  • Adds workflow_dispatch taking an entry name, a comma-separated list, or all, so a sweep can be run on demand without inventing a commit.

Scale

185 enabled entries — 177 on the matrix (GitHub's cap is 256) and 8 compose entries running sequentially on the self-hosted box.

Timeouts were sized for the handful of entries a normal PR touches, so both are raised:

job was now why
matrix 30 min 60 min a sweep reaches entries that build from source on a four-core hosted runner — h2o, the LTO Rust entries, Swift
self-hosted 90 min 300 min one sequential loop now covers all eight compose entries, not one or two

What to expect from this run

This is the first sweep against the hardened static probe, so it is also the blast-radius measurement for #1284. From a 12-entry local sample, no entry that passed the old probe fails the new one — every failure below already fails on main:

old probe new probes
bun, caddy, elysia, hono-bun, h2o-mruby, slimeweb, genhttp-11-ioxide PASS PASS
humming-bird, frankenphp-trueasync, genhttp-11, genhttp-11-kestrel, ioxide FAIL FAIL

Two things that sample already surfaced, both independent of the hardening:

  • genhttp-11 and genhttp-11-kestrel serve from GenHTTP's Assets.From, which never revalidates — 35s after the file changed on disk they still serve the original bytes. Their sibling genhttp-11-ioxide passes because IoxideFiles.From re-stats per request. Same framework, different handler.
  • ioxide is stale on both the identity and variant paths out to 35s, despite Program.cs:100 describing the plain path as read-per-request.

Expect some failures in this sweep to be infrastructure rather than entries — a four-core hosted runner is not the benchmark box, and a few builds are slow or flaky there. Worth reading the first run as a survey, not a verdict.

🤖 Generated with Claude Code

validate.yml triggers on scripts/validate.sh, then builds its framework list
from changed frameworks/ paths -- so a pull request that only touches the
validator matches the trigger and then validates nothing. That is what
happened when the static staleness probe landed in #1267 and again when it
was hardened in #1284: both changed what every entry is checked against, and
neither ran a single entry.

A validator change now covers every enabled entry (185: 177 on the matrix,
8 compose entries sequential on the self-hosted box). The workflow file
itself joins the trigger paths and the same rule, since editing the workflow
can change what validation means just as much as editing the script.

Also adds workflow_dispatch, taking an entry name, a comma-separated list, or
"all", so a full sweep can be run on demand without inventing a commit.

Timeouts raised to match what a full sweep reaches: 60 minutes on the matrix,
because entries that build from source on a four-core hosted runner (h2o, the
LTO Rust entries, Swift) do not finish in 30; and 300 on the self-hosted job,
which runs all eight compose entries in one sequential loop.
@MDA2AV
MDA2AV merged commit ae7522b into main Aug 24, 2026
111 of 179 checks passed
MDA2AV added a commit that referenced this pull request Aug 24, 2026
From the full validation sweep in #1285. Eleven entries fail `docker build`
and cannot be validated or benchmarked at all, so they are disabled and their
published results removed.

  araara, araara-standard   opam: no package matching hcs.0.16.1
  fasthttp, pico, veb,      the V compiler no longer builds from source:
  vanilla-epoll,            "C function C.open was already declared with a
  vanilla-h2c,              different signature". All seven pin different V
  vanilla-io_uring,         commits and fail identically, so it is the
  vanilla-ws                toolchain rather than any one pin.
  symfony-spawn-franken     composer exits 127, not present in its base image
  workerman-websocket       composer cannot unpack: "the zip extension and
                            unzip/7z commands are both missing"

Every one was reproduced locally, not taken from CI alone.

laravel and hyperf are deliberately not here. Both failed the sweep on
composer errors and both build clean on the benchmark box -- laravel first
try, hyperf earlier the same day at 53/0 -- and their three failures carried
three different signatures (CI exit 100, local exit 139, local heap
corruption inside composer). That is the flaky toolchain, not the entries,
and disabling them would have dropped 33 result keys of good data.
MDA2AV added a commit that referenced this pull request Aug 24, 2026
…1289)

From the full validation sweep in #1285. Nineteen entries fail the
exhaustive fragmentation check, which splits nine request shapes at every
byte offset and requires the server to answer correctly regardless of where
the request is cut across TCP segments.

Twelve fail 100% of offsets in the shapes they fail -- fishcake,
helidon-production, helidon-tuned, ioxide, libreactorng, may-minihttp,
minima, nestjs, ringzero, rust-epoll, vinyl-cache, web-framework-csharp --
and slim and dart-io fail 91% and 88%. Those cannot parse a request that
arrives in more than one read.

The remaining five fail a narrow slice rather than the whole shape: hical
9%, effinitive 3%, and bjoern, dart-zig and blackbull about 1% (blackbull is
a single offset out of 140). They are included because the instruction was
every entry that fails the check, but they are a different kind of defect
from the twelve above -- an edge case at one split point rather than a
parser that cannot reassemble at all -- and are the ones to revisit first if
this looks too broad.
MDA2AV added a commit that referenced this pull request Aug 24, 2026
… entries that fail them (#1290)

Two changes from the sweep in #1285.

The profiles stop feeding the framework composite. static and static-tls go
to scored=False, which is the shape Pipelined already has: still measured,
still shown, faded on the board, out of the sum. infraScored stays True --
serving files fast from a tuned cache is what a proxy is for, and that league
is unaffected. engineScored was already False.

CATALOG is the single source: the board reads it through data.js and the
badge port reads it through _scored_for(), so the two scoring implementations
cannot disagree about this and check_badge_parity.js stays green.

Twelve entries fail validation on static and on nothing else, and are
flagship, emerging or experimental. They lose the two profiles from their
tests array and the 69 result keys those profiles produced:

  django, fletch, genhttp-11, genhttp-11-kestrel, genhttp-kestrel,
  mq-bridge, mq-bridge-py, pyronova, rage, rails, true-async-server, userver

Nothing else is touched: every other profile keeps its results, and
static-h2 and static-h3 are left alone -- the failures were on the H1 pair.

The entries stay enabled, since static was their only failure.
MDA2AV added a commit that referenced this pull request Aug 24, 2026
)

The last group from the sweep in #1285: twelve entries that fail checks
other than the static probe, so neither #1288 (build), #1289 (fragmentation)
nor #1290 (static reference-only) covers them.

  deno-websocket, fleck        reject non-upgrade GET /ws
  node-websocket               text echo, multi-message, dies during the run
  dogrider                     binary echo
  sark                         upload chunked, upload truncated body,
                               StreamSum over h2+TLS
  swerver                      HTTP/2 negotiation and h2c prior-knowledge
  wtx-grpc, wtx-grpc-tls       GetSum over h2c / h2+TLS
  iris, typev                  upload chunked, plus static
  zio-http, zix                upload chunked

Each fails something the entry claims to implement, so the numbers it
publishes for those profiles do not describe a working server.

zio-http is the only flagship here and the only one whose failure is a
single profile -- upload chunked. It is included because the instruction was
the remaining failures, but it is the one worth a second look before this
merges: it loses 23 result keys over one check.
@nigoroll

Copy link
Copy Markdown
Contributor

I am coming from #1289 and am surprised. Could you please help me understand which validation is failing and where I can find more information about it?

@MDA2AV

MDA2AV commented Aug 24, 2026

Copy link
Copy Markdown
Owner Author

I am coming from #1289 and am surprised. Could you please help me understand which validation is failing and where I can find more information about it?

Of course, you can find details here https://www.http-arena.com/docs/test-profiles/h1/isolated/baseline/validation/ last point "Exhaustive TCP fragmentation" this is a added validation step taken from https://github.com/uNetworking/uWebSockets/blob/master/tests/fragment_test.ts modified to also include query parameters.

Run details on Vinyl Cache here https://github.com/MDA2AV/HttpArena/actions/runs/32719610781/job/97408097735 from 1904 onwards

@nigoroll

Copy link
Copy Markdown
Contributor

Of course, you can find details here

Excellent, thank you for your help!

@MDA2AV

MDA2AV commented Aug 24, 2026

Copy link
Copy Markdown
Owner Author

Excellent, thank you for your help!

Sorry for the trouble, we are still not 100% sure if some validation failures are false positives hence the "temporary", as we keep adding more validation points. Would be great if you could look into it.

@nigoroll

Copy link
Copy Markdown
Contributor

we are still not 100% sure if some validation failures are false positives

This is a great attitude, but in the case of Vinyl Cache I can already confirm that it is real and a regression: https://code.vinyl-cache.org/vinyl-cache/vinyl-cache/issues/4575

I love your project already.

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.

2 participants