Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .github/workflows/prune.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ permissions:
# pins.yml issue body lists every site.
env:
PINNED_TOOLCHAIN: nightly-2026-04-03
RECONVERGE_VERSION: "0.3.0"
RECONVERGE_VERSION: "0.4.0"

jobs:
gate:
Expand Down
112 changes: 112 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,118 @@ change measured timings are marked `bench:`.

## [Unreleased]

## [2.0.0] - 2026-08-26

A major, and both reasons are in the "breaking" list this project keeps in
[docs/RELEASING.md](docs/RELEASING.md): a changed CLI flag, and a change to
what the gate admits.

### Changed — breaking

- **The safety gate pins `reconverge` 0.4.0**, up from 0.3.0, and 0.4.0
reads a shared-memory length written as a **named const**. 0.3.0 could
only read a literal: `SharedArray<f32, TILE>` arrived as an unevaluated
const, the analyzer's `eval_target_usize` refused it, and the static was
dropped from the RC004 budget with no finding and no diagnostic.

This is not an abstract gap — it is the shape every tunable kernel has.
`corpus/matmul-tiled` declares `SharedArray<f32, { TM * TK }>`, and
launchbound rewrites `TM`/`TK` per candidate, so **every configuration
this tool tries took the path RC004 could not see**. A space with an
over-cap tile pruned as all-clean.

Verified against the corpus: the six kernels' verdicts are unchanged
(their tiles are well under the cap), and raising `matmul-tiled` to
`TM = TK = 128` now produces
`error[RC004]: kernel matmul declares 73728 bytes`, where 0.3.0 said
nothing. Any kernel whose shared memory is sized by a const may now be
refused where it previously passed — which is the gate working, and is
why this is a major.

- **`--cc` is required by `tune`**, as it already was by `prune` and
`model`. It defaulted to `8.6`, which meant the one command whose answer
you act on quietly picked a device, while the two inspection commands
made you choose. `prune --cc`'s own help says a verdict at one capability
does not transfer to another; RC004 is a capacity check, and 8.6 offers
164 KB per SM against 7.5's 64 KB.

`launchbound tune <kernel> --backend model` now asks for `--cc`.

### Added

- **`--cc` is validated at the command line, and the CUDA spellings work.**
A mistyped `--cc` used to be handed to `cargo reconverge` once per
candidate: eleven subprocesses for `reduce-flip`, 101 over the corpus, and
ninety lines of output in which the actual problem appeared nowhere. It is
now one line in ~100ms with nothing spawned. `--cc 86` and `--cc sm_86`
are normalized to `8.6` rather than rejected — for two digits the mapping
is unambiguous, and it is the spelling a CUDA user already has.

### Fixed

- **A reconverge failure reports what reconverge said.** The tool error
showed the *last* six lines of its stderr — a reasonable-looking default,
since a failing tool usually fails last, and reliably the wrong six:
reconverge prints its diagnosis first and its usage reference after it, so
the tail was the exit-code legend. reconverge 0.4.0 stopped printing usage
after a bad value, which fixes that case at the source; this reads the
lines marked `error:` regardless, because no caller controls what its
analyzer prints, and falls back to the head rather than the tail.

- **`model` says that it is not gated.** It ranks the whole space, and on
`reduce-flip` its top five are all configurations the gate refuses — the
fastest row was a kernel that hangs, under a header that carefully said
"estimated, not a measurement" and nothing about safety. It still runs no
gate and needs no `reconverge`; it now says so, and names `tune --backend
model` as the gated form.

- **`tune --backend model` no longer leaves an empty run directory.** The
directory was created before the backend match, for every backend, and the
model path writes nothing — so every run littered `runs/`, which is
checked in, and `launchbound report` on it failed with `verdicts.json: No
such file`. An `--out` given to this backend is now answered rather than
silently ignored.

- **`launchbound-tui --help` prints help.** It read `args()` directly, so
every flag was taken as a run-directory path: `--help` came back as
`run dir: --help/verdicts.json: No such file or directory`, which reads as
a broken tool. `-h`, `--help`, `-V` and `--version` answer; any other
leading dash is reported as an unknown option, which is what stops the
next flag landing here as a path. This is a published binary.

- **The chosen configuration's interval is dropped, not cut.** At eighty
columns — the default terminal size, and the width this suite mandates —
the line ended `0.0400 ms [0.0398, `: a number with no upper bound and a
dangling comma, on the one line carrying the result. The interval now goes
whole when it does not fit; at 110 columns it is unchanged.

- **The TUI goldens wait for a finished frame.** They synced on a 150ms
quiet period, which is a guess at how long a repaint takes; on a loaded
runner the app pauses mid-repaint and the screen read is half-painted.
This had already cost the suite once — `ranking_scrolls_a_long_candidate_list`
carries a comment about a golden blessed from a too-early capture, which
then verified nothing while passing — and the same shape failed
reconverge's `main` on macOS. The binary already brackets every repaint in
DEC 2026 synchronized updates, so `wait_frame` observes only whole frames.
The 100-iteration stress gate went from **15.8s to 0.7s**.

### Migrating from 1.x

- **The Action's floating tag is now `@v2`.** `uses:
vyncint/launchbound/action@v1` keeps working and stays on 1.2.0, which is
the point of a floating major tag — but it stays on reconverge 0.3.0 with
it, and 0.3.0 is the analyzer that cannot see a named-const shared-memory
size. Move to `@v2` to get the gate this release is about.
- **Add `--cc` to any `launchbound tune` invocation.** It has no default now.

### Documentation

- The CLI table listed `launchbound tui <run>`, which is not a subcommand —
the binary is `launchbound-tui`. It also omitted `model`, and showed
`tune` without the `--cc` it now requires.
- The Action's input table still gave `reconverge-version` as `0.1.11`, two
releases stale.

## [1.2.0] - 2026-08-22

### Changed
Expand Down
22 changes: 11 additions & 11 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 9 additions & 9 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ members = [
exclude = ["corpus"]

[workspace.package]
version = "1.2.0"
version = "2.0.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
Expand All @@ -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.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" }
launchbound-space = { path = "crates/launchbound-space", version = "2.0.0" }
launchbound-prune = { path = "crates/launchbound-prune", version = "2.0.0" }
launchbound-build = { path = "crates/launchbound-build", version = "2.0.0" }
launchbound-bench = { path = "crates/launchbound-bench", version = "2.0.0" }
launchbound-report = { path = "crates/launchbound-report", version = "2.0.0" }
launchbound-search = { path = "crates/launchbound-search", version = "2.0.0" }
launchbound-model = { path = "crates/launchbound-model", version = "2.0.0" }
launchbound-metal = { path = "crates/launchbound-metal", version = "2.0.0" }
anyhow = "1"
clap = { version = "4", features = ["derive"] }
serde = { version = "1", features = ["derive"] }
Expand Down
18 changes: 14 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,15 +130,25 @@ Two asymmetries, published rather than buried:

```
launchbound space <kernel> [--json] # enumerate the space, print its size
launchbound prune <kernel> [--json] # reconverge pass only — NO GPU NEEDED
launchbound tune <kernel> [--budget 30m] [--backend cuda|metal|model]
launchbound prune <kernel> --cc 8.6 [--json] # reconverge pass only — NO GPU NEEDED
launchbound model <kernel> --cc 8.6 # analytical ranking — NOT GATED
launchbound tune <kernel> --cc 8.6 --backend cuda|metal|model [--budget 30m]
launchbound report <run> [--json] [--rejected] # includes refused-but-faster configs
launchbound apply <run> # emit the cuda-oxide policy specialization
launchbound tui <run> # the run in four views: the chosen
launchbound-tui <run> # the run in four views: the chosen
# configuration and the field it beat,
# the ranking, the refusals, the progress
```

`--cc` is required by `prune`, `model` and `tune` alike — a verdict at one
compute capability does not transfer to another, and `tune` is the command
whose answer you act on. The CUDA spellings work: `--cc 86` and `--cc sm_86`
mean `8.6`.

`model` ranks the **whole** space and says so: it runs no gate and needs no
`reconverge`, so its fastest row may be a configuration that hangs. `tune
--backend model` is the gated form of the same ranking.

Exit codes: `0` a safe configuration was found; `1` the fastest candidates
were refused and the chosen one is slower than a rejected candidate — notable,
not an error; `2` tool error. `--allow-unsafe` exists, requires an explicit
Expand All @@ -147,7 +157,7 @@ reason string recorded in the report, and is never the default.
`prune` needing no GPU is the reason it exists as its own verb: it is the only
part of the pipeline a developer on a laptop can run, and it is the part that
finds the bugs. It also ships as a [GitHub Action](action/) —
`uses: vyncint/launchbound/action@v1` puts the gate in your CI.
`uses: vyncint/launchbound/action@v2` puts the gate in your CI.

## Compared to the neighbours

Expand Down
4 changes: 2 additions & 2 deletions action/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:

- name: Safety gate
id: gate
uses: vyncint/launchbound/action@v1
uses: vyncint/launchbound/action@v2
with:
kernel: myrepo/kernels/my-reduction
cc: "8.6" # the part you will actually run on
Expand Down Expand Up @@ -63,7 +63,7 @@ the gate specializes per candidate.
| `cc` | — | target compute capability, e.g. `"8.6"` (required; verdicts do not transfer across parts) |
| `fail-on` | `tool-error` | `never`, `refused`, or `tool-error` |
| `version` | `latest` | launchbound-cli release to install |
| `reconverge-version` | `0.1.11` | reconverge release from crates.io — moves in lockstep with `toolchain` |
| `reconverge-version` | `0.4.0` | reconverge release from crates.io — moves in lockstep with `toolchain` |
| `toolchain` | `nightly-2026-04-03` | the nightly that built that reconverge |
| `summary` | `"true"` | write the verdict table to the job summary |

Expand Down
4 changes: 2 additions & 2 deletions action/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ inputs:
default: tool-error
version:
description: >-
Which launchbound release runs the gate, e.g. "1.0.1". The default,
Which launchbound release runs the gate, e.g. "2.0.0". The default,
`latest`, installs the newest release on crates.io each run. Pin a
number if you want the gate to change only when you say so.
required: false
Expand All @@ -51,7 +51,7 @@ inputs:
reconverge-driver). Must be built by the toolchain below — the pins
move together.
required: false
default: "0.3.0"
default: "0.4.0"
toolchain:
description: >-
The pinned nightly that matches reconverge-version; the pair moves
Expand Down
Loading
Loading