Contributing to rotor #
Building #
Requires Go 1.25+:
git clone https://github.com/uproot/rotor && cd rotor
go build ./cmd/rotor
Running the tests #
go test ./internal/... -count=1 # full test suite
go test ./internal/diff/ -v -run TestDifferential # differential suite only (see below)
go test ./internal/luau/render/ -bench . -benchmem # renderer benchmarks
go test ./internal/spike/ -v # checker integration spike
go vet ./internal/... # required clean before commits
No Node is required to run rotor itself or the committed differential goldens. A clean clone still needs fixture dependencies installed inside testdata/diff/project, testdata/conformance/project, and testdata/transformers/project (plus tools/sidecar) before the project-layer, conformance, and real-package transformer suites run. Use bun install --no-save first; npm install --no-audit --no-fund still works as a fallback.
The differential suite (how rotor proves byte-parity) #
internal/diff compiles every fixture project under testdata/diff/project/src/ with rotor and byte-compares the output against committed goldens in testdata/diff/golden/ — which were generated by the real rbxtsc 3.0.0. A fixture passes only when rotor's output is byte-identical; the first diverging line is reported.
Adding a fixture:
- Write the TypeScript in
testdata/diff/project/src/(it must compile cleanly under rbxtsc). - Regenerate goldens:
powershell -File tools/oracle/oracle.ps1(this is the only step that needs the JS toolchain — the script prefers Bun for fixture installs, falls back to npm, and runs the pinnedroblox-ts@3.0.0over the fixture project). - Enable the fixture in
internal/diff/manifest.goand rungo test ./internal/diff/ -v.
Existing goldens must stay byte-unchanged when regenerating — git diff testdata/diff/golden/ should only show your new files.
Releasing a new version #
rotor's version is defined in code: internal/version/version.go. Maintainer flow:
- Bump the
Versionconstant (e.g."1.4.0") andpackage.json'sversionin lockstep (the npm shim derives its binary-download URL from it) and commit. - Tag and push — the tag must match the constant:
git tag v1.4.0
git push origin v1.4.0
The tag triggers the release GitHub Actions workflow, which verifies the tag matches internal/version, runs the test suite, cross-builds rotor for Windows/macOS/Linux (amd64 + arm64) via GoReleaser, and publishes the archives, checksums, and GitHub Release automatically. A tag that doesn't match the code version fails the workflow before anything is published.
Project docs #
- User documentation:
docs.md - Design doc:
docs/superpowers/specs/2026-06-05-rotor-design.md - Luau toolchain design:
docs/superpowers/specs/2026-06-12-rotor-luau-toolchain-design.md - Full roadmap with every phase and task:
roadmap.md - Phase plans:
docs/superpowers/plans/· porting digests (the transformer's source of truth):docs/superpowers/research/
Ground rules #
- Byte-parity is law: everything that compiles must be byte-identical to
rbxtsc3.0.0. Never silently emit different output — fail loudly with a "not yet supported" diagnostic instead. tsgo/is a generated mirror — never edit it by hand; regenerate withgo run ./tools/mirror.go vet ./internal/...must be clean before commits.