Skip to content

Add the initial example corpus and its verification script - #1

Merged
DerekCorniello merged 6 commits into
mainfrom
fix/retire-workarounds
Aug 14, 2026
Merged

Add the initial example corpus and its verification script#1
DerekCorniello merged 6 commits into
mainfrom
fix/retire-workarounds

Conversation

@DerekCorniello

Copy link
Copy Markdown
Contributor

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.txt that CI diffs against.

Language essentials: hello, collections, classes_and_interfaces,
enums_and_match, optional_and_result, generics_and_closures

Programs that do a job: csv_to_json, inventory_report, event_timeline,
route_finder, dice_simulation, http_server

Every 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.md so the
next example follows it.

scripts/run-examples.sh compiles and runs each example and diffs its
output. It is the shared contract, and it takes nothing but a mux binary:

MUX_BIN=/path/to/mux ./scripts/run-examples.sh            # check
MUX_BIN=/path/to/mux ./scripts/run-examples.sh --update   # re-record

.github/workflows/examples.yml builds mux-compiler main and 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 main rather than a release

An 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:

  • mux-runtime#50 - next_range returned only the lower half of its range
  • mux-runtime#51 - string.length() counted bytes, not characters
  • mux-runtime#52 - JSON integers became floats; values past 2^53 changed
  • mux-runtime#53 - JSON object keys were re-ordered alphabetically
  • mux-compiler#394 - ICE writing a captured variable from inside a match arm
  • mux-compiler#395 - expressions could not span lines; no trailing comma in calls
  • mux-compiler#397 - a match arm's pattern binding leaked into later arms
  • mux-compiler#391 - importing a type whose interface was not in scope was an ICE
  • plus a nested function reading an enclosing local, also an ICE

The last commit retires the workarounds those fixes made unnecessary:
dice_simulation uses random.next_range again and rolls a uniform 1-6 rather
than never exceeding 3, and http_server reads status 201 rather than 201.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_server compares a quoted path ("\"/echo\""), because Json has
    only stringify and a string field arrives JSON-encoded with no way to strip
    the quotes (mux-compiler#392, #389).
  • route_finder imports Collection by hand. That is now a clean diagnostic
    rather 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 main at 9b32db3 - the
merge of #396 - with the runtime its lockfile resolves.

DerekCorniello and others added 3 commits August 12, 2026 21:45
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.
@greptile-apps

greptile-apps Bot commented Aug 14, 2026

Copy link
Copy Markdown

Greptile Summary

The PR adds twelve deterministic Mux examples and a shared verification runner, along with CI that builds the compiler and checks recorded output.

  • Adds language-focused and end-to-end example programs with documentation and expected output.
  • Adds update and verification modes for recorded baselines.
  • Adds GitHub Actions coverage against mux-compiler main.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

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

Comment thread scripts/run-examples.sh
Comment thread examples/csv_to_json/main.mux
Comment thread scripts/run-examples.sh Outdated
Comment thread README.md Outdated
--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.
Comment thread scripts/run-examples.sh
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.
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.

1 participant