Build and run Compukters
Start from the Compukters repository. Clone with submodules, or run
git submodule update --init --recursive in an existing checkout. The pinned Rust VM is part of the build.
Minecraft builds use Architectury Loom for dependencies, mappings and development runs. Shared Minecraft modules
are packaged directly through their ordinary JAR variants (namedElements on 1.21.1) and the project’s Shadow
conventions; they do not
use Architectury Plugin transformations or its runtime Java agent. Register shared source sets through loom.mods
when adding a Minecraft module. The Minecraft 1.21.1 production archive is remapped after merging its dependencies.
Agent task worktrees
Create a ready task checkout with one command:
./worktree-task feat/display-api
It creates a new branch from the current local dev under the main workspace’s .worktrees/feat-display-api,
initializes host/compukter-vm at the revision pinned by that checkout, prepares an independent Gradle cache through
the sandbox cache workflow, and prints the command to enter it.
The original checkout can contain unrelated edits; they are preserved. Invoking the command from another parent
worktree still places the new checkout under the main workspace. It does not fetch, pull or switch the base branch.
Choose additional modules or create a task in an already initialized submodule repository:
./worktree-task feat/create-support --submodule addons/create
./worktree-task chore/addon-integration --all-submodules
./worktree-task fix/vm-execution --repo host/compukter-vm
./worktree-task fix/create-addon --repo addons/create
Submodule tasks start from local main and use names such as .worktrees/host-compukter-vm--fix-vm-execution.
Use --base <local-branch> or --path <directory> to override those defaults, and --no-submodules for work that
needs none. Extra --submodule options can be repeated. Modules are always initialized at the new checkout’s pinned
revisions, including recursive dependencies; the source checkout’s current module HEAD is not copied. Existing local
module Git objects are reused when they contain the pinned revision, without changing stored remote URLs. Missing
objects or nested modules can require network access. If GitHub SSH has no usable agent, an installed gh supplies
HTTPS authentication through command-scoped settings. Agents must run potential remote operations outside the sandbox.
Setup refuses existing destinations and branches. If submodule or cache preparation fails after checkout creation,
it preserves the branch and checkout and prints the location. Correct the problem and repeat the same command with
--resume; this requires a clean registered checkout on the expected task branch and preserves its current HEAD.
After making edits, finish any remaining setup manually rather than using resume. After verified integration into
dev (or submodule main), retain verification logs in the main checkout and remove only that completed task’s
worktree. New tasks start from the updated base.
Diagnose readiness
./worktree-task doctor
./worktree-task doctor --path .worktrees/feat-display-api
Diagnostics print OK, INFO, WARN or ERROR with the checkout/branch, committed submodule pins,
Java launcher (JDK 25 for Gradle), required commands, cache home/template and an actual reflink probe
between the template filesystem and checkout. An absent or cold cache is informational: the first build
may need network access. A populated cache does not prove that every offline task can run. Missing initialization of selected submodules (VM by default), pin mismatches, an invalid Java launcher, shared/symlinked cache homes or unsupported
reflinks produce a nonzero exit code. Unselected submodules and dirty files are reported without
repairing them. Submodule-only Rust checkouts skip Gradle/JDK/cache checks. The command does not fetch,
install dependencies or start a build; its temporary reflink probe files are removed.
Complete a task
Commit, merge and verify the integrated change first, then run from outside the checkout being removed:
./worktree-task finish feat/display-api
./worktree-task finish fix/vm-execution --repo host/compukter-vm --evidence /path/to/cargo-test.log
Independent nested Gradle builds with tracked sandbox wrappers are checked for active builds too. Their
.gradle-sandbox homes are disposable cache data and are excluded from the completion archive; worlds
and IDE files remain preserved.
finish uses the task record written during setup, checks that the recorded branch still owns that
worktree, and requires its HEAD to be an ancestor of the recorded local base (dev or submodule main
by default). It performs no merge, commit, push or branch deletion. Dirty or untracked files, locked
worktrees, active managed Gradle builds, mismatched/dirty submodules, unpublished submodule branch/tag
commits and additional worktrees belonging to nested module clones prevent removal.
Provide real verification output through repeatable --evidence <file-or-directory> options, or keep it
in the task’s build/agent-logs/, which is included automatically. Evidence must exist and directories
must contain files; this records agent-supplied evidence, without interpreting it as proof that tests
passed. Before removal, the command archives it with task/integration commits and ignored files other
than disposable build outputs and caches under the main workspace’s
build/agent-logs/worktree-completion/<task>-<unique-id>/. Dev worlds, temporary plans and IDE settings
are preserved there. Generated build, .gradle, .kotlin, root target, .gradle-sandbox and
.toolchain data are disposable. If archiving fails, the checkout remains intact.
Git requires --force to remove worktrees containing submodules even after deinitialization. The helper
uses it only for those checkouts after all cleanliness, pin, local-ref and preservation checks; it never
force-removes a dirty checkout. Other worktrees are untouched. --path optionally asserts the recorded
checkout location. For a task created with an older helper, explicitly register its clean checkout by
repeating its setup command with --resume before using finish. A new task always starts from the
updated local base.
Reviewing textures and block models
Keep visual candidates in their task worktree until the user approves their final in-client appearance.
For standalone addon builds such as Create, initialize the addon with worktree-task --submodule addons/create,
run doctor and stage the addon SDK if absent. Build/test the candidate before launching its client:
./gradlew-sandbox-dev-parallel-summary stageAddonDevelopmentDependencies
./gradlew-sandbox-dev-parallel-summary -p addons/create test lintKotlin verifyProductionJar
./visual-preview --addon create run --scenario 'Compare both rings, both flow states, pipe ends and item'
The helper supports addon projects with runClient, src/main/resources and build/resources/main.
It refuses to launch from the main checkout. Its client uses <task-checkout>/addons/create/run/client;
create a disposable creative flat world there, keeping normal user saves untouched. For later launches,
add --world 'Pump Preview' to open an existing isolated world directly. Compare the candidate beside its
original counterpart, horizontal/vertical orientations, both states, all visible sides, inventory item,
daylight and a shaded placement. Base-mod previews use their owning version-specific client instead.
For resource-only edits while the client remains open:
./visual-preview reload
# Press F3+T in the preview client, inspect the result, then press F2.
./visual-preview capture --label pipe-ends --scenario 'Both ends retain original recessed shading'
./visual-preview status
reload copies resources to existing runtime outputs; it does not press keys or remove stale files.
Stop the client and rebuild for deleted/renamed resources, Kotlin, registration or property changes.
A running managed client holds the shared workspace Gradle slot, so another Gradle invocation waits.
The capture command uses the latest F2 screenshot in this client’s game directory, or a specific PNG
via --image <path>. Verify that the screenshot actually shows the current resources: capture metadata
cannot prove that F3+T completed or that the candidate looks correct.
Captures under build/agent-logs/visual-preview/<addon>/ include the scenario, revisions, resource
fingerprint and screenshot hash. Show the candidate to the user before integration. After an explicit
approval, record the user’s actual reply against that capture:
./visual-preview approve --review <capture.json> --note '<explicit user approval>'
The helper rejects approval after resources, revisions or the screenshot change. An approval record is
evidence of a conversation, not permission supplied by the script. If appearance changes, review it again.
Exit through the game’s menu before final focused checks and worktree cleanup; destroying the X11 window
can leave GLFW shutdown hanging. After approved integration, worktree-task finish retains preview evidence
and the disposable world. Verify the exact source candidate again before committing and integration.
Minecraft development
Run Gradle with JDK 25 selected through JAVA_HOME or a Gradle-discoverable
installation:
./gradlew-sandbox-dev-parallel :v26_1-neoforge:runClient
./gradlew-sandbox-dev-parallel :v26_1-neoforge:runGameTestServer
./gradlew-sandbox-dev-parallel :v26_1-neoforge:buildProductionUniversalJar
./gradlew-sandbox-dev-parallel :v1_21_1-neoforge:runClient
./gradlew-sandbox-dev-parallel :v1_21_1-neoforge:buildProductionUniversalJar
To collect the production mod JARs and first-party addons in one workspace folder, run:
./gradlew-sandbox-dev-parallel-summary -p addons/dev collectDistributionJars
# Clean exact-tag universal release using the pinned downloadable Runtime bundles:
./gradlew-sandbox-dev-parallel-summary -p addons/dev collectReleaseDistributionJars
The task builds and verifies the archives before synchronizing dist/: 1.21.1/ contains Compukters plus the
Create, Sable and Propulsion addons; 26.1.2/ contains Compukters. Re-running removes stale distribution files.
collectDistributionJars builds Rust locally and packages the configured local native platform.
collectReleaseDistributionJars downloads the pinned Linux/Windows Runtime bundles and runs both clean tagged
release gates before collecting the same mod/addon layout. Full candidate verification remains a separate step.
Third-party mods required by addons must be installed separately.
Minecraft-independent Gradle modules are grouped beneath modules/common, while
all game-facing code is grouped beneath modules/minecraft. Minecraft and
NeoForge code shared across supported versions has one canonical source tree
under modules/minecraft/shared. IntelliJ indexes that tree against
the target selected by compuktersActiveMinecraftVersion in gradle.properties;
change the property to 1.21.1 or 26.1.2 and reload the Gradle project when
switching the version being edited. Inactive targets use generated mirrors, so
ordinary Gradle verification continues to compile both versions from the same
canonical content.
For fast feedback, ./gradlew-sandbox-dev-parallel-summary verifyLocalFast runs policy,
build-script, and a curated JVM test slice. Before treating the current checkout
as fully verified, run ./gradlew-sandbox-dev-parallel-summary verifyLocalFull; it covers
every Gradle subproject check, all registered Kotlin-to-VM conformance scenarios, Rust, FFM and JNI checks, runtime
integrations, the real GameTest server, and the
production artifacts for the locally configured native platform.
A distributable multi-platform release has a stricter, separate gate:
running buildReleaseUniversalJar from the repository root selects both
version-specific tasks. It requires a clean exact-tag checkout and the pinned
Linux and Windows Runtime bundles, then assembles and verifies both the 26.1.2
FFM and 1.21.1 JNI artifacts. A successful local full verification does not by
itself establish release readiness.
See Publish Compukters releases for the GitHub/Modrinth workflow, credentials, candidate validation, and publication recovery.
Standalone playground
The playground exercises the same isolated compiler, artifact verifier, FFM adapter, Rust VM, and terminal capability that the mod will use. Run the included multi-file example from the repository root:
./gradlew :playground:run --args examples/hello
It prompts on stdout, reads one UTF-8 line from stdin, and executes the emitted Compukter bytecode. To retain the verified compiler output for inspection:
./gradlew :playground:run --args="examples/hello --emit build/hello.cpkt"
Compilation diagnostics and runtime failures go to stderr. Add --debug to
show launcher stack traces. The process uses stable exit categories: 2 usage,
3 project input, 4 compilation, 5 compiler platform, 6 artifact
verification, 7 VM admission/start, 8 guest trap, 9 VM fault, 10 host
failure or EOF, 11 quota, 12 allocation resource failure, and 13 launcher
or native platform failure.
For combined addon and physics runs, follow the all-addon development stand.