Skip to content

Espalier std lib - #1

Merged
cuzzo merged 419 commits into
masterfrom
Espalier-STDLib
Aug 1, 2026
Merged

Espalier std lib#1
cuzzo merged 419 commits into
masterfrom
Espalier-STDLib

Conversation

@cuzzo

@cuzzo cuzzo commented Jul 31, 2026

Copy link
Copy Markdown
Owner

No description provided.

cuzzo and others added 30 commits July 27, 2026 12:15
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
cuzzo and others added 4 commits July 31, 2026 22:03
The repository is private and GitHub Actions is gone, so ci.yml either
executes on this machine or it does not execute at all.

This reads the workflow rather than restating it -- jobs, `needs` order,
`if` conditions, matrices and steps all come from the YAML, so a job
added to CI is a job this runs. Each is pinned to two CPUs, which is
what a hosted runner has, and they run in parallel up to `--jobs`.

What it does not reproduce is the runner image. Steps that provision one
are skipped, because the toolchains are already installed and `sudo`
here would want a password. Steps that talk to GitHub -- SARIF upload,
Codecov, Bencher -- are no-ops because there is nothing to talk to.
Caches report a hit when the path they name is already populated, which
is what the install-unless-cached steps actually ask. Artifacts are
real, so a job that consumes another's binaries gets them.

  tools/ci_local.rb --list
  tools/ci_local.rb --job sorbet
  tools/ci_local.rb --jobs 8

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uf8MndFjJ9y6eUZzSySSwa
A hosted runner gets a fresh disk per job; here every job shares one.
Each cargo-llvm-cov job builds its own multi-gigabyte target tree, and
six of them in parallel filled 154GB -- which surfaces as
""rustc-LLVM ERROR: IO failure on output stream"" and a linker bus error,
neither of which reads as "out of disk".

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uf8MndFjJ9y6eUZzSySSwa
`srb tc` segfaults on this repository as soon as a developer has fetched
stdlib sources: `.cache/` holds 7,837 Ruby files -- 776MB of CRuby 3.2.3
and Kotlin stdlib source -- and `--dir .` walked all of it. Sorbet dies
on the standard library's own source, with `Segmentation fault` and
nothing else to go on.

CI never saw it because a fresh checkout has no `.cache/`, which makes
this exactly the kind of failure that only ever happens to a person.

`.cache/` was not gitignored either, so it was one `git add -A` away
from being committed. Nor were `.ci-local/`, `kcov-bin/`, or the
`nil-kill-golden*` and `nil-kill-missing-evidence*` directories that
crashed spec runs leave in the repository root -- 34 of them had
accumulated.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uf8MndFjJ9y6eUZzSySSwa
A hosted runner checks out fresh, so SimpleCov starts with nothing to
merge. Here every job shares one working tree, and SimpleCov merges
whatever an earlier job left in coverage/ -- which is how the Test Miser
step that tracks one directory came to report 49% line coverage over
75,000 lines of the whole repository and fail its 95% gate.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uf8MndFjJ9y6eUZzSySSwa
@github-advanced-security

Copy link
Copy Markdown

You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool.

What Enabling Code Scanning Means:

  • The 'Security' tab will display more code scanning analysis results (e.g., for the default branch).
  • Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results.
  • You will be able to see the analysis results for the pull request's branch on this overview once the scans have completed and the checks have passed.

For more information about GitHub Code Scanning, check out the documentation.

Comment on lines +23 to +24
when /\ACollections\/DictionaryEntry#/
"System.Runtime"
unless File.file?(scip_java) && Digest::SHA256.file(scip_java).hexdigest == SCIP_JAVA_SHA256
temporary = "#{scip_java}.download-#{Process.pid}"
url = "https://github.com/scip-code/scip-java/releases/download/v#{SCIP_JAVA_VERSION}/scip-java-v#{SCIP_JAVA_VERSION}"
URI.open(url) { |input| File.open(temporary, "wb") { |file| IO.copy_stream(input, file) } }
end

temporary = "#{destination}.download-#{Process.pid}"
URI.open(url) { |input| File.open(temporary, "wb") { |output| IO.copy_stream(input, output) } }
@github-actions

github-actions Bot commented Jul 31, 2026

Copy link
Copy Markdown

Diff Coverage Buckets

Diff base: origin/master...HEAD

bucket files additions deletions line cov additions branch cov additions
total 725 91496 11343
compiler/ruby/**/*.rb public functions 0 0 0
compiler/ruby/**/*.rb private functions 0 0 0
compiler/ruby/**/*.rb OTHER 0 0 0
zig/**/*.zig prod 0 0 0
compiler/spec/ 3 13 13 not tracked not tracked
transpile-tests/ 0 0 0
tools/ 14 565 42 not tracked not tracked
gems/ 697 90272 11188
zig/**/*-test.zig + vopr/loom harness 0 0 0
*.md 4 492 7
other 7 154 93

Src Ruby Visibility Breakdown

Scope: compiler/ruby/**/*.rb

Files: 213

Counts are nonblank, non-comment Ruby source lines. Protected methods are grouped into OTHER.

bucket lines share
public functions 32198 32.1%
private functions 42815 42.7%
OTHER 25367 25.3%
total 100380 100.0%

Zig Special Coverage Alerts

No added production Zig lines require missing Loom/VOPR/wait-loop coverage alerts.

cuzzo and others added 4 commits July 31, 2026 22:42
`FactMine Rust coverage` failed on
`ruby_safe_navigation_nil_kill.rb: oracle mismatch`, and the diff was one
field: `domain_id` held `/home/yahn/easy-vm/...` where the runner
produced `/home/runner/work/clear/clear/...`. The oracle had been
regenerated on a machine and carried that machine's home directory into
the repository.

`normalize_paths` allow-listed `path`, `file`, `id` and `key`, so
`domain_id` and `symbol_owner` went through untouched, as did bare
strings inside `requirements` and `blockers` arrays -- including a prose
blocker, "unknown return expression ARGS at <path>:13", where the root
sits mid-sentence rather than at the start.

It now normalizes every string carrying the checkout root, whatever
holds it and wherever it appears. A string with the root in it is
machine-specific; which key it hangs off is not the question worth
asking. The oracle guard in the test follows the same rule, so a
regenerated oracle cannot smuggle one back in.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uf8MndFjJ9y6eUZzSySSwa
…tory

`nil-kill gem specs` failed on the golden shard everywhere but the
machine that recorded it: the fixture is a real collect, so every path
in it is absolute, and the spec asked FactMine to shape it against
`NilKill::ROOT` -- this checkout's root, not the one the shard was
recorded under. On a runner nothing matched, and the shaping produced
zero anchor executions where 3,782 were expected.

The fixture is re-rooted to `/golden/easy-vm`, which the spec passes
explicitly. No home directory is committed, and the shard shapes the
same way in every checkout.

Two things stayed machine-specific after that and should not have been
pinned at all: the package name follows the root's basename, so the
synthetic root keeps it; and the environment claim digesting the
checkout's own Gemfile.lock says nothing about the shaping and would
have failed the golden shard on any dependency bump.

The buildable-corpus job also needs the collector extension it traces
with -- the same step the nil-kill specs already grew.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uf8MndFjJ9y6eUZzSySSwa
Three Espalier tests named `target/debug/fact-mine-rust` outright. CI
builds `--release`, so all three errored with `Errno::ENOENT` rather
than running -- four errors in `Ruby gems coverage`.

They now resolve it the way every other caller does, through
`Espalier::StaticEvidence::FACT_MINE_RUST_BINARY`, which honours
`FACT_MINE_RUST_BINARY` and otherwise prefers release over debug.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uf8MndFjJ9y6eUZzSySSwa
All five benchmark-leak shards failed with `invalid value '' for
'--token <TOKEN>'`. Re-creating the repository dropped its secrets, so
`BENCHER_API_TOKEN` is empty.

The benchmark still runs and still fails the job on a leak; this step
only publishes the numbers afterwards. Without a token there is nowhere
to publish them, and failing the gate on an absent credential says
nothing about the code. It skips with a message instead.

Setting the secret restores the upload with no further change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uf8MndFjJ9y6eUZzSySSwa

@github-advanced-security github-advanced-security AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Boobytrap found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.

cuzzo and others added 2 commits July 31, 2026 23:22
Decomplex, Espalier and Nil-Kill SARIF all exited 124 -- `timeout` --
after scoping to 335 changed source files. The analyzers are not stuck;
the branch is 403 commits ahead of master, and a 15-minute budget was
set for a PR of ordinary size.

Raised to 40 minutes, with the job ceilings above it so the step reports
the timeout rather than the job being killed around it.

Worth revisiting if it recurs on a small PR: >2.7s per file is the
number to look at, not the budget.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uf8MndFjJ9y6eUZzSySSwa
Reverts the skip-when-absent guard. The secret was missing because the
repository had just been re-created; it is configured now, so the gate
should fail loudly if the upload cannot happen rather than pass quietly
without publishing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uf8MndFjJ9y6eUZzSySSwa
@cuzzo
cuzzo force-pushed the Espalier-STDLib branch from 3082276 to f991c6e Compare July 31, 2026 23:41
@github-actions

github-actions Bot commented Jul 31, 2026

Copy link
Copy Markdown

🐰 Bencher Report

ProjectCLEAR
BranchEspalier-STDLib
Testbedubuntu-latest

⚠️ WARNING: No Threshold found!

Without a Threshold, no Alerts will ever be generated.

Click here to create a new Threshold
For more information, see the Threshold documentation.
To only post results if a Threshold exists, set the --ci-only-thresholds flag.

Click to view all benchmark results
Benchmarkleak-build-msMeasure (units) x 1e3leak-countMeasure (units)leak-run-msMeasure (units)
benchmarks/concurrent/01_socket_throughput/bench📈 view plot
⚠️ NO THRESHOLD
6.13 units x 1e3📈 view plot
⚠️ NO THRESHOLD
0.00 units📈 view plot
⚠️ NO THRESHOLD
7.52 units
benchmarks/concurrent/06_dynamic_spawn/bench📈 view plot
⚠️ NO THRESHOLD
6.02 units x 1e3📈 view plot
⚠️ NO THRESHOLD
0.00 units📈 view plot
⚠️ NO THRESHOLD
35.85 units
benchmarks/concurrent/11_parallel_aggregation/bench📈 view plot
⚠️ NO THRESHOLD
6.20 units x 1e3📈 view plot
⚠️ NO THRESHOLD
0.00 units📈 view plot
⚠️ NO THRESHOLD
19.30 units
benchmarks/concurrent/18_atomic_counter/bench📈 view plot
⚠️ NO THRESHOLD
6.09 units x 1e3📈 view plot
⚠️ NO THRESHOLD
0.00 units📈 view plot
⚠️ NO THRESHOLD
5.81 units
benchmarks/inter-clear/04_concurrent_mvcc_fat_struct/bench📈 view plot
⚠️ NO THRESHOLD
6.43 units x 1e3📈 view plot
⚠️ NO THRESHOLD
0.00 units📈 view plot
⚠️ NO THRESHOLD
167.73 units
benchmarks/sequential/03_alloc_throughput/bench📈 view plot
⚠️ NO THRESHOLD
5.83 units x 1e3📈 view plot
⚠️ NO THRESHOLD
0.00 units📈 view plot
⚠️ NO THRESHOLD
7.70 units
benchmarks/sequential/08_sort/bench📈 view plot
⚠️ NO THRESHOLD
5.97 units x 1e3📈 view plot
⚠️ NO THRESHOLD
0.00 units📈 view plot
⚠️ NO THRESHOLD
5.19 units
benchmarks/sequential/13_soa_layout/bench📈 view plot
⚠️ NO THRESHOLD
5.98 units x 1e3📈 view plot
⚠️ NO THRESHOLD
0.00 units📈 view plot
⚠️ NO THRESHOLD
744.11 units
benchmarks/server/03_pathological/server📈 view plot
⚠️ NO THRESHOLD
6.16 units x 1e3📈 view plot
⚠️ NO THRESHOLD
0.00 units📈 view plot
⚠️ NO THRESHOLD
1,002.71 units
🐰 View full continuous benchmarking report in Bencher

cuzzo and others added 2 commits August 1, 2026 00:04
Three jobs compiled overlapping sets of the same crates on three fresh
runners: `sarif-rust-binaries` built fact-mine, decomplex and sql-cov;
`ruby-gems-coverage` built fact-mine, nil-kill, decomplex and gigasail;
`fact-mine-rust-coverage` built the same four again beside its
instrumented build.

`sarif-rust-binaries` now builds all five and publishes them, and the two
coverage jobs download that artifact instead. Six `cargo build --release`
invocations of already-built crates go.

What does not move is FactMine's `cargo llvm-cov` build. Coverage comes
from that instrumented binary; there is nothing to share.

`ruby-gems-coverage` copies the downloaded binaries to the
`target/release` paths its tests resolve, so no test needed changing.
And `sarif-rust-binaries` is no longer restricted to same-repo pull
requests -- the coverage jobs consume it and they run on pushes. The
SARIF jobs keep that restriction, which is about where a SARIF upload
may come from, not about compiling anything.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uf8MndFjJ9y6eUZzSySSwa
The gem SARIF analyzers scanned giga-ui's browser bundle as project
JavaScript: a hand-written `app.js` and a content-hashed, minified diff
viewer that is build output. Neither is code anyone writes or reviews
here.

`assets` joins the ignored path components. Every supported-extension
file under an `assets/` directory in this repository is of that kind --
the rest are SimpleCov's and RubyCritic's own report JavaScript, already
excluded by `coverage` and `vendor`, so this newly affects only giga-ui.

The UI's actual source is not in one of those directories and stays in
scope: `gems/gigasail/ui/src/*.tsx` is hand-written React, and
`giga-ui/src/ui/ui.rs` is 12,000 lines of hand-written axum and askama.
Excluding either would drop real code, which is the opposite of the
point.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uf8MndFjJ9y6eUZzSySSwa

@github-advanced-security github-advanced-security AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Decomplex found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.

@github-advanced-security github-advanced-security AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nil-Kill found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.

@github-advanced-security github-advanced-security AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Espalier found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.

cuzzo and others added 5 commits August 1, 2026 01:29
…-analysis

Two failures from the last run, and the first is mine.

`FactMine Rust coverage` got the shared binaries but only through the
environment, and several tests resolve `target/release` directly:

    SlopCop::Classifier cannot find executable decomplex-rust at
    .../gems/decomplex/target/release/decomplex-rust
    Required integration test binary not found: .../fact-mine-rust

It now installs them there, as `ruby-gems-coverage` already did.

`Architecture SARIF` was killed at its 45-minute job ceiling and
`SlopCop constraint coverage SARIF` at its 20-minute step budget --
`timeout`'s exit 124, the same overrun the other three SARIF jobs hit on
a branch this size. Their budgets go up with them, and every step budget
is now under its job ceiling so an overrun reports as a step timeout
rather than the job being killed around it. `boobytrap-sarif` and
`slopcop-sarif` had a 60-minute step under a 25-minute ceiling already.

Still a budget, not a fix: >2.7s per file over 335 files is the number
to look at if these recur on a small PR.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uf8MndFjJ9y6eUZzSySSwa
The architecture SARIF job took over 30 minutes. It was not the graph
work: a single fact-mine syntax-facts call on giga-ui's bundled diff
viewer ran 38 minutes at 100% CPU and had not finished. The file is
222KB across 61 lines, one of them 122,875 characters, and tree-sitter
degrades badly on it. Its hand-written neighbour app.js parses in 0.19s.

`assets` holds what a program serves rather than what it is, which is
why tools/generate_lint_sarif.rb already ignores it elsewhere; the
espalier corpus never got the same treatment. Excluding it drops two
files from a 746-file corpus and reports the same cycles.

cycle_report 30m+ -> 191s, reach_through_report likewise, both pinned to
two cores to match the runner.

Co-authored-by: Codex <codex@openai.com>
parse_files already fanned out across workers, but the projection that
follows it ran as a serial iter().map(). Projection is the bulk of the
work: it derives the per-function dataflow (liveness, dominators,
reaching definitions, def-use, path conditions), which is why 120 Ruby
files cost 12s while a 2.2MB but structurally empty get-pip.py costs
0.5s. The serial map capped a whole-corpus run at ~1.5 cores regardless
of how many were available.

map_ordered preserves document order, so output is unchanged: the
emitted JSON is byte-identical to the serial binary's on the same
120-file corpus, on both 2 cores and 32.

  2 cores:  14.8s @ 126% -> 10.7s @ 178%
  32 cores: 12.2s @ 157% ->  5.3s @ 362%

call-resolution already reaches 164%/309% on the same corpus and is left
alone.

Co-authored-by: Codex <codex@openai.com>
A full projection of this repository is ~1.08 GB of JSON across 40 keys
per document. The architecture reports read five of them - 8% - and
discard the dataflow bulk (clone_candidates alone is 32%, liveness 9%).
Everything else was serialized here and JSON.parse'd back in Ruby only to
be dropped, which was most of that stage's runtime.

--fields keeps only the named top-level keys. Selection happens after the
document is built, so the analysis is unchanged and the retained values
are byte-identical to a full projection's; only what crosses the pipe
shrinks. Omitting the flag emits everything, as before.

Whole corpus on two cores, 744 files: syntax-facts 93.4s -> 64.4s, and
the per-batch payload 151 MB -> 13.1 MB. Both architecture SARIFs are
unchanged.

Note this does not yet SKIP the discarded work: the keys are still built,
then dropped. Making construction lazy is a further change.

Co-authored-by: Codex <codex@openai.com>
cycle_report.rb and reach_through_report.rb were introduced as "WIP
anti-pattern experiment tools" (beb2fe8) and never left that state.
Each costs a whole-corpus FactMine projection; together they were the
entire runtime of the Architecture SARIF job.

reach_through also cannot model package-scoped languages. It derives
owners per file, so two files in one Go package read as a cross-module
private call - Go has no private, lowercase is package-private and
same-package access is legal. Its 81 self-quarantined "suspect" findings
are that bug, and suspects were never uploaded to SARIF anyway.

change_coupling.rb stays: it runs in 0.83s and does not use FactMine, so
the job no longer needs the shared Rust binaries at all.

The tools and their tests stay in the tree for whenever they earn the
cost. Job budget 75m -> 15m.

Co-authored-by: Codex <codex@openai.com>
@@ -0,0 +1,524 @@
//! The top-level diff summary ("funnel"): a language-aware breakdown of the
@@ -0,0 +1,524 @@
//! The top-level diff summary ("funnel"): a language-aware breakdown of the
@@ -0,0 +1,630 @@
//! `gigasail diff` — an interactive terminal UI for reviewing changes by risk.
@@ -0,0 +1,630 @@
//! `gigasail diff` — an interactive terminal UI for reviewing changes by risk.
@@ -0,0 +1,1282 @@
// Hot observation loop for the Ruby runtime-SCIP collector.
@@ -0,0 +1,1282 @@
// Hot observation loop for the Ruby runtime-SCIP collector.
The SlopCop constraint SARIF job hit its 60-minute timeout. Same root
cause as the architecture job: giga-ui's content-hashed, minified diff
viewer is 222KB across 61 lines, one of them 122,875 characters, and
tree-sitter takes tens of minutes on it. SlopCop has its own file
discovery, so the espalier-side fix did not reach it, and it scanned the
bundle three separate times.

The exclusion goes in LanguageProvider rather than each of the thirteen
providers' EXCLUDED_DIRS: a served bundle is not a language-specific
concern. It matches path segments, so lib/assets_helper.rb still scans.

Whole run against origin/master: 60m timeout -> 26s, exit 0, 83 results
across 32 rules, no asset paths among them.

Co-authored-by: Codex <codex@openai.com>

@github-advanced-security github-advanced-security AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SlopCop found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.

@cuzzo
cuzzo merged commit f882f8c into master Aug 1, 2026
102 of 103 checks passed
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