test(craftos): rename harness success marker

This commit is contained in:
Guillaume ARM 2026-06-08 04:28:56 +02:00
parent 3450d6e258
commit 1010e0d844
12 changed files with 17 additions and 399 deletions

View File

@ -1,160 +0,0 @@
# CraftOS Just Recipes Plan
## Goal
Add repository-local CraftOS-PC launch recipes:
- `just craftos` launches CraftOS-PC with repo-local save data under `.craftos`.
- `just repl` launches the same environment with `--cli` for human interactive use only.
- `just install` delegates tool validation to a new `just check-install` recipe.
## Findings
CraftOS-PC docs confirm `-d` / `--directory` changes the save data root. With `-d .craftos`, generated files should be under `.craftos/config/global.json`, `.craftos/config/0.json`, and `.craftos/computer/0/`, not directly as `.craftos/global.json`.
CraftOS-PC docs describe `-C` as a CCEmuX compatibility flag for the computer-data directory. Do not make `-C .` the default unless an isolated probe proves it gives the desired repo layout without polluting the repository root.
`just` runs recipes from the Justfile directory by default, and `justfile_directory()` is available. Use it to anchor `.craftos` and mount paths to the repository root even when `just` is invoked from a subdirectory.
`manifest.json` already lists the shipped files. Its top-level directories are currently `startup`, `servers`, `programs`, and `apis`.
## Recommended Approach
Use `--directory` for persistent CraftOS-PC state and command-line mounts for repository files.
Do not use `-C .` as the normal path to expose this repository. It controls saved computer contents, while `--mount-ro` directly models the intended behavior: make repo files available in the ComputerCraft filesystem without copying them into a VM save.
Mount the repository root at `/trapos` so existing code can read `/trapos/manifest.json`. Also mount each manifest top-level directory at its ComputerCraft root path, for example `/apis`, `/programs`, `/servers`, and `/startup`.
Prefer read-only mounts by default. Use explicit caller-provided args for unusual cases.
## Implementation Steps
1. Add `.craftos/.gitignore` so the directory exists in the repo while generated CraftOS-PC state stays untracked.
```gitignore
*
!.gitignore
```
2. Add `check-jq` to `Justfile`.
3. Add `check-luacheck` to `Justfile` so install checks every host-side CLI requirement explicitly.
`check-jq` and `check-luacheck` are tool-presence checks only (verify the binary is on `$PATH`), mirroring the shape of the existing `check-craftos`. They do not run lint.
4. Add `check-install: check-craftos check-jq check-luacheck`.
5. Change `install` from `install-git-hooks check-craftos` to `install-git-hooks check-install`.
6. Change `check` to depend on `check-luacheck` and keep `luacheck .` as the command. Because `check-luacheck` only verifies presence (step 3), no lint runs twice.
Leave `ci: check-craftos check` unchanged. `ci` deliberately does not pull in `check-jq`: the pre-commit path does not use `jq`, so adding it would inflate the hook's required tooling for no benefit. The two dep chains (`install → check-install`, `ci → check-craftos + check → check-luacheck`) are intentional, not an oversight to unify later.
7. Add `craftos *args: check-install`.
Recommended behavior:
- Use `--directory "{{justfile_directory()}}/.craftos"`.
- Keep the existing macOS `--rom /Applications/CraftOS-PC.app/Contents/Resources` workaround.
- Add `--mount-ro "/trapos={{justfile_directory()}}"`.
- Use `jq` over `manifest.json` to derive unique top-level directories and add one `--mount-ro "/<dir>={{justfile_directory()}}/<dir>"` per directory.
- Forward user args after the recipe defaults. `just` forwards flags to variadic recipes normally, so callers should use commands like `just craftos --headless` and `just craftos --exec 'print("__READY__"); os.shutdown()'` without an extra `--` separator.
Build the recipe-generated argv with quoted arguments and invoke `craftos` with forwarded user args as `"$@"` so paths and `--exec` code containing spaces survive. Do not use unquoted `$mount_args` word splitting, and do not use `{{args}}` for forwarding because it does not preserve shell quoting.
Sketch:
```just
# Launch CraftOS-PC with repo-local data and read-only repo mounts.
# Pass args through to `craftos`, for example:
# just craftos --headless --exec 'print("__READY__"); os.shutdown()'
[positional-arguments]
craftos *args: check-install
#!/usr/bin/env bash
set -euo pipefail
repo='{{justfile_directory()}}'
argv=(--directory "$repo/.craftos")
if [ "$(uname -s)" = "Darwin" ]; then
argv+=(--rom /Applications/CraftOS-PC.app/Contents/Resources)
fi
argv+=(--mount-ro "/trapos=$repo")
while IFS= read -r dir; do
argv+=(--mount-ro "/$dir=$repo/$dir")
done < <(jq -r '.files[] | split("/")[0]' manifest.json | sort -u)
exec craftos "${argv[@]}" "$@"
```
Verify the shebang recipe receives variadic args as `"$@"` before committing. If it does not, use a linewise `[positional-arguments]` recipe that delegates to `bash -c '...' craftos "$@"`; do not fall back to `{{args}}`.
8. Add `repl` as an interactive human-only wrapper.
```just
# Human-only interactive REPL. LLM agents must not execute this command.
repl:
@just craftos --cli
```
9. Add the same `just repl` restriction to `CLAUDE.md` under constraints or boot/install guidance.
10. Update `DEVELOPMENT.md` requirements to include `jq`.
11. If the final mount behavior changes the local harness contract, update `docs/install-craftos-pc.md` or ADR-0005 accordingly.
## Probe Before Finalizing Mounts
Run probes in `/var/folders/l7/c5f9hw8j52zg5rx0vwz7_4d40000gn/T/opencode`, not in the repository first.
Compare these cases:
1. Plain repo-local data directory:
```sh
craftos -d <temp-data> --headless --exec 'print("__READY__"); os.shutdown()'
```
2. CCEmuX computer-data override:
```sh
craftos -d <temp-data> -C <temp-computers> --headless --exec 'print("__READY__"); os.shutdown()'
```
3. Explicit mounts:
```sh
craftos -d <temp-data> \
--mount-ro /trapos=<repo> \
--mount-ro /apis=<repo>/apis \
--mount-ro /programs=<repo>/programs \
--mount-ro /servers=<repo>/servers \
--mount-ro /startup=<repo>/startup \
--headless \
--exec 'print(fs.exists("/apis/net.lua")); print(fs.exists("/trapos/manifest.json")); os.shutdown()'
```
4. macOS `-d` + `--rom` combo (the recipe's actual shape):
```sh
craftos -d <temp-data> \
--rom /Applications/CraftOS-PC.app/Contents/Resources \
--mount-ro /trapos=<repo> \
--headless \
--exec 'print(fs.exists("/trapos/manifest.json")); os.shutdown()'
```
This must succeed before committing the macOS branch of the recipe — `-d` changes data-root resolution, and the existing test recipe only proves `--rom` works without `-d`.
Choose mounts unless `-C` demonstrably gives the desired repo-root ComputerCraft layout without repository pollution.
## Verification
Run:
- `just --list`
- `just check-install`
- `just craftos --help`
- `just craftos --headless --exec 'print("__READY__"); os.shutdown()'`
- `just test`
- `git status --short`
Do not run `just repl` as an LLM agent.

View File

@ -1,144 +0,0 @@
# Goo Program Plan
## Goal
Add a turtle program named `goo` for automating Just Dire Things goo block processing.
The turtle is placed directly below the goo block. It should run forever, adapt to its inventory, feed the goo when it is not alive, place supported process blocks around the goo, and mine the resulting crystal blocks.
## Command
- `goo start` starts the automation loop.
- `goo help` / `goo --help` prints usage.
- `goo version` / `goo --version` prints the version.
## Starting Position
The turtle must start directly below the goo block:
```text
[goo]
[turtle]
[ground]
```
The turtle cannot move down because of the ground. The block below the goo is the turtle's starting cell, so the bottom side must be handled last and from an adjacent position.
## Goo Detection
Use `turtle.inspectUp()` from the starting position.
Supported goo blocks:
- `justdirethings:gooblock_tier1`
- `justdirethings:gooblock_tier2`
- `justdirethings:gooblock_tier3`
- `justdirethings:gooblock_tier4`
Parse the tier from the block name. Re-inspect regularly so the program adapts if the goo tier changes.
## Alive Handling
The goo is ready when:
```lua
inspected.state and inspected.state.alive == true
```
If `alive` is false or nil, the turtle should use the appropriate feeding item directly on the goo with `turtle.placeUp()`.
Feeding items are ordered arrays so preference is data-driven:
```lua
local FEEDING_ITEMS_BY_TIER = {
[1] = { 'minecraft:sugar', 'minecraft:rotten_flesh' },
[2] = { 'minecraft:nether_wart' },
[3] = { 'minecraft:chorus_fruit' },
[4] = { 'minecraft:sculk' },
};
```
Tier 1 naturally prefers sugar because it appears first.
If no valid feeding item is present, log a useful message and wait forever for the player to add one.
## Process Blocks
Supported process blocks and required goo tiers:
```lua
local PROCESS_ITEMS = {
['minecraft:iron_block'] = { tier = 1, label = 'iron' },
['minecraft:coal_block'] = { tier = 1, label = 'coal' },
['mekanism:block_charcoal'] = { tier = 1, label = 'charcoal' },
['minecraft:gold_block'] = { tier = 2, label = 'gold' },
['minecraft:diamond_block'] = { tier = 3, label = 'diamond' },
['minecraft:netherite_block'] = { tier = 4, label = 'netherite' },
};
```
Rules:
- A goo tier can process items whose required tier is less than or equal to the goo tier.
- Lower-tier goo skips higher-tier items and logs the required tier.
- Tier 4 goo can process every supported item.
- Materials may be mixed; a full six-block batch of one material is not required.
- The program should continue forever, even if no eligible process blocks are currently present.
## Six-Side Handling
The program should work all six sides of the goo:
- top
- north
- south
- east
- west
- bottom
Horizontal sides:
- Move from center to adjacent floor position.
- Use `inspectUp`, `digUp`, and `placeUp` to work the side target.
- Return to center.
Top side:
- Move to an adjacent floor position.
- Climb up twice when the side path allows it.
- Face the top target.
- Use `inspect`, `dig`, and `place`.
- Return to center.
Bottom side:
- Ensure the goo is alive before leaving the center.
- Move to an adjacent floor position and face the original center cell.
- Work the original center cell with `inspect`, `dig`, and `place`.
- If a process block was placed in the bottom position, wait outside until it is consumed or transformed.
- Mine the resulting crystal block, then return under the goo when the center is clear.
## Crystal Mining Rule
After placement, the goo transforms process blocks into crystal blocks that should be mined with a pickaxe-equipped turtle.
No tag or exact crystal ID check is required. For any goo target position:
- Empty means available for placement.
- Known process material means already placed and still processing; do not dig.
- Goo block means protected; do not dig.
- Any other block means processed result/crystal; use the appropriate `turtle.dig*` call.
## First-Iteration Assumptions
- The area around the turtle has clear walking space.
- The program does not dig movement-path obstacles.
- The turtle has a suitable pickaxe equipped for crystal mining.
- Runtime validation must happen in CC:Tweaked/CraftOS-PC or in-game; local Lua execution is not available for this repo.
## Integration Notes
- Ship `programs/goo.lua` via `install.lua` `LIST_FILES`.
- Document `goo` in `README.md`.
- Add `turtle` to `.luacheckrc` globals.
- Run `just check` after Lua edits.

View File

@ -1,78 +0,0 @@
# Repo Fix Plan
## Findings
### Medium: Fresh install does not create `/servers`
Reference: `install.lua:32-38`
`LIST_FILES` downloads files under `servers/`, but `install.lua` only creates `/programs`, `/apis`, and `/startup`. On a fresh ComputerCraft machine, `wget ... servers/ping-server.lua` can fail because the parent directory does not exist.
Fix:
- Add `fs.makeDir('/servers');` before downloading files.
- Bump `install.lua` `_VERSION`.
### Medium: `cube set-boot <machineId>` cannot clear boot safely
Reference: `programs/cube.lua:177-204`, `servers/cube-server.lua:57-60`
The documented behavior says an empty command deletes the boot hook. The client sends `nil` when no command is provided, and the server calls `writeFile('/.cubeboot', startupCommand)`. Writing `nil` can error, and even an empty string currently leaves an empty file instead of deleting `/.cubeboot`.
Fix:
- In `servers/cube-server.lua`, if `startupCommand == nil or startupCommand == ''`, delete `/.cubeboot` and reply `true`.
- Otherwise write the command.
- Bump `servers/cube-server.lua` `_VERSION`.
- Optionally adjust the client message to say `boot DELETED` only when the server replies successfully.
### Medium: `deploy-file` reports success even when writes fail
Reference: `servers/cube-server.lua:62-66`, `programs/cube.lua:242-248`
`deploy-file` ignores the return value from `writeFile` and always replies `true`. If a parent directory is missing, the destination is read-only, or `fs.open` fails, the client counts the file as transferred.
Fix:
- Reply with the boolean result of `writeFile`.
- Have the client keep the existing error print when `res` is false.
- Bump `servers/cube-server.lua` `_VERSION`.
### Medium: Deployment cannot create new nested directories
Reference: `servers/cube-server.lua:24-35`, `programs/cube.lua:20-38`
`cube deploy` sends file paths recursively, but the server writes files directly without creating parent directories. This fails for new directories that do not already exist on the target cube.
Fix:
- Before writing a deployed file, create its parent directory when needed.
- Keep behavior minimal: derive the parent path from `payload.path`, call `fs.makeDir(parent)` if not empty and not present, then write.
- Return false if `payload` is malformed or the file write fails.
### Low: `set-boot` help says `[command]`, but only one argument is accepted
Reference: `programs/cube.lua:6`, `programs/cube.lua:325`
`local cubeCommand, firstArg, secondArg = ...` means `cube set-boot 12 mining turtle start` only sends `mining`; extra words are dropped. This matters for shell commands with spaces.
Fix:
- Use `table.pack(...)` to collect all arguments.
- For `set-boot`, concatenate arguments after the machine id with spaces.
- Preserve existing aliases and help behavior.
- Bump `programs/cube.lua` `_VERSION`.
## Proposed Order
1. Fix `install.lua` to create `/servers`.
2. Fix `cube-server.lua` boot clearing and deploy write reporting.
3. Add parent-directory creation for deployed files.
4. Fix multi-word `cube set-boot` command parsing.
5. Update module versions for changed files.
## Verification
Manual/runtime verification is required inside ComputerCraft or CraftOS-PC because this repo has no runnable local test harness.
Suggested checks:
- On a clean machine, run the installer and confirm `/servers/*.lua` downloads.
- Run `cube set-boot <id>` and confirm `/.cubeboot` is deleted remotely.
- Run `cube set-boot <id> program arg` and confirm the full command is stored.
- Deploy a file inside a new nested directory and confirm it exists remotely.
- Force a bad deploy path or write failure and confirm the client reports an error instead of counting success.

View File

@ -13,7 +13,7 @@ Use `docs/README.md` as the entrypoint for CC:Tweaked, CraftOS-PC, Advanced Peri
- Do not add a standalone Lua test harness unless asked. Local execution happens through the CraftOS-PC harness (see `docs/install-craftos-pc.md`, `docs/craftos_pc_glossary.md`, and ADR-0005); code otherwise executes in-game.
- Do not run `just repl` as an LLM agent; it is a human-only interactive CraftOS-PC wrapper. Use `just craftos --headless ...` for automated probes.
- When changing behavior, add as many useful CraftOS-PC tests as practical. It is acceptable to skip tests that require human-only validation, such as complex turtle motion, in-game UX feel, or visual approval, but still add unit-style non-regression tests for deterministic parts when possible.
- Use `/apis/libtest.lua` for CraftOS-PC test scripts under `tests/`; scripts must print `__READY__` only after all assertions pass.
- Use `/apis/libtest.lua` for CraftOS-PC test scripts under `tests/`; scripts must print `__TRAPOS_TEST_OK__` only after all assertions pass.
- After editing Lua, run `just check` and fix all `luacheck` warnings.
- Use 2-space indent, semicolons, and `local function`.
- `require` paths are absolute ComputerCraft paths, for example `require('/apis/net')()`.
@ -22,7 +22,7 @@ Use `docs/README.md` as the entrypoint for CC:Tweaked, CraftOS-PC, Advanced Peri
## Architecture
- `apis/eventloop.lua` is the single-threaded event loop around `os.pullEventRaw`; consider using it everywhere async behavior is needed. A handler that returns `api.STOP` auto-unregisters.
- `apis/libtest.lua` is the lightweight CraftOS-PC test helper used by scripts under `tests/`; it provides assertions, verbose case output, failure reporting, and the `__READY__` success marker.
- `apis/libtest.lua` is the lightweight CraftOS-PC test helper used by scripts under `tests/`; it provides assertions, verbose case output, failure reporting, and the `__TRAPOS_TEST_OK__` success marker.
- `apis/net.lua` builds modem packet messaging, routing, and request/response RPC on the event loop. `sendRequest` returns `ok, result, packet` and defaults to a 0.5s timeout.
- A router (`/programs/router.lua`) must be running somewhere on the network; without it, packets lack `routerId`, `isPacketOk` rejects them, and cross-machine messaging silently fails.
- `servers/` listen for requests and start loops; `programs/` are clients that send requests and exit.

View File

@ -16,6 +16,6 @@ This creates `.env` from `.env.sample` when needed and installs the local Git ho
`just ci` is the local verification entry point. Today it verifies that `craftos --version` reports v2.8.3 or newer, runs `just check` for `luacheck`, and runs `just test` for CraftOS-PC headless tests. Use `just craftos` to launch CraftOS-PC with repo-local save data under `.craftos` and read-only mounts for `/trapos`, `/apis`, `/programs`, `/servers`, and `/startup`. `just repl` opens the same environment with `--cli` for human interactive use only; LLM agents must not run it. Use the CraftOS-PC glossary when adjusting `--headless`, `--exec`, `--script`, `--rom`, or `--mount-*` usage.
Tests live under `tests/` and run inside CraftOS-PC through `just test`. API-level tests should use `require('/apis/libtest')({ ... })`, register cases with `testlib.test(name, fn)`, and call `testlib.run()` at the end. `libtest` prints `__READY__` only when every case passes, which is the contract consumed by the shell harness. Pass `--verbose` to `just test` to list each test script; `libtest` also accepts `--verbose` and prints each case when script stdout is inspected.
Tests live under `tests/` and run inside CraftOS-PC through `just test`. API-level tests should use `require('/apis/libtest')({ ... })`, register cases with `testlib.test(name, fn)`, and call `testlib.run()` at the end. `libtest` prints `__TRAPOS_TEST_OK__` only when every case passes, which is the contract consumed by the shell harness. Pass `--verbose` to `just test` to list each test script; `libtest` also accepts `--verbose` and prints each case when script stdout is inspected.
Each CraftOS-PC test process is killed if it does not finish within `TRAP_CCLIBS_TEST_TIMEOUT_SECONDS`, defaulting to `3`. Override this in `.env` for slower local probes.

View File

@ -76,7 +76,7 @@ check-luacheck:
check-install: check-craftos check-jq check-luacheck
# Pass args through to `craftos`, for example:
# just craftos --headless --exec 'print("__READY__"); os.shutdown()'
# just craftos --headless --exec 'print("__TRAPOS_TEST_OK__"); os.shutdown()'
# Launch CraftOS-PC with repo-local data and read-only repo mounts.
[positional-arguments]
craftos *args: check-install
@ -128,14 +128,14 @@ test *args:
status="$?"; \
kill "$watchdog" >/dev/null 2>&1 || true; \
wait "$watchdog" >/dev/null 2>&1 || true; \
if grep -q __READY__ "$tmp"; then \
if grep -q __TRAPOS_TEST_OK__ "$tmp"; then \
rm -f "$tmp"; \
[ "$verbose" -eq 1 ] && printf '%s\n' "${green}PASS${reset} $script"; \
else \
if [ "$status" -eq 143 ]; then \
printf '%s\n' "${red}FAIL${reset} $script timed out after ${timeout_seconds}s" >&2; \
else \
printf '%s\n' "${red}FAIL${reset} $script did not print __READY__" >&2; \
printf '%s\n' "${red}FAIL${reset} $script did not print __TRAPOS_TEST_OK__" >&2; \
fi; \
cat "$tmp" >&2; \
rm -f "$tmp"; \

View File

@ -1,4 +1,4 @@
local _VERSION = '1.0.0';
local _VERSION = '1.1.0';
local function createLibTest(args)
local api = {};
@ -58,7 +58,7 @@ local function createLibTest(args)
end
end
print('__READY__');
print('__TRAPOS_TEST_OK__');
os.shutdown();
end

View File

@ -36,7 +36,7 @@ The existing `CLAUDE.md` constraint ("Do not run Lua locally or add a test harne
- 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`. Smoke tests still prove CraftOS-PC boots and the event queue works, and API behavior tests use `/apis/libtest.lua` for named cases and assertions. Each script is invoked as `craftos --headless --script <file>` and its stdout is grepped for `__READY__`. Adding another test means dropping a Lua file in `tests/` and adding it to the loop in the `test:` recipe.
- Headless tests live under `tests/` and are driven by `just test`. Smoke tests still prove CraftOS-PC boots and the event queue works, and API behavior tests use `/apis/libtest.lua` for named cases and assertions. Each script is invoked as `craftos --headless --script <file>` and its stdout is grepped for `__TRAPOS_TEST_OK__`. Adding another test means dropping a Lua file in `tests/` and adding it to the loop in the `test:` recipe.
- 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 craftos` 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.

View File

@ -15,7 +15,7 @@ ADR 0005 made CraftOS-PC the local harness. The first real behavior test, `tests
- Collect named cases.
- Print per-case progress only in verbose mode.
- Fail fast with a useful message.
- Print `__READY__` only after every assertion passes.
- Print `__TRAPOS_TEST_OK__` only after every assertion passes.
- Shut the ComputerCraft process down cleanly.
Those details are easy to copy incorrectly. A blocked eventloop test also showed that the shell harness needs a timeout and captured output so agentic debugging can proceed without manual interruption.
@ -37,7 +37,7 @@ end);
testlib.run();
```
`libtest` intentionally stays small. It provides named cases, `assertEquals`, `assertTrue`, `assertErrors`, verbose `RUN <name>` output, failure reporting, the `__READY__` success marker, and `os.shutdown()` at process end.
`libtest` intentionally stays small. It provides named cases, `assertEquals`, `assertTrue`, `assertErrors`, verbose `RUN <name>` output, failure reporting, the `__TRAPOS_TEST_OK__` success marker, and `os.shutdown()` at process end.
The shell harness keeps ownership of process-level concerns: CraftOS-PC launch flags, read-only mounts, stdout capture, and timeout enforcement through `TRAP_CCLIBS_TEST_TIMEOUT_SECONDS`.
@ -47,7 +47,7 @@ The shell harness keeps ownership of process-level concerns: CraftOS-PC launch f
- Tests that require human validation, such as complex turtle motion, in-game UX feel, or visual approval, may be skipped, but deterministic pieces should still get unit-style non-regression coverage.
- Test scripts remain normal ComputerCraft programs, not standalone Lua tests. They run through `just test`, not through a separate Lua test framework.
- `libtest` lives under `/apis` and is listed in `manifest.json`, so it can be required consistently in the mounted CraftOS-PC environment.
- The `__READY__` marker remains the single shell-level success contract.
- The `__TRAPOS_TEST_OK__` marker remains the single shell-level success contract.
## Future Work

View File

@ -44,7 +44,7 @@ User data (computer state, settings) lives in `~/Library/Application Support/Cra
When running headless tests on macOS, prefer passing the app bundle's resource directory explicitly if `craftos` is reached through the `/usr/local/bin/craftos` symlink:
```sh
craftos --headless --rom /Applications/CraftOS-PC.app/Contents/Resources --exec 'print("__READY__"); os.shutdown()'
craftos --headless --rom /Applications/CraftOS-PC.app/Contents/Resources --exec 'print("__TRAPOS_TEST_OK__"); os.shutdown()'
```
## Windows
@ -75,7 +75,7 @@ Must report `CraftOS-PC v2.8.3` or newer. Once this works, `just install` will s
To verify that CraftOS-PC can boot a computer, not only print its executable version, run:
```sh
craftos --headless --exec 'print("__READY__"); os.shutdown()'
craftos --headless --exec 'print("__TRAPOS_TEST_OK__"); os.shutdown()'
```
On macOS, use the `--rom` form shown above if the command fails with `Could not mount ROM`.
@ -93,7 +93,7 @@ The recipe mounts the repository root read-only at `/trapos` so code can read `/
Pass CraftOS-PC flags directly after the recipe name, for example:
```sh
just craftos --headless --exec 'print("__READY__"); os.shutdown()'
just craftos --headless --exec 'print("__TRAPOS_TEST_OK__"); os.shutdown()'
```
`just repl` delegates to `just craftos --cli` for human interactive use only. LLM agents must not run `just repl`.

View File

@ -1,4 +1,4 @@
-- Smoke test: prove CraftOS-PC boots and stdout flushes.
-- Invoked via `craftos --headless --script tests/boot.lua` from `just test`.
print('__READY__');
print('__TRAPOS_TEST_OK__');
os.shutdown();

View File

@ -3,6 +3,6 @@
os.queueEvent('craftos-ready');
local ev = os.pullEventRaw();
if ev == 'craftos-ready' then
print('__READY__');
print('__TRAPOS_TEST_OK__');
end
os.shutdown();