cc-libs/docs/adrs/adr-0005-craftos-pc-harness.md

6.0 KiB

ADR 0005: CraftOS-PC As The Local Harness

Status

Accepted

Date

2026-06-08

Context

This repository targets CC:Tweaked on Minecraft 1.21. The Lua we ship runs inside the ComputerCraft sandbox: it depends on os.pullEventRaw, peripheral, rednet, textutils.serializeJSON, modem channels, and a handful of CC-specific globals. Standard Lua (or LuaJIT) cannot execute this code as-is, so a normal local test harness was never a serious option.

In practice contributors have been running the code in two places:

  • In-game on a real Minecraft server, which is slow to iterate on.
  • In CraftOS-PC (https://www.craftos-pc.cc/), a desktop emulator that ships the same ROM/BIOS as CC:Tweaked, supports modem peripherals via periphemu, and can run fully headless (--cli --headless --script <file>).

CraftOS-PC has been the de facto local harness for months, but that fact lived only as a single line in CLAUDE.md. There was no install guide, no minimum version, and just install did not check that the binary was present. The recent upgrade from v2.6.6 → v2.8.3 (the first one in years) made it obvious that this dependency needed to be made explicit.

Decision

Treat CraftOS-PC as a first-class local development dependency.

  • Minimum version v2.8.3 — recent enough to have the current CC:Tweaked ROM, old enough that contributors already on a 2.8.x build will not be forced to upgrade again immediately.
  • Documented install in docs/install-craftos-pc.md, with a SHA-256-verified macOS flow and pointers to the official Windows/Linux artifacts.
  • Documented upstream navigation in docs/craftos_pc_glossary.md, covering CLI flags, mounts, periphemu, save data, and troubleshooting pages.
  • Verified by just install via check-install, which checks craftos, jq, luacheck, and openssl. check-craftos runs craftos --version and requires v2.8.3 or newer. Failure prints a one-line pointer to the install guide instead of a long stack trace.
  • Repository-local launch recipe. just trapos runs CraftOS-PC with --directory .craftos, keeps the macOS --rom /Applications/CraftOS-PC.app/Contents/Resources workaround, mounts the repository root read-only at /trapos, and mounts each manifest top-level directory read-only at its ComputerCraft root path. (Originally just craftos; renamed when a separate vanilla-emulator recipe was added — see ADR-0012.)
  • Vanilla launch recipe. just craftos launches CraftOS-PC under .craftos-vanilla/ with no mounts, for probes that should not see TrapOS files and for the just trapos-install end-to-end install verification. See ADR-0012.
  • just ci is the local verification entry point. Today it runs check-craftos, check, and test. Local Git hooks are installed by just install; see ADR-0011 for the current commit/push split.

The existing CLAUDE.md constraint ("Do not run Lua locally or add a test harness unless asked") is reframed rather than removed: there is still no standalone Lua harness, and we are not adding a Busted-style test runner. The harness is CraftOS-PC, invoked deliberately.

Consequences

  • Contributors must install CraftOS-PC before just install succeeds. The install guide makes this a 4-step copy/paste on macOS.
  • Contributors should use the local CraftOS-PC glossary first when researching emulator behavior, then follow the linked upstream pages for details.
  • Headless tests live under tests/ and are driven by just test. Basic boot tests still prove CraftOS-PC boots and the event queue works, and API behavior tests use /apis/libtest.lua for named cases and assertions. The Justfile launches /programs/runtest.lua, which discovers tests, invokes them with shell.run, and prints __TRAPOS_TEST_OK__ only after the suite passes.
  • Each test process is guarded by TRAP_CCLIBS_TEST_TIMEOUT_SECONDS, defaulting to 3, so a blocked ComputerCraft event loop fails quickly and prints captured output.
  • The macOS install symlinks the binary into /usr/local/bin, which makes CraftOS-PC unable to auto-discover the ROM that ships inside the .app bundle (Could not mount ROM). The test: recipe works around this by passing --rom /Applications/CraftOS-PC.app/Contents/Resources on Darwin. Linux (AppImage) and Windows (installer) auto-discover correctly, so no flag is passed there.
  • just trapos uses repository-local save data under .craftos/config/ and .craftos/computer/. This keeps emulator state out of ~/Library/Application Support/CraftOS-PC during repository work and keeps repo files visible through read-only mounts instead of copying them into the VM save.
  • just repl is a human-only interactive wrapper around just trapos --cli; automation and LLM agents must use headless just trapos --headless --exec '<lua>; os.shutdown()' (TrapOS dev env) or just craftos --headless --exec '<lua>; os.shutdown()' (vanilla emulator) invocations instead. ADR-0012 frames these as the canonical hypothesis-probe pattern.
  • The harness version becomes a project-level concern. When CC:Tweaked ships breaking changes that require a newer CraftOS-PC build, we bump the minimum version in docs/install-craftos-pc.md and check-craftos keeps contributors honest.
  • No CI integration yet. Running CraftOS-PC headless in GitHub Actions is feasible (the AppImage works on Ubuntu runners) but is out of scope here; the contract is local-only for now.

Future Work

  • API-loading smoke test. Extend the tests/ set with a script that requires /apis/eventloop, /apis/net, /apis/libtest, and the router, asserting the wiring loads without errors.
  • CI. Run just test on push using the Linux AppImage.
  • Pinned ROM. CraftOS-PC ships its own copy of the CC:Tweaked ROM per release. If we ever need to test against a specific in-game version, point CraftOS-PC at a vendored ROM via --rom (the same flag we already pass on macOS for a different reason).