Add the initial example corpus and its verification script - #1
Merged
Conversation
Eleven complete programs, each in its own directory with a README and a recorded expected_output.txt. Six cover language essentials; five do a real job end to end using the standard library. scripts/run-examples.sh compiles and runs every example and diffs its output against the recorded baseline. It is the shared contract: this repo's CI calls it against mux-compiler main, and the release jobs in mux-compiler and mux-runtime will call it against the binary they just built. Every example is deterministic and offline - no clock, no network, no unseeded randomness - so a recorded output is a usable assertion. CI builds mux-compiler main rather than pinning a release. Pinning would deadlock: a fix for a compiler-caused failure could not merge until the release shipped, and the release should not ship with a broken example. Three examples work around known defects, each linked from its README: mux-compiler#390 (string ordering silently does nothing), mux-compiler#391 (ICE importing a std.dsa type without Collection), and mux-runtime#50 (next_range returns only half its range). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A server and a client over loopback on two threads, using std.net, std.sync and std.data.json. Binds an ephemeral port, so nothing leaves the machine and the port never reaches the output. The client returns its status rather than printing it, and the main thread prints after join. Two threads printing race, and an example whose line order can vary cannot be diffed against a recorded output. Building this turned up four defects, each linked from the example's README: mux-compiler#392 (Json has no accessors, so a string field keeps its quotes), mux-runtime#52 (every JSON number is a float, so status 201 reads as 201.0), mux-compiler#394 (ICE writing a captured variable from a match arm in a closure), and mux-compiler#393 (result handling forces nesting for class-typed values). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
dice_simulation goes back to random.next_range(1, SIDES + 1), the spelling it should have been teaching. Its distribution is now uniform across all six faces rather than never rolling above a 3. http_server reads status 201 instead of 201.0, so the note about every JSON number being a float is gone. The note about writing a captured variable from a match arm is gone too - that ICE is fixed, and the structure it forced (matching extracted into fetch_status) is kept because it reads better, not because it is required. Still present and still true: the quoted-string workaround in http_server (Json has no accessors) and the Collection import in route_finder (the ICE is now a diagnostic, but the import still has to be written by hand). DO NOT MERGE before those two PRs. Verified 12/12, twice, against them.
|
| Filename | Overview |
|---|---|
| scripts/run-examples.sh | Adds example discovery, compilation, output comparison, baseline updates, portable timeout handling, and correct failure propagation. |
| .github/workflows/examples.yml | Adds CI that builds the compiler and runtime before running the shared example verifier. |
| README.md | Documents the example corpus, verification workflow, authoring requirements, and the planned status of release integration. |
| examples/csv_to_json/main.mux | Adds a self-contained CSV-to-JSON example with safe handling for ragged rows. |
| examples/http_server/main.mux | Adds a deterministic loopback HTTP client/server example with coordinated output after thread completion. |
Reviews (4): Last reviewed commit: "Fail --update when a baseline cannot be ..." | Re-trigger Greptile
--update recorded a failure and then exited 0, so a partial re-record reported as a complete one and the missing baseline only surfaced later as a check failure, far from the run that caused it. It now names the examples that were not updated and exits 1. Verified: a deliberately broken example makes `--update` exit 1 rather than 0. csv_to_json indexed each row by header position, which panics on the first ragged row - a short line in the file produces a short row, and a real CSV eventually contains one. It reads with `get` and handles the missing case instead, which is also the pattern worth copying out of an example. Both raised by Greptile on #1.
The runner said it depends on nothing but a mux binary and then invoked `timeout`, which is GNU coreutils and absent from a default macOS install - where every example would have failed as command-not-found rather than being checked. It now uses `timeout` or `gtimeout` when present and runs without a limit otherwise, saying so. The limit only guards against an example that hangs, so losing it costs a safety net rather than the check. Verified with a PATH containing no timeout: the note prints and the example still runs. The README presented mux-compiler's and mux-runtime's release jobs as current callers guaranteeing the examples work against a real release. Those jobs are not written. Both the README and the script header now say so, so nobody relies on a guarantee that is not enforced - today the guarantee is only against main. Both raised by Greptile on #1.
An unwritable expected_output.txt - a read-only checkout, a permissions problem - was counted as updated and the run exited 0, which is the same shape as the unrunnable-example bug fixed in the previous commit: an unchecked step reporting success. The redirection is checked now. Verified with a read-only baseline: exit 1, not 0. Raised by Greptile on #1.
This was referenced Aug 14, 2026
Merged
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The initial example corpus for
mux-examples, plus the CI that verifies it.Implements muxlang/mux-context#26, and depends on the decision recorded for
muxlang/mux-context#44: the compiler's test corpus and the published examples
are separate artifacts, so nothing here moves out of
mux-compiler/test_scripts.What is here
12 examples, each a directory with the program, a README saying what it
teaches, and an
expected_output.txtthat CI diffs against.Language essentials:
hello,collections,classes_and_interfaces,enums_and_match,optional_and_result,generics_and_closuresPrograms that do a job:
csv_to_json,inventory_report,event_timeline,route_finder,dice_simulation,http_serverEvery one is deterministic and offline - no clock, no network, no unseeded
randomness, no file it did not write itself. That is the constraint that makes a
recorded output a usable assertion, and it is written into
AGENTS.mdso thenext example follows it.
scripts/run-examples.shcompiles and runs each example and diffs itsoutput. It is the shared contract, and it takes nothing but a
muxbinary:.github/workflows/examples.ymlbuilds mux-compilermainand runs it.Deliberately NOT path-filtered: it is a required check, and a required check
skipped by a paths filter never reports, so the PR would wait on it forever.
Why CI builds
mainrather than a releaseAn example is authored here before the compiler change it depends on has
shipped. Pinning to a release would deadlock: the fix for a compiler-caused
failure could not merge until the release shipped, and the release should not
ship with a broken example. The guarantee that examples work against a real
RELEASE comes from the release jobs in mux-compiler and mux-runtime running this
same script - that part is not built yet and is the obvious follow-up.
Authoring this found nine defects
Writing programs against a real compiler is what turned them up, which is most
of the argument for the repo existing. All are now fixed and merged:
next_rangereturned only the lower half of its rangestring.length()counted bytes, not charactersThe last commit retires the workarounds those fixes made unnecessary:
dice_simulationusesrandom.next_rangeagain and rolls a uniform 1-6 ratherthan never exceeding 3, and
http_serverreads status201rather than201.0.Still worked around, deliberately
Two notes remain in the examples because the underlying issues are open, and
both say so in the code:
http_servercompares a quoted path ("\"/echo\""), becauseJsonhasonly
stringifyand a string field arrives JSON-encoded with no way to stripthe quotes (mux-compiler#392, #389).
route_finderimportsCollectionby hand. That is now a clean diagnosticrather than an ICE, but importing a type still does not pull in the interfaces
it implements (mux-compiler#391).
Verification
12/12, run twice for determinism, against mux-compiler
mainat 9b32db3 - themerge of #396 - with the runtime its lockfile resolves.