From 85c0e1450ad14d5b38d891df2c755a4cb0ba10d8 Mon Sep 17 00:00:00 2001 From: Vyncint Ng <115854244+vyncint@users.noreply.github.com> Date: Sat, 22 Aug 2026 11:30:22 +0700 Subject: [PATCH 1/2] chore: format .claude/settings.json as the standard does MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Same settings, written compact where termlens and mossaic write them formatted. Functionally identical and textually different, which is exactly the drift the ecosystem repo's sync tool exists to catch — and it caught this one on its first dry run, before anybody had to notice by eye. Signed-off-by: Vyncint Ng <115854244+vyncint@users.noreply.github.com> --- .claude/settings.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.claude/settings.json b/.claude/settings.json index 2172764..1d544e8 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -1 +1,7 @@ -{"includeCoAuthoredBy":false,"attribution":{"co_authored_by":false,"commit_message_footer":false}} +{ + "includeCoAuthoredBy": false, + "attribution": { + "co_authored_by": false, + "commit_message_footer": false + } +} From 3a7ab504d3f268360d3fe2446344eb10c95c5848 Mon Sep 17 00:00:00 2001 From: Vyncint Ng <115854244+vyncint@users.noreply.github.com> Date: Sat, 22 Aug 2026 11:47:51 +0700 Subject: [PATCH 2/2] feat(gate): pin reconverge 0.3.0, and prove the gate is unchanged MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The gate ran an analyzer two minor versions old. reconverge has been at 0.3.0 since 22 August; launchbound pinned 0.1.11 in four places — the corpus workflow, the Action default, and the two documents that name it. This was deferred rather than forgotten, because a newer analyzer can change what the gate refuses, and that is a change in product behaviour rather than a dependency bump. So it is measured rather than assumed. The corpus was run under both versions on the same toolchain and compared: histogram 12 clean 0 caveats 0 refused matmul-tiled 18 clean 0 caveats 0 refused reduce-flip 3 clean 0 caveats 8 refused reduce-stable 11 clean 0 caveats 0 refused scan-block 4 clean 0 caveats 0 refused stencil-1d 45 clean 0 caveats 0 refused The two runs are byte-identical — not merely equal in the totals, but the same candidate hashes, the same REFUSED RC001 lines, the same reasons. The eight refusals are reduce-flip above one warp, the corpus's known flip and the behaviour the gate exists to produce. The gate tests pass unchanged, including known_flip_kernel_disqualifies_above_one_warp. No toolchain change was needed: launchbound and reconverge 0.3.0 already pin the same nightly-2026-04-03, so the rule that the analyzer and the toolchain move together is satisfied without moving either. What this does not establish is general equivalence. reconverge gained multi-warp replay, bounded inlining and unmasked warp-wrapper analysis between these versions; a kernel exercising those paths could be decided differently. Six kernels are the evidence, and six kernels are what they are — which is why the measurement is written down in research-baseline.md rather than summarized as "no change". Signed-off-by: Vyncint Ng <115854244+vyncint@users.noreply.github.com> --- .github/workflows/prune.yml | 2 +- CHANGELOG.md | 31 ++++++++++++++++++++++++++ Cargo.lock | 22 +++++++++---------- Cargo.toml | 18 ++++++++-------- action/action.yml | 2 +- docs/LIMITATIONS.md | 4 ++-- docs/research-baseline.md | 43 ++++++++++++++++++++++++++++++++++++- 7 files changed, 97 insertions(+), 25 deletions(-) diff --git a/.github/workflows/prune.yml b/.github/workflows/prune.yml index d9f7a89..64be1a0 100644 --- a/.github/workflows/prune.yml +++ b/.github/workflows/prune.yml @@ -17,7 +17,7 @@ permissions: # pins.yml issue body lists every site. env: PINNED_TOOLCHAIN: nightly-2026-04-03 - RECONVERGE_VERSION: "0.1.11" + RECONVERGE_VERSION: "0.3.0" jobs: gate: diff --git a/CHANGELOG.md b/CHANGELOG.md index aa671a9..92b3947 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,37 @@ change measured timings are marked `bench:`. ## [Unreleased] +## [1.2.0] - 2026-08-22 + +### Changed + +- **The safety gate now pins `reconverge` 0.3.0**, up from 0.1.11 — two minor + versions of analyzer the gate was not getting. The pin moves in four places: + the corpus workflow, the Action's `reconverge-version` default, and the two + documents that name it. + + **The gate admits exactly the same set.** A newer analyzer can change what + the gate refuses, which is a change in product behaviour rather than a + dependency bump, so the corpus was re-run under both versions on the same + toolchain and compared: **93 clean, 8 refused, 0 caveats, 0 tool errors**, + and the two runs are **byte-identical** — same candidate hashes, same + `REFUSED RC001` lines, same reasons. The eight refusals are `reduce-flip` + above one warp, which is the corpus's known flip and the behaviour the gate + exists to produce. The measurement is recorded in + [docs/research-baseline.md](docs/research-baseline.md#analyzer-equivalence-0111--030). + + No toolchain change was needed: launchbound and reconverge 0.3.0 already pin + the same `nightly-2026-04-03`, so the rule that the analyzer and the + toolchain move together is satisfied without moving either. + + What this does *not* establish is general equivalence. reconverge gained + multi-warp replay, bounded inlining and unmasked warp-wrapper analysis + between these versions; a kernel exercising those paths could be decided + differently. Six kernels are the evidence, and six kernels are what they are. + +- `docs/LIMITATIONS.md` now describes the limits of the analyzer the gate + actually runs, and carries the date it was re-checked. + ## [1.1.0] - 2026-08-22 ### Added diff --git a/Cargo.lock b/Cargo.lock index 6791ceb..ffdb2d9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1121,7 +1121,7 @@ checksum = "bf36173d4167ed999940f804952e6b08197cae5ad5d572eb4db150ce8ad5d58f" [[package]] name = "launchbound-bench" -version = "1.1.0" +version = "1.2.0" dependencies = [ "anyhow", "launchbound-space", @@ -1135,7 +1135,7 @@ dependencies = [ [[package]] name = "launchbound-build" -version = "1.1.0" +version = "1.2.0" dependencies = [ "launchbound-space", "serde", @@ -1146,7 +1146,7 @@ dependencies = [ [[package]] name = "launchbound-cli" -version = "1.1.0" +version = "1.2.0" dependencies = [ "anyhow", "clap", @@ -1164,7 +1164,7 @@ dependencies = [ [[package]] name = "launchbound-metal" -version = "1.1.0" +version = "1.2.0" dependencies = [ "launchbound-bench", "launchbound-space", @@ -1175,7 +1175,7 @@ dependencies = [ [[package]] name = "launchbound-model" -version = "1.1.0" +version = "1.2.0" dependencies = [ "launchbound-space", "serde", @@ -1185,7 +1185,7 @@ dependencies = [ [[package]] name = "launchbound-prune" -version = "1.1.0" +version = "1.2.0" dependencies = [ "launchbound-build", "launchbound-space", @@ -1198,7 +1198,7 @@ dependencies = [ [[package]] name = "launchbound-report" -version = "1.1.0" +version = "1.2.0" dependencies = [ "insta", "jsonschema", @@ -1210,7 +1210,7 @@ dependencies = [ [[package]] name = "launchbound-runner" -version = "1.1.0" +version = "1.2.0" dependencies = [ "launchbound-bench", "launchbound-search", @@ -1218,7 +1218,7 @@ dependencies = [ [[package]] name = "launchbound-search" -version = "1.1.0" +version = "1.2.0" dependencies = [ "launchbound-bench", "proptest", @@ -1226,7 +1226,7 @@ dependencies = [ [[package]] name = "launchbound-space" -version = "1.1.0" +version = "1.2.0" dependencies = [ "proptest", "serde", @@ -1237,7 +1237,7 @@ dependencies = [ [[package]] name = "launchbound-tui" -version = "1.1.0" +version = "1.2.0" dependencies = [ "anyhow", "crossterm", diff --git a/Cargo.toml b/Cargo.toml index 8807016..4345ddf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ members = [ exclude = ["corpus"] [workspace.package] -version = "1.1.0" +version = "1.2.0" edition = "2024" # MSRV for crates that do not require the pinned nightly (CONTRIBUTING.md); # set by ratatui 0.30. The analysis and compile paths require @@ -33,14 +33,14 @@ keywords = ["cuda", "gpu", "autotuner", "convergence", "kernel"] categories = ["development-tools", "development-tools::profiling"] [workspace.dependencies] -launchbound-space = { path = "crates/launchbound-space", version = "1.1.0" } -launchbound-prune = { path = "crates/launchbound-prune", version = "1.1.0" } -launchbound-build = { path = "crates/launchbound-build", version = "1.1.0" } -launchbound-bench = { path = "crates/launchbound-bench", version = "1.1.0" } -launchbound-report = { path = "crates/launchbound-report", version = "1.1.0" } -launchbound-search = { path = "crates/launchbound-search", version = "1.1.0" } -launchbound-model = { path = "crates/launchbound-model", version = "1.1.0" } -launchbound-metal = { path = "crates/launchbound-metal", version = "1.1.0" } +launchbound-space = { path = "crates/launchbound-space", version = "1.2.0" } +launchbound-prune = { path = "crates/launchbound-prune", version = "1.2.0" } +launchbound-build = { path = "crates/launchbound-build", version = "1.2.0" } +launchbound-bench = { path = "crates/launchbound-bench", version = "1.2.0" } +launchbound-report = { path = "crates/launchbound-report", version = "1.2.0" } +launchbound-search = { path = "crates/launchbound-search", version = "1.2.0" } +launchbound-model = { path = "crates/launchbound-model", version = "1.2.0" } +launchbound-metal = { path = "crates/launchbound-metal", version = "1.2.0" } anyhow = "1" clap = { version = "4", features = ["derive"] } serde = { version = "1", features = ["derive"] } diff --git a/action/action.yml b/action/action.yml index 6da5c84..b650ed3 100644 --- a/action/action.yml +++ b/action/action.yml @@ -51,7 +51,7 @@ inputs: reconverge-driver). Must be built by the toolchain below — the pins move together. required: false - default: "0.1.11" + default: "0.3.0" toolchain: description: >- The pinned nightly that matches reconverge-version; the pair moves diff --git a/docs/LIMITATIONS.md b/docs/LIMITATIONS.md index 34f35e6..f0bf692 100644 --- a/docs/LIMITATIONS.md +++ b/docs/LIMITATIONS.md @@ -2,11 +2,11 @@ A tool that overstates its reach is worse than one that does less. These are launchbound's, with numbers where we have them. Everything here was true on -2026-08-20 against the pins in rust-toolchain.toml and CONTRIBUTING.md. +2026-08-22 against the pins in rust-toolchain.toml and CONTRIBUTING.md. ## The gate inherits reconverge's limits, wholesale -A clean gate is **not a proof of correctness**. `reconverge` (v0.1.11) is +A clean gate is **not a proof of correctness**. `reconverge` (v0.3.0) is summary-based and interprocedural, handles reducible control flow only, cannot evaluate non-literal masks, and puts data races entirely out of scope. Its own documentation is the authority; launchbound adds no analysis diff --git a/docs/research-baseline.md b/docs/research-baseline.md index c4770d4..beea862 100644 --- a/docs/research-baseline.md +++ b/docs/research-baseline.md @@ -15,7 +15,7 @@ Measured 2026-08-20. | tier-1 guest | Apple `container` 1.2.0, **native arm64** Ubuntu 24.04 (no Docker, no Rosetta — operator requirement), CUDA toolkit 13.2 (sbsa), LLVM 21.1.8, container `cuda-oxide-dev` | | tier-2 box | AWS `g5.xlarge` spot @ **$0.364/hr**, us-east-2c, **NVIDIA A10G** (`sm_86`, cc 8.6), driver 595.71.05, CUDA 13.2 (V13.2.51), LLVM 21.1.8 — chosen over the T4 by the operator; barely above T4 spot ($0.335/hr) | | pinned toolchain | `nightly-2026-04-03` (`rustc 1.96.0-nightly (55e86c996 2026-04-02)`) | -| reconverge | `cargo-reconverge 0.1.11` (built at `~/Projects/reconverge/target/release`) | +| reconverge | `cargo-reconverge 0.1.11` (built at `~/Projects/reconverge/target/release`) — **the version these measurements were taken with**; the gate now pins 0.3.0, which was verified to admit the identical set (see below) | | cuda-oxide | checkout `50d07314eb8b7d5ec821ba02b0048a753c20dd4e` — the tree synced to the box (the box AMI's own stale clone reports `e28248c1`, but `./gpu sync` replaces the working tree and excludes `.git`, so the synced tree is what compiled) | | subject kernels | `s0-reduce` (device-only lib crate, dep `cuda-device` only, containing the README's known-flip reduction); cuda-oxide examples `vecadd` (small) and `tiled_gemm` (large) | | evidence logs | `~/Projects/cuda-oxide/.gpu-evidence/20260820T{071248,071807,071959}Z.log` | @@ -131,3 +131,44 @@ Findings on the subject kernel (`--strict`, `--cc 7.5`): |---|---|---| | RC005 | warning | kernel `reduce_flip` calls `index_1d()` without a launch contract | | RC001 | warning | kernel `reduce_flip` may execute `sync_threads()` under thread-divergent control | + +## Analyzer equivalence: 0.1.11 → 0.3.0 + +The gate's pinned analyzer moved from `cargo-reconverge` 0.1.11 to 0.3.0. A +newer analyzer can change **what the gate admits**, which is a change in +product behaviour rather than a dependency bump — so the corpus was re-run +under both, on the same toolchain, and compared. + +Measured 2026-08-22 on `nightly-2026-04-03`, cuda-oxide `50d07314`, tier 0 +(no GPU): + +```console +$ cargo run -q -p launchbound-cli -- prune --cc 8.6 +``` + +| kernel | clean | caveats | refused | tool errors | +|---|---|---|---|---| +| histogram | 12 | 0 | 0 | 0 | +| matmul-tiled | 18 | 0 | 0 | 0 | +| reduce-flip | 3 | 0 | **8** | 0 | +| reduce-stable | 11 | 0 | 0 | 0 | +| scan-block | 4 | 0 | 0 | 0 | +| stencil-1d | 45 | 0 | 0 | 0 | +| **total** | **93** | **0** | **8** | **0** | + +**The two runs are byte-identical** — not merely equal in the totals, but the +same candidate hashes, the same `REFUSED RC001` lines, the same reasons. The +eight refusals are the `reduce-flip` candidates at block sizes above one warp, +which is the corpus's known flip and the behaviour the gate exists to produce. + +The gate tests pass under 0.3.0 unchanged, including +`known_flip_kernel_disqualifies_above_one_warp` and +`known_stable_kernel_disqualifies_nothing`. + +**What this does and does not establish.** It establishes that on *this* +corpus, at cc 8.6, the two analyzers decide identically — so the bump carries +no behaviour change this project can observe. It does not establish that they +are equivalent in general: reconverge gained multi-warp replay, bounded +inlining and unmasked warp-wrapper analysis between these versions, and a +kernel exercising those paths could well be decided differently. The corpus is +the evidence, and the corpus is six kernels.