{"id":51019717,"url":"https://github.com/pyrex41/bifrost","last_synced_at":"2026-06-21T15:30:31.674Z","repository":{"id":365058130,"uuid":"1268586005","full_name":"pyrex41/bifrost","owner":"pyrex41","description":null,"archived":false,"fork":false,"pushed_at":"2026-06-15T16:39:57.000Z","size":36,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-15T18:22:52.537Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pyrex41.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-13T17:51:10.000Z","updated_at":"2026-06-14T19:23:06.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/pyrex41/bifrost","commit_stats":null,"previous_names":["pyrex41/bifrost"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/pyrex41/bifrost","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pyrex41%2Fbifrost","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pyrex41%2Fbifrost/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pyrex41%2Fbifrost/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pyrex41%2Fbifrost/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pyrex41","download_url":"https://codeload.github.com/pyrex41/bifrost/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pyrex41%2Fbifrost/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34616509,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-21T02:00:05.568Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-21T15:30:28.438Z","updated_at":"2026-06-21T15:30:31.664Z","avatar_url":"https://github.com/pyrex41.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Bifrost\n\n**A cross-implementation META test harness for the Shen language ports.**\n\n\u003e In Norse myth, *Bifrost* is the burning rainbow bridge between worlds. Here it\n\u003e is the bridge that checks the worlds **agree**: it runs the *same* programs and\n\u003e behaviours across **all** the Shen implementations and asserts they produce\n\u003e the same observable result (differential / conformance testing).\n\nBifrost sits alongside [Ratatoskr / Yggdrasil](../ratatoskr) in the same Norse\nlineage — Ratatoskr is the squirrel that runs *up and down* the world-tree (the\ntwo-stage shaker); Bifrost is the bridge that verifies the worlds at the ends of\nthe tree agree.\n\nDifferential testing is its origin, but Bifrost is now three things:\n\n1. **a differential test harness** (this README's first half) — does every port\n   agree on the same input?\n2. **a reusable test framework** — any Shen project drops a\n   [`bifrost.suite.json`](#using-bifrost-from-another-shen-program-suite-manifests)\n   and runs *its own* suite across every port;\n3. **a Roswell-style front door** — [`run`/`eval`/`repl`/`impls`/`use`/`install`/`build`](#bifrost-as-a-shen-front-door-roswell-style)\n   give Shen one CLI across all ports, on Linux, macOS **and Windows**.\n\nIt is packaged as a [uv](https://docs.astral.sh/uv/) tool, so it runs with no\ninstall: `uvx --from git+https://…/bifrost bifrost …`.\n\n## The three-way distinction (read this first)\n\nThere are **three** different kinds of Shen test suite. Bifrost is the third one\nand is deliberately distinct from the other two:\n\n| Suite | What it tests | Owned by | Example |\n|-------|---------------|----------|---------|\n| **(a) Canonical kernel suite** | Does *this* port implement the Shen *spec* correctly? | The Shen kernel (`tests/`) | `(run \"README.shen\")` style kernel conformance |\n| **(b) Per-port unit tests** | Does *this* port's internals (reader, writer, GC, FFI…) behave? | Each port repo | `shen-go/cmd/shen/main_test.go` |\n| **(c) Bifrost (this repo)** | Do **all ports agree with each other** on the same input? | This repo | `(+ 0.1 0.2)` → are the answers the same string across ports? |\n\nBifrost never re-implements (a) or (b). It drives each port's *launcher* exactly\nthe way a user would from the shell, captures stdout, normalises launcher\nchatter, and **diffs the ports against each other** (or against a golden value).\n\n## What it covers\n\nThe corpus (`cases/*.json`, driven by `programs/*.shen`) includes:\n\n- **Behavioural parity** — arithmetic (incl. floats), list ops, string ops,\n  closures (incl. currying), `fix`, recursion, **tail calls** (a 100 000-deep\n  countdown that must not blow any stack), a small **Prolog** query, and\n  **`trap-error`** catchability.\n- **CLI parity** — `eval -e` prints the value; `(version)` / `--version` carry\n  the kernel version **41.2**; **stdin-EOF causes a clean exit** (no hang) on\n  every impl.\n- **Known divergences** (documented, *not* hard failures): **none currently** —\n  all previously-tracked divergences have converged (see below).\n- **Resolved divergences** (converged across all available impls; now asserted\n  as **hard agreements**, no longer tagged):\n  - ~~`float-formatting`~~ — **RESOLVED.** `(+ 0.1 0.2)` now prints the shortest\n    round-trippable form `0.30000000000000004` on *every* impl. shen-go\n    previously printed `0.300000` (`%f`, fixed in pyrex41/shen-go#11) and\n    shen-lua printed `0.3` (`%.14g` via `tostring`, fixed in\n    pyrex41/shen-lua#24). Exactly-representable floats such as `2.5` and `4.75`\n    also agree. Now asserted as a hard agreement — any impl that prints a\n    non-shortest-round-trip float is a real FAIL.\n  - ~~`int-div-zero`~~ — **RESOLVED.** `(/ 1 0)` now raises a *catchable* kernel\n    error on every impl, so `(trap-error (/ 1 0) …)` yields `divide-by-zero`\n    everywhere. shen-go previously returned `maxint`\n    (`9223372036854775807`); fixed on `fix-go-divzero-and-floatfmt`. Any impl\n    that fails to raise is now a real FAIL.\n  - ~~`hush-file-write`~~ — **RESOLVED.** Under `-q` (`*hush* = true`), `pr` to a\n    **file** stream now writes on *every* impl (`*hush*` gates only the standard\n    output stream). shen-lua (`fix/hush-pr-file-22`) and shen-rust\n    (`fix/hush-pr-file-2`) previously silenced the write (zero-byte file); both\n    fixed. Now asserted as a hard agreement — any impl that silences a\n    file-stream `pr` is a real FAIL.\n- **Heavy** (`--heavy`) — **Ratatoskr stage-1 parity**: run\n  `(ratatoskr.shake [\"tests/fib.shen\"] OUT)` on every host and assert the\n  produced `kernel.kl` + `ratatoskr.manifest` are **byte-identical** across\n  hosts. (User KL differs only by gensym counter, so it is *not* asserted.)\n\n### Expected vs. agreement modes\n\nEach case is one of two modes:\n\n- **`expect: output`** — assert each impl's normalised stdout equals a *golden*\n  value.\n- **`expect: agreement`** — no golden; assert **all available impls produce\n  identical** normalised output. If a case is tagged `known_divergence`, a\n  disagreement is reported as **DIVERGE** (documented) instead of **FAIL**.\n\n## Running it\n\n```bash\n# Standalone runner — works with just python3, no third-party deps:\npython3 bifrost.py                 # light cases + matrix; exit !=0 on real FAIL\npython3 bifrost.py --heavy         # also run the ratatoskr stage-1 parity case\npython3 bifrost.py --list          # list discovered impls + cases\npython3 bifrost.py --only int-mul float-add-imprecise\npython3 bifrost.py --impls shen-cl,shen-go\npython3 bifrost.py --json          # machine-readable result blob\n\n# Optional pytest wrapper (same corpus; divergences become xfail):\npytest\nBIFROST_HEAVY=1 pytest -k ratatoskr\n```\n\n`DIVERGE` rows are reported in their own section and **do not** fail the run.\nOnly real `FAIL` rows set a non-zero exit code.\n\n### Install\n\nBifrost is a single static **Go binary** (no runtime deps), distributed three\nways — pick whichever suits you:\n\n```bash\n# 1. Go toolchain — installs to $GOBIN:\ngo install github.com/pyrex41/bifrost@latest\n\n# 2. Prebuilt release binary (no toolchain) — download for your OS/arch from\n#    the GitHub Releases page (produced by GoReleaser on each v* tag), unpack,\n#    put `bifrost` on your PATH.\n\n# 3. uvx — builds the Go binary on your machine and runs it (needs Go installed):\nuvx --from git+https://github.com/pyrex41/bifrost bifrost --list\nuvx --from . bifrost impls                            # from a local checkout\n```\n\nThe binary embeds `adapters.json` + the corpus, so it is self-contained — but\nthe Shen *ports* it drives are resolved on your machine. Point it at your ports\nwith the per-impl env vars (below) or a project-local `adapters.json`;\nresolution order is `$BIFROST_ADAPTERS` → `./adapters.json` → the embedded\ndefault.\n\n\u003e The Python implementation (`bifrost.py`) remains in the repo as the reference\n\u003e oracle the Go binary is tested against (`bifrost --json` is byte-identical to\n\u003e `python bifrost.py --json` across the corpus); `go test ./...` plus a\n\u003e Windows/Linux/macOS CI matrix guard the binary.\n\n### Shake-then-run (deploy-path parity)\n\n`--shake` runs each **script-mode** program through\n[Ratatoskr](../ratatoskr): it tree-shakes the program once, builds a *standalone\nartifact* for every target (Lisp/Lua/Go/Rust/JS/Julia), runs each artifact, and\ndiffs them. This checks the real stand-alone deploy path, not just\nload-from-source.\n\n```bash\npython3 bifrost.py --shake --only recursion-fib-file   # all buildable targets\npython3 bifrost.py --shake --impls shen-lua,ShenScript # just the fast ones\n```\n\nArtifacts map onto their impl column (lisp→`shen-cl`, lua→`shen-lua`,\ngo→`shen-go`, rust→`shen-rust`, js→`ShenScript`, julia→`shen-julia`,\nscheme→`shen-scheme`, swift→`shen-swift`) — all eight ports now have a\nRatatoskr builder. Needs the per-target toolchains\n(`sbcl`/`luajit`/`go`/`cargo`/`node`/`julia`/`chez`/`swift`) and\n`$BIFROST_RATATOSKR_DIR` (default `../ratatoskr`); missing toolchains are\nskipped, not failed. This mode is minutes, not seconds: go/rust compile from\nscratch, and the **julia** target AOT-bakes a per-program sysimage\n(PackageCompiler, ~250MB, several minutes) — the deploy artifact for\nShen-on-Julia, analogous to the Lisp saved image. The **scheme** target\ncompiles the shaken slice with shen-scheme's own `kl-\u003escheme` into a\nself-contained Chez program; the **swift** target drives the shen-swift\ntree-walking interpreter on the shaken slice in `--shaken` mode (booting a\n~200-line kernel instead of the full ~2500-line kernel).\n\n`--shake` drives the **Go `ratatoskr` binary** (resolved on `$PATH`, else\n`$RATATOSKR_BIN`, else `$BIFROST_RATATOSKR_DIR` / a sibling `../ratatoskr`); no\nPython is involved. The heavy `ratatoskr-shake-parity` case (`--heavy`) likewise\nshakes on each host via the Go ratatoskr and diffs the kernel.kl/manifest md5s.\n\n## How implementations are located\n\nBifrost **auto-detects** each port. For every impl it resolves a launcher path\nin this order, and **skips-with-report** (never errors) any that is missing:\n\n1. the impl's env-var override (e.g. `$BIFROST_SHEN_GO`), if it points at an\n   existing file;\n2. the first existing path in the adapter's `default_paths`.\n\nDefaults (see [`adapters.json`](adapters.json)):\n\n| Impl | Env override | Default launcher |\n|------|--------------|------------------|\n| `shen-cl` | `BIFROST_SHEN_CL` | `…/shen-cl/bin/sbcl/shen` (also `clisp`, `ecl`) |\n| `shen-go` | `BIFROST_SHEN_GO` | `.bin/shen-go` (build: `go build -o .bin/shen-go ./cmd/shen`) |\n| `shen-rust` | `BIFROST_SHEN_RUST` | `…/shen-rust/target/release/shen-rust` |\n| `shen-lua` | `BIFROST_SHEN_LUA` | `…/shen-lua/bin/shen` (needs `luajit`) |\n| `ShenScript` | `BIFROST_SHENSCRIPT` | `node …/ShenScript/bin/shen.js` |\n| `shen-scheme` | `BIFROST_SHEN_SCHEME` | `…/shen-scheme/_build/bin/shen-scheme` (Chez) |\n| `shen-julia` | `BIFROST_SHEN_JULIA` | `…/shen-julia/bin/shen` (needs `julia`) |\n| `shen-swift` | `BIFROST_SHEN_SWIFT` | `…/shen-swift/.build/release/shen-swift` (needs `swift`) |\n\nAll eight target ShenOSKernel **41.2**. Run `bifrost impls --versions` to see the\nlive per-port kernel version, install state, and which is active.\n\nTo build shen-go locally into the gitignored `.bin/`:\n\n```bash\ngo build -o .bin/shen-go -C /Users/reuben/projects/shen/shen-go ./cmd/shen\n```\n\n### Launcher quirks Bifrost encodes (real cross-impl differences)\n\n- **shen-cl** *requires* `-q` for clean `eval`/REPL output.\n- **shen-lua** has **no** `--version` flag and **no** `script` subcommand — its\n  file path is `(load FILE)` (which echoes `(fn …)` and a `run time:` banner);\n  Bifrost's normaliser strips that chatter. It also has no clean script value\n  channel, so file-mode programs end with `(do (print …) (nl))`.\n- **shen-lua / shen-rust** must be driven **without** `-q` for normal cases and\n  for the ratatoskr parity case, or `pr` output is silenced (the\n  `hush-file-write` divergence). This is exactly why Bifrost's eval/script\n  templates for those two do **not** pass `-q`.\n\n## Bifrost as a Shen front door (Roswell-style)\n\nBeyond differential testing, Bifrost is the single **front door** for Shen\nacross every port — the way [Roswell](https://roswell.github.io/) (`ros`) is for\nCommon Lisp. The differential test matrix is still the default (bare `bifrost`);\nthese are added as verbs. They reuse the same `adapters.json` port registry, so\n\"run my program on shen-go\", \"which ports do I have\", and \"install shen-rust\"\nall share one source of truth.\n\n```bash\nbifrost run prog.shen [--impl X]   # run a .shen program on the active/chosen port\nbifrost eval -e '(+ 2 3)' [--impl X]\nbifrost repl [--impl X]            # interactive REPL (inherits your terminal)\nbifrost impls [--versions]         # list ports: state, kernel (live probe), status, active(*)\nbifrost use IMPL [--project]       # set the active port (global, or ./.bifrost-impl pin)\nbifrost install IMPL [--method M] [--git URL] [--ref R] [--force]\nbifrost build prog.shen OUT --target T [--run]   # standalone artifact (delegates to Ratatoskr)\n```\n\n### Choosing the implementation\n\n`run`/`eval`/`repl` resolve which port to use in this order (rustup-style — a\nper-command override beats a pin beats the global default):\n\n1. `--impl X` on the command line,\n2. a project pin file `./.bifrost-impl` (written by `bifrost use X --project`),\n3. the global default `~/.config/bifrost/impl` (written by `bifrost use X`),\n4. otherwise `shen-cl` if present, else the first available port.\n\n`bifrost impls --versions` probes each port's kernel version **live** (the port\nREADMEs' badges drift — e.g. shen-cl's says 41.1 but it reports 41.2), so the\nmatrix of who-is-on-what is always accurate.\n\n### Installing a port\n\nEach port declares an **install backend** in `adapters.json` (asdf/mise style):\n\n| method | ports | what runs |\n|---|---|---|\n| `brew` | shen-scheme (and shen-cl via `--method brew`) | `brew install \u003cformula\u003e` |\n| `npm` | ShenScript | `npm install -g shen-script` |\n| `luarocks` | shen-lua | `luarocks install shen` |\n| `git-build` | shen-cl, shen-go, shen-rust, shen-julia, shen-swift | clone (if absent) + the port's `build` recipe |\n\n`install` prechecks the required toolchain (and names the exact missing tool\nrather than failing opaquely), refuses `experimental` ports unless `--force`,\nis idempotent (skips an already-resolved launcher), and verifies the launcher\nresolves afterward.\n\n**Forks** (e.g. `pyrex41/shen-cl`) are first-class:\n- to *run* a fork, point the port's env var at your built binary\n  (`$BIFROST_SHEN_CL=/path/to/fork/bin/sbcl/shen`) — no install needed;\n- to *build* a fork, override the git-build source:\n  `bifrost install shen-cl --git https://github.com/me/shen-cl --ref my-branch`\n  (or `$BIFROST_SHEN_CL_GIT` / `$BIFROST_SHEN_CL_REF`). The git-build defaults\n  point at the `pyrex41` forks this workspace tracks.\n\n### Windows / Linux / macOS\n\nThe `bifrost` tool itself is pure-stdlib Python and runs on all three. The\nplumbing is OS-aware:\n\n- **Launcher resolution** — extensionless `default_paths` also match\n  `shen.exe` / `shen.cmd` / `shen.bat` on Windows (via `PATHEXT`); a\n  `.bat`/`.cmd`/`.sh` launcher is auto-wrapped (`cmd /c` / `sh`) so it runs\n  through Python's shell-less `subprocess`.\n- **Config** — the global active-impl pin lives in `%APPDATA%\\bifrost\\impl` on\n  Windows, `$XDG_CONFIG_HOME`/`~/.config/bifrost/impl` elsewhere.\n- **Per-OS adapter tweaks** — an adapter may carry an `os_overrides` block\n  keyed by `win32`/`darwin`/`linux` (shallow-merged over the adapter) to give a\n  port a platform-specific `default_paths`/`launcher`/template. See the\n  `_os_overrides_example` in [`adapters.json`](adapters.json) — e.g. driving\n  shen-lua on Windows by invoking `luajit` explicitly.\n\nWhat's *not* in Bifrost's hands is whether a given **port** runs on Windows —\nthat's each port's own story. Natively-compiled ports (shen-go, shen-rust,\nshen-cl, shen-scheme) produce a Windows `.exe`; interpreter ports work when\ntheir runtime is present (`node` for ShenScript, `luajit`, `julia`). As\neverywhere, point `$BIFROST_\u003cIMPL\u003e` at your launcher if auto-detection misses\nit. The `portability` CI job runs the cross-platform plumbing tests on\n`windows-latest` (and Linux/macOS) on every push.\n\n## Using Bifrost from another Shen program (suite manifests)\n\nBifrost is not only its own corpus — it is a **reusable framework** any Shen\nproject can point at its *own* test suite to run it across every supported\nport and assert the ports agree. A project written in Shen (e.g.\n[`../shen-cas`](../shen-cas)) gets cross-implementation conformance for free:\n\"does my suite produce the *same* result, and pass, on every supported port?\"\n\nA project plugs in by dropping a **`bifrost.suite.json`** manifest in its repo\nand running:\n\n```bash\npython3 /path/to/bifrost/bifrost.py --suite /path/to/project/bifrost.suite.json --heavy\n# or, checked out side-by-side:\npython3 ../bifrost/bifrost.py --suite ./bifrost.suite.json --heavy\n```\n\nPorts are resolved from Bifrost's own [`adapters.json`](adapters.json) (launcher\nlocations are machine-global, not project-specific), so the project supplies\n**only** what is project-specific: where its sources live and how its suite\nreports success.\n\n### Manifest schema\n\n```jsonc\n{\n  \"name\": \"my-project\",\n  \"root\": \".\",                       // project root; paths below resolve here.\n                                     //   default = the manifest's own directory\n  \"programs_dir\": \".\",               // where script-mode {file} programs live\n                                     //   (default = root)\n  \"default_cwd\": \".\",                // cwd the launcher runs in, so the suite's\n                                     //   relative (load \"src/...\") resolves\n                                     //   (default = root)\n  \"strip_line_prefixes\": [\"my loader banner\"],  // extra normaliser prefixes:\n                                     //   drop project-specific chatter lines\n  \"cases\": [                         // inline cases (preferred). Alternatively\n    {                                //   \"cases_dir\": \"bifrost/cases\" points at\n      \"name\": \"self-suite\",          //   a dir of *.json corpus files.\n      \"mode\": \"script\",              // \"script\" runs a .shen entrypoint\n      \"program\": \"load.shen\",        // your loader, which ends by running tests\n      \"expect\": \"agreement\",         // all ports must produce identical output\n      \"marker\": \"ALL PASS\",          // ...AND each must print this success token\n      \"heavy\": true,                 // big suite -\u003e use the long timeout\n      \"doc\": \"all ports agree and report ALL PASS\"\n    }\n  ]\n}\n```\n\n### How a project's entrypoint should behave\n\nThe `\"script\"` entrypoint (your `load.shen` or equivalent) should:\n\n1. load your sources,\n2. run your checks, printing **deterministic** output (identical across ports —\n   beware gensym counters, hash ordering, and float formatting, which Bifrost\n   will surface as a divergence),\n3. end by printing a **marker** line (e.g. `ALL PASS`) *iff* everything passed.\n\nBifrost runs it on every available port with the cwd set to your project root,\nnormalises launcher chatter (run-time banners, `(fn …)` load echoes, and\nshen-lua's trailing value echo — so your entrypoint may safely return a\nboolean), then asserts:\n\n| `expect` | `marker` | assertion |\n|----------|----------|-----------|\n| `agreement` | set | normalised stdout **byte-identical across all ports** AND the (shared) output contains the marker — *the strongest mode* |\n| `agreement` | — | byte-identical across all ports (pure differential) |\n| `marker` | set | each port prints the marker and exits cleanly; ports need **not** agree (the project's own harness is the sole judge) |\n| `output` | optional | normalised stdout equals `golden` (+ marker if set) |\n\nA worked, runnable example lives in\n[`examples/tiny-suite/`](examples/tiny-suite/) — a tiny self-test that passes\non every available port. shen-cas ships a real manifest at\n[`../shen-cas/bifrost.suite.json`](../shen-cas/bifrost.suite.json).\n\nAdd `--shake` to run a suite's script-mode entrypoint through the\n[deploy-path parity](#shake-then-run-deploy-path-parity) pipeline: each port's\n*standalone artifact* is built and diffed, not just the load-from-source run.\n\n### Scripting it (importable API)\n\n`bifrost.py` is importable; the manifest path is just sugar over it:\n\n```python\nimport bifrost\nsuite = bifrost.build_suite_from_manifest(\"path/to/bifrost.suite.json\")\nblob, results, available, skipped, cases = bifrost.run_suite(suite, include_heavy=True)\nprint(blob[\"cases\"][\"self-suite\"][\"verdict\"])   # PASS / FAIL / DIVERGE\n```\n\n## Adding a case\n\n1. If the case needs a `.shen` program, drop it in `programs/`. End it with a\n   single `(do (print EXPR) (nl))` top-level form so the output normalises\n   cleanly across all impls (shen-lua's `load` echoes values otherwise).\n2. Add an entry to a file under `cases/` (or make a new `*.json`):\n\n   ```json\n   {\n     \"name\": \"my-case\",\n     \"mode\": \"eval\",            // or \"script\" (uses \"program\": \"foo.shen\")\n     \"expr\": \"(+ 2 3)\",         // for eval mode\n     \"expect\": \"output\",        // or \"agreement\"\n     \"golden\": \"5\",             // required when expect == output\n     \"doc\": \"what this checks\"\n   }\n   ```\n\n   For an agreement case that is a *documented* difference, add\n   `\"known_divergence\": \"some-tag\"` and omit the golden.\n3. Run `python3 bifrost.py --only my-case` and confirm the matrix.\n\n## Layout\n\n```\n*.go                the bifrost Go binary (primary):\n  main.go             subcommand router + flag helpers\n  adapters.go         port registry: load / discover / resolve / os_overrides\n  run.go              launch path: build_argv, run, normalize, exec wrapping\n  front.go            verbs: run/eval/repl/impls/use\n  install.go          install backends + build (delegates to ratatoskr)\n  matrix.go,runmatrix.go  differential test matrix, --suite, reporting\n  shake.go            --shake + ratatoskr-parity (drive the Go ratatoskr)\n  *_test.go           cross-platform unit tests (go test ./...)\nbifrost.py          the original Python — kept as the reference oracle\ntest_bifrost.py     pytest wrapper over the same corpus (oracle checks)\npybin/              hatchling wheel that builds+bundles the Go binary for uvx\nadapters.json       per-impl launchers + arg templates + hush flags + install backends\ncases/*.json        data-driven case corpus (Bifrost's own suite)\nprograms/*.shen     .shen programs referenced by script-mode cases\nexamples/           worked third-party suite manifests (--suite)\n.goreleaser.yaml    release-binary build (on v* tags)\n.github/workflows/  go (build/test, win/linux/mac), portability, matrix, release\n```\n\nRun `python3 bifrost.py --suite PATH/bifrost.suite.json` to drive an external\nproject's suite (see *Using Bifrost from another Shen program* above).\n\n## CI\n\n[`.github/workflows/ci.yml`](.github/workflows/ci.yml) runs the matrix\nbest-effort. It is fine for CI to build/run only a subset of impls; missing\nimpls are **skipped and reported**, not failed.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpyrex41%2Fbifrost","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpyrex41%2Fbifrost","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpyrex41%2Fbifrost/lists"}