Compukters Architecture

Product boundary

Compukters is an in-game Kotlin programming platform with two production authoring paths. Programs created inside a computer use the Rust-owned /home filesystem and the packaged /rom/edit and /rom/kotlinc tools. The client IDE owns bounded multi-file projects with compukter.toml, an optional compukter.lock, and Kotlin sources beneath src; it can analyze and compile a project locally, attach to a computer, and deploy the resulting executable.

Both paths produce the same canonical Compukter artifact and execute through the same VM session boundary. A project must declare exactly one supported top-level entry point: fun main() with either no parameters or one Array<String> parameter, returning Unit. The standalone playground uses the same compiler artifact and VM session contracts without the Minecraft carrier.

Platform and K2 tooling

Built-in Guest Kotlin declarations are authored in guest-platform as separately versioned modules. Its build produces only the canonical base platform bundle consumed by both compilation and IDE analysis. Optional integrations own and build their Guest declarations as addon bundles against that base; addons/create owns the first such bundle. The public ru.lazyhat.compukters.addon Gradle plugin resolves an isolated builder, base bundle, Minecraft-independent host API, and thin target adapter from one independently versioned SDK release by Maven coordinate. The SDK version changes with its public compatibility boundary rather than every Compukters release. The plugin derives the wire schema and bindings from Guest Kotlin declarations, validates a checked-in selector lock, generates the typed JVM host contract, and packages the resulting .cagb in the independent addon JAR. platform-bundle owns the base bundle model, codec, module graph, and default imports; platform-k2 exposes that metadata to K2 without making the K2 implementation part of the platform format. Constant Int and qualified enum-entry defaults cross this bundle explicitly; compiler lowering materializes an omitted platform argument without a JVM-style mask dispatcher. Other platform default expressions remain unsupported. Canonical IntRange and IntProgression declarations let K2 resolve ascending, descending, and stepped Int loops. The compiler recognizes only their canonical for shape and evaluates start, end, and step once into scalar registers; no range or iterator object enters the VM. It validates a positive step before iteration, calculates the next index in Long to avoid Int overflow, and retains a loop-header quota safepoint. Stored progressions are outside this subset. Direct IntArray for loops follow the same canonical-shape rule: the compiler snapshots the array reference, loads its length, and reads each element with existing array instructions at the current index. The loop header retains its quota safepoint; no iterator object is created. A source variable may be reassigned without changing the traversed array, while writes to that array’s elements remain visible to later iterations.

Compiler and analysis workers are pinned, isolated JVM processes. Their payloads are assembled into one bounded k2-tooling-workers.zip.xz: nested runtime JARs and the carrier ZIP use canonical stored entries, then the complete ZIP is compressed as one checksummed XZ stream. The outer runtime uses XZ for Java’s pure-Java streaming decoder with a fixed memory limit and feeds the decoded ZIP directly into bounded, hash-verified, atomic publication. An external copy of tooling.bundle identifies the expected content-addressed cache directory before the carrier is opened. A cache hit validates the complete file tree and hashes without decompressing XZ; a corrupt hit is retained until a replacement has been fully decoded and verified, then replaced with the valid tree. Kotlin compiler and Analysis API internals stay inside the workers and do not enter the mod runtime classpath. worker-client owns the generic payload publication, process, framing, deadline, and immutable-byte machinery shared by both worker clients. The production mod archive embeds Tomlj and its ANTLR runtime under a private relocated Compukters namespace. They do not enter NeoForge’s module layer as separate automatic modules and therefore cannot collide with loader-provided versions; Checker Qual remains compile-only.

The two execution-producing paths are:

In-computer source
  -> Rust captures source and output preconditions
  -> server-global ServerCompilerService
  -> isolated compiler K2 worker
  -> server-global persistent artifact cache
  -> Rust re-verifies and atomically installs the artifact

Client IDE project
  -> bounded project snapshot + manifest + lock + resolved platform profile
  -> client compilation service and client-local artifact cache
  -> isolated compiler K2 worker
  -> attach and verify against a server target
  -> revision-checked deployment into the Rust filesystem
  -> optional canonical terminal submission to run the executable

Both compilation services send ordered project sources, target settings, worker identity, platform-module identities, and limits through the same bounded compiler protocol. compiler-k2-engine owns the shared FIR-to-IR and Compukter lowering implementation; compiler-k2 supplies the isolated compiler-worker entry point and payload.

The client IDE has a separate analysis path. ide-analysis-client owns the bounded protocol, scheduling, cancellation, and worker lifetime without depending on K2. ide-analysis-k2 owns the isolated incremental K2 workspace and answers diagnostics, completion, symbol, reference, expression, and semantic-token queries. Compilation and analysis use the same resolved platform bundle and source-snapshot identities, but have separate worker sessions and result contracts.

Analysis protocol v8 also carries explicit Kotlin format and parameter-information requests. Parameter information resolves the innermost call at a UTF-16 caret into a bounded, deterministically ordered set of K2-substituted callable signatures with active-parameter spans; client-side snapshot, revision, path, caret, and call-range checks reject stale popup results. The format request includes the exact editor text and UTF-16 caret captured by the Reformat Code action, so formatting does not depend on whether the semantic snapshot has caught up. The worker runs ktlint standard rules from a compiler-free nested runtime whose shaded IntelliJ references are relocated back to the ordinary namespace. Its child classloader reloads the worker’s existing Kotlin compiler JAR files with a platform parent: the distribution stores one compiler copy, while formatter and Analysis API retain separate IntelliJ global state. Formatted text is bounded by the negotiated source-file limit; the client applies a current result as one undoable edit and leaves the document dirty for a separate save. Stale results are discarded, and formatter failures warn without changing or saving the source. Ctrl+S, autosave, and implicit saves do not format.

Server compilation

The trusted server JVM owns compiler scheduling, diagnostics, artifact production, and the server-global content-addressed cache. The Rust machine validates guest paths, captures an immutable source snapshot plus filesystem preconditions, and suspends only the requesting foreground process. The server tick submits and polls bounded compiler work without waiting on the worker or cache. On completion Rust re-verifies the complete artifact and atomically installs it only if the source and output preconditions still match. Native buffers remain caller-owned and no Rust pointer enters Kotlin.

The packaged tooling payload is validated and published beneath <world>/compukters/compiler-worker; temporary worker state is kept separately beneath <world>/compukters/compiler-temp. Successful server artifacts are stored beneath <world>/compukters/compiler-cache/v1, shared by every computer and dimension in that server world, and reused after restart. Cache keys cover the ordered source snapshot, compiler and payload identity, target, selected trusted platform modules, and compilation limits. Cache publication and cache hits both pass the stateless Rust artifact verifier over FFM; runtime admission quotas are deliberately not part of cache validity.

ServerCompilerService performs bounded asynchronous preparation, persistent-cache lookup, and single-flight deduplication by compilation identity. Minecraft-facing code submits requests and drains completions; worker and cache I/O run outside the server tick thread.

Runtime ownership

The asynchronous actor migration (#603) has a server-scoped service registered with NeoForge. Server startup records its lifetime; workers are allocated on first use. Each pre-tick drains at most 1024 actor results and runs reply callbacks on the server thread before world and block-entity ticking. Work submitted during one tick therefore has the full inter-tick interval before the next deterministic delivery boundary. Stopping removes the service before closing it, so late callbacks cannot reopen it. Production computers attach one actor identified by ComputerId and a machine epoch. A full scheduler rejects a new attachment without blocking or failing the server tick; the block remains powered off and retries on a later tick. The server config exposes vm.workers, vm.maximum_actors, vm.mailbox_capacity, vm.messages_per_turn, and vm.result_capacity_per_worker; defaults are half of the available processors clamped to 2..8, 4096 actors, 64 commands, 4 commands per turn, and 256 replies per worker. Operators may explicitly configure up to 64 workers. Increasing queue limits trades bounded memory for burst tolerance; increasing messages per turn trades inter-actor latency for locality.

Scheduler snapshots read atomic counters and bounded lane sizes without scanning registered actors. Every five seconds the debug log reports registered actors together with configured capacity, runnable actors, mailbox and result depths, worker occupancy, average/maximum command-queue latency, average/maximum execution time, completed-result latency, the last server pump size and duration, deferred world requests, host-completion-to-next-advance delay in server ticks, and rejected or capacity-coalesced redstone input submissions. Pending redstone transitions retain their sampled order while a VM turn is in flight, up to the configured mailbox capacity; overflow coalesces only the newest retained packet and increments the diagnostic counter. These are lifetime counters and gauges for the current server service rather than an equal-CPU or delivery-latency contract.

ActorProgramComputer is the asynchronous carrier implementation for that migration. Its server-side state is an observation from actor replies, and terminal, filesystem, deployment, and input requests return futures. The scheduler admits at most one pending or executing tick permit per actor, while completed replies may remain queued for the next server-thread pump without blocking admission of a later tick. The carrier suppresses obsolete lifecycle replies and performs redstone and sound world actions on its owning server thread. On a later server tick it submits one typed continuation containing the immutable world-action result. The actor validates and applies that completion before using the same command to perform the next bounded advance; its single reply both completes the old deferred request and may publish the next one. A full actor mailbox retains the exact continuation for retry without repeating the world mutation. Its close future reports the final filesystem generation after accepted work drains and native resources close; this barrier does not depend on server result pumping and may complete on a worker thread.

Third-party world capabilities use the same continuation boundary. Loader integrations register bounded ProgramAddonHost factories together with deterministic AddonGuestApiBundle values. Each bundle contains one versioned Kotlin platform module, its exact external-call bindings, the matching typed capability schema, and optional sources; it contains no executable addon JVM classes. Registration rejects duplicate addon/module identities, and a created host must expose the exact registered capability schema. The actor transfers immutable typed requests to the server thread, where the host may complete immediately or retain a bounded wait; completions resume the exact VM task on a later turn. The Minecraft 1.21.1 Create adapter and its Guest Kotlin declarations live in the standalone addons/create Gradle root. It declares the public plugin, tooling, platform bundle, common host API, target adapter, and development mod through their external coordinates. For local co-development its composite build substitutes the adjacent Compukters projects, selecting the self-contained namedElements development mod instead of a republished Maven Local runtime. The API and adapter share the independent SDK version; the runtime-only development mod retains the product version. The Compukters root does not include or invoke Create tasks, so the base platform, shared Minecraft code, and 26.1 code have no direct Create ownership. The adapter resolves adjacent loaded positions or named devices across loaded peripheral cables and binds handles to exact block-entity identities, preventing replacement from silently rebinding a running Guest program. Stock Ticker snapshots and package requests remain inside that addon and use the same bounded server-thread host boundary.

ProgramRuntimeHost owns one current Rust ComputerMachine, advances it with bounded guest and maintenance budgets, commits terminal changes once per active server tick, and exposes typed full/delta states and failures through JDK 25 FFM on the 26.1 product line. The Java 21 runtime API owns the same typed session boundary independently of its native transport. It does not own a second grid or output transcript. It is loader-independent and confined to its actor worker. Within one process, the Rust VM may schedule several bounded cooperative Guest tasks. Exactly one task executes instructions at once; suspension on a host request or Task.join() transfers execution to the next runnable task in FIFO order. Pending requests retain their (TaskId, RequestId) owner, so independent reads and writes may remain in flight and complete out of order without running Guest code re-entrantly. Returning from the root task ends the process and cancels its remaining task work. The built-in compukter:timer capability at ABI 1.0 backs Tasks.sleepTicks: the loader-independent runtime retains the bounded request and resumes its owning task at the requested server-tick boundary. It does not introduce a VM clock, background timer, wall-clock dependency, or work while the computer is not receiving tick permits. Guest Kotlin exposes this as transparent stackful blocking through ordinary functions: suspend declarations are outside the supported source subset, while legacy suspend-call artifact instructions remain decodable and executable. The production execution profile reserves a 256 KiB managed heap for each active foreground process; child-process capacity is charged independently while its parent is suspended. Heap arenas are released with their owning machine. An explicit actor request can also compose one immutable resource snapshot from host lifecycle/configuration and the native machine’s semantic work, Guest heap, admitted mutable execution-resident, and filesystem quota counters. The host counts Guest and maintenance budgets only when it actually invokes native advancement; both host and native counters saturate explicitly instead of overflowing. These values describe VM work and granted capacity, not host CPU percentage. No sampling loop, history buffer, heap-content scan, or unsolicited FFM call runs for unobserved computers.

Published Runtime 0.15.0 platform bundles use manifest schema 2 and contain the FFI and JNI native libraries for one operating-system target under a single Runtime, VM commit, and C ABI identity. Bundle validation covers both transport entries before staging; the Java 25 runtime module packages only FFI, while the Java 21 runtime module packages only JNI. The two Minecraft release artifacts therefore share one pinned native release without carrying an unusable transport. FFI ABI 15 carries host failures as a stable category plus a non-empty, bounded UTF-8 detail instead of an opaque numeric producer code; the VM retains that detail without allocating while resuming and exposes it in terminal process diagnostics. Runtime ABI 1.3 adds exact decimal materialization for the existing I64 scalar form; artifacts require it only when an I64 value is converted to String, while purely numeric Long programs remain compatible with Runtime ABI 1.0. Runtime ABI 1.4 does the same for F32 using Kotlin/JVM-compatible spellings, including signed zero, infinities, NaN, and the smallest subnormal values; purely numeric Float programs likewise remain compatible with Runtime ABI 1.0.

The Minecraft carrier owns exactly one actor endpoint and submits at most one ordinary advance or host continuation for each server tick. Rust starts /rom/boot, compiled from system/programs/boot.kt; boot delegates to /rom/shell, compiled from system/programs/shell.kt. A foreground child suspends its parent until it exits or fails. There is one active foreground lane today, while the runtime contract leaves room for later parallel execution. Reboot replaces the complete machine stack and clears the terminal. Minecraft sends full state to a new viewer and ordered deltas thereafter. Terminal viewers submit bounded asynchronous key, text, state, resync, and resource-snapshot operations, which merge in server-arrival order without client-side echo or a terminal input lease. A valid standalone-terminal viewer samples resources immediately and then every ten server ticks through the same single-poll admission. Its viewer-local window retains one prior sample to derive semantic consumed/granted Guest-unit usage; it is discarded on close, invalidation, machine replacement, or server stop. Replies are published only after returning to the server thread and only while the viewer still refers to the same machine epoch.

The raw terminal is a synchronous Rust device: cell writes, positional writes, rectangular fills, colors, cursor changes, and input polling never cross into Minecraft. The authoritative fixed 51x19 cell grid and its replication journal live in the machine; Minecraft only renders full states or ordered deltas. Waiting for an absent input event and foreground process execution are explicit suspension points. Kotlin guests consume ordered raw Text and Key events; there is no compatibility line buffer or second input protocol. A program must finish its active event before starting an interactive child, so event ownership never leaks across foreground process frames. The server host never blocks the Minecraft thread.

Minecraft owns the persistent 30-bit redstone output register and samples dirty local faces before VM advancement. Rust owns the complete input snapshot, predicate waiters, and confirmed output mirror. Input crosses FFI as one scalar changed-mask-plus-levels packet; output requests are reduced in publication order and committed through one loader-independent host port at most once per computer per tick. A successful physical commit is confirmed to Rust before every original blocking request resumes. Completion and the next bounded advance share one actor command and one result without allowing multiple advances in a server tick. VM halt, fault, shutdown, reboot, and replacement never synthesize a zero output.

One-shot sound requests cross the same actor boundary as immutable (note, volume) batches. Minecraft emits the vanilla note-block pling in the block sound category, using equal-temperament pitch around neutral note 12, before the VM task receives its Boolean admission result. Each loaded computer has a four-tick cooldown, and one server-wide counter admits at most 64 computer sounds per tick. Rejected sounds return false immediately and are never queued; unloaded block entities retain no sound state or background work.

The Minecraft carrier also owns a direct-touch peripheral fabric. Passive orthogonal cables form bounded components across loaded chunks without forcing chunk loads. Addon providers map a touched block or multiblock part to a canonical logical identity; a versioned world directory stores normalized names against that identity independently of computers and cable topology. Lookup is scoped to one cable component and addon ID, rejects duplicate reachable names, and never returns a partial component after a traversal bound is exceeded. Typed addon host handles bind to the resolved device identity, so later rewiring or renaming cannot redirect an existing handle.

The base text display uses the same bounded world-request path with an internal compukters:display capability. Its 20x10 buffer and exclusive writer lease belong to the display block entity on the server, outside the VM terminal. The block tick checks its writer and cable reachability even while Guest code is idle. Stopping or disconnecting that writer clears the screen, and no display text is saved in world NBT. Clients receive at most one full-grid block-entity update per changed server tick and render text on the oriented front face.

The client renders the fixed 51x19 grid in a centered compact panel while the world remains visible through a translucent dim layer. A separate footer presents rolling CPU utilization, current Guest heap and virtual-disk usage, and concise lifecycle activity. Here CPU is the virtual computer’s consumed/granted semantic Guest-unit budget, not physical host timing. Users can select the packaged Cozette 6x13, Dina 6x10, or ProggyTiny 6x10 terminal font without changing terminal coordinates or creating a second grid. The terminal screen can suspend its observation and open the IDE, whose target terminal view consumes the same replicated terminal state but does not yet display these standalone-terminal gauges. Returning from the IDE reopens the standalone observation without reopening the screen and receives a fresh authoritative terminal state.

The Rust VM owns verification, the Tier 0 interpreter, managed memory and collection, quotas, traps and faults, capability suspension, and host-neutral sessions. Future JIT or AOT tiers must remain behind the same verified artifact and session contract.

Filesystem and machine lifetime

The Rust runtime owns the guest filesystem and its persistence. Minecraft stores only a stable 128-bit ComputerId; guest paths and bytes never enter block-entity NBT or a JVM-side mirror. Every computer sees an immutable packaged /rom and a private persistent /home. The world store lives under <world>/compukters/filesystems, performs bounded I/O on its own worker, flushes active generations on world saves, and drains, flushes, and closes before server shutdown completes. A permanent lock anchor carries a process-lifetime exclusive OS file lock: a live second server is rejected, while orderly close or process termination releases ownership without deleting the anchor. Removing a computer through the player destruction lifecycle closes its machine before creating a recoverable tombstone; ordinary block-entity removal during chunk unload only closes the current machine and preserves its filesystem.

The Minecraft filesystem registry accepts asynchronous machine-close barriers. An unloading computer retains its attachment until the barrier completes and the final generation is flushed; destruction defers its tombstone until that point. Store shutdown starts every outstanding drain before waiting for the combined barrier. Ordinary release does not wait for VM completion; the server-stopping hook alone permits a bounded ten-second wait. A failed close barrier keeps the store unavailable rather than closing storage underneath a possibly live native machine. Native flush, tombstone, recovery, and store close run on one lazy persistence executor per world, outside the registry monitor and outside both the server tick and VM actor workers. Each world captures the configured VM actor capacity when its store opens and admits at most that many computer identities, including pending removal/recovery; each identity owns at most one queued or running token. Repeated saves coalesce to the latest requested generation. The identity remains unavailable until final persistence completes, so a quickly reloaded block retries attachment instead of opening a second machine. Failed background carrier admission is retried once every 20 block-entity ticks; explicit terminal access may retry immediately. Persistence failures are logged once, fail outstanding lifecycle futures, and prevent unsafe reattachment or store close. Initial store opening happens during server startup rather than an ordinary computer tick. Calls sharing a native world-store handle are serialized by a fair lock across VM actors and persistence work, while ordinary VM execution stays on actor workers.

The versioned C ABI v16 exposes opaque world-store lifecycle operations, machine creation inside a store, stateless artifact verification, dedicated bounded compilation request and completion calls, and typed Unit, Int, raw-bit Float, Boolean, String, or categorized and human-readable failure host-request completion. Kotlin can select a world store, identify a computer, request flush, tombstone, or recovery, and route compiler results, but it cannot perform arbitrary guest file operations. Guest code reaches Rust-owned state only through declared capabilities. The guest machine-creation calls carry a bounded, versioned schema for optional host capabilities; Rust validates and owns that schema before admitting the executable, so addon operations use the same typed verifier contract as built-in devices. ABI v16 also adds an instruction-limited advance that returns retired instruction count separately from the existing weighted guest and maintenance budgets. The earlier advance wire result and its meaning remain unchanged. Server tick requests enter actor mailboxes with a deferred permit. After block entities and benchmark carriers submit their requests, the NeoForge post-tick hook divides aggregate instruction capacity among the submitted runnable computers and releases their permits. World effects remain ahead of each permit, while later actor commands stay behind it. A deferred permit releases its worker token while waiting for the post-tick allocation. The guest filesystem facade exposes bounded stat, list, readText, and writeText; Rust validates paths, UTF-8, permissions, quotas, and atomic replacement while /rom remains immutable. The shell and editor map stable failures to user-facing diagnostics. Executable installation remains a Rust-owned filesystem transaction and never accepts a host path.

The IDE target protocol is a separate bounded host interface rather than direct filesystem ownership. After attaching to a target, the client may inspect supported filesystem metadata and content, upload an artifact for verification, observe the destination revision, and deploy using a verification ticket plus the expected revision. Heartbeats and detach bound the target attachment lifetime; revision conflicts require an explicit retry or user confirmation.

Server-side IDE verification, deployment, canonical input, and filesystem operations may suspend until the runtime answers. The request transport admits at most 256 pending operations per server and four per player, including suspended work. Verification retains its upload staging reservation while awaiting the runtime, and a candidate returned after its target lease ends is closed instead of becoming a ticket. The IDE terminal also supports suspended open, resync, input, and polling operations. Each viewer has at most one pending poll; replies are checked against the current viewer session and machine before publication. The standalone terminal uses the same bounded asynchronous transport as the IDE terminal, and its viewer state is discarded when the server stops.

Guest programs and APIs

Boot, shell, kotlinc, edit, and vmbench are ordinary no-std Kotlin programs packaged as extensionless executables in /rom. Shell owns line editing, authoritative echo, prompts, and direct built-ins. A non-absolute external command resolves first to /home/<name> and, only when that path is absent, falls back to /rom/<name>; an absolute command is used as given.

Process.run(path) and Process.run(path, args) execute one verified extensionless artifact as a foreground child and return ProcessResult.Exited(code) or ProcessResult.Failed(reason, diagnostic). Process.exit(code) terminates the current process. Guest code does not select an explicit capability mask when starting a child.

Tasks.launch starts a supported () -> Unit value in a bounded cooperative task. The compiler keeps direct top-level references as static spawns; other function values cross the existing spawn boundary as one managed reference into a private Guest trampoline, which invokes Function0<Unit> through normal dynamic dispatch. The child frame retains that reference after the launching function returns, and shared capture cells remain VM-owned. Ordinary higher-order Guest calls admit non-null function values whose parameter and result types are supported by Guest Kotlin. The compiler creates one managed interface per concrete signature and lowers calls through the same interface-call instruction, preserving unboxed primitive values and ordinary reference ownership. There is no special arity transition at 22 or 23 arguments in the Guest artifact. Nested closures capture free values through their enclosing closure objects. Mutable captured locals retain a single VM-owned typed cell shared across nesting levels and sibling closures, including after the enclosing call returns. An unbound top-level Guest function reference uses the same managed function-value interface and a capture-free closure whose invoke method calls the referenced project function. An inferred KFunction value has the same Guest call behavior; reflection is not part of the Guest function-value contract. A reference to an admitted Guest primary constructor uses a capture-free managed closure. Its invoke method allocates the object, calls its constructor function with that receiver, and returns the object, without a constructor-specific VM type or instruction. Direct construction follows the same path. The constructor calls its superclass constructor on the same receiver before evaluating backed properties and init blocks in declaration order. Constructor-backed Guest var properties use the same managed instance fields as val properties. Default setter calls lower to verified field_set instructions after evaluating the receiver and assigned value in source order. For primary constructors, the compiler evaluates explicitly supplied arguments once in call-site order, then omitted default expressions in parameter order. Earlier constructor parameters are available while evaluating later defaults. The existing full-arity constructor function receives the resolved values, so field initialization and init blocks observe the same values and object identity. A constructor reference retains its full arity when the expected function type supplies every parameter. When the expected type omits supported trailing defaults, K2 supplies an adapter function whose body calls that constructor. The compiler lowers the adapter as a managed closure, so each invocation evaluates defaults and allocates a new object without another VM instruction or ABI change. Source-defined class getters and setters lower as ordinary non-suspending instance methods, so overridden accessors use the existing virtual dispatch table. A computed property has no managed field; field inside a backed accessor lowers to verified field_get or field_set on its receiver. Non-overriding final default accessors retain direct field access. Abstract class and interface properties contribute getter and setter method signatures without managed fields. Implementing classes provide backed or computed accessors, selected through the existing virtual or interface call instruction when the property is used through a base reference. Interface method and computed property bodies use the same function records and bytecode as class methods. During artifact admission the VM first searches the class inheritance chain, then chooses the most specific matching interface declaration. An abstract redeclaration suppresses an inherited default; missing or ambiguous concrete targets reject admission. Dispatch entries are shared by all VMs using the admitted execution image. An explicit super<Interface> call in an override resolves the concrete interface body at compile time and emits a direct call. If the named interface only inherits the body, K2’s fake override resolves to that concrete ancestor. Abstract or ambiguous targets publish no artifact; the direct call retains the original receiver and skips the override’s dynamic dispatch entry. A bound Guest instance-method reference evaluates its receiver once and stores that object in the closure. Its invoke method loads the stored receiver and uses the target method’s static, virtual, or interface dispatch mode. An unbound instance-method reference has no receiver field; its first function parameter supplies the receiver for the same dispatch path.

/rom/kotlinc source.kt [-o output] accepts exactly one source file today; its default output is the source basename without .kt. This single-file in-computer command is distinct from the IDE and compiler protocol, which support bounded multi-file project snapshots.

/rom/edit <path> is a nano-like 51x19 editor backed by one managed 4096-unit CharArray gap buffer. Cursor motion and deletion preserve UTF-16 surrogate pairs, CRLF input is normalized to LF, Tab inserts four spaces, Enter inherits leading indentation, and the viewport scrolls in both axes. Ctrl+S writes through Rust-owned FileSystem.writeText; Ctrl+X exits directly when clean or opens a Y/N/Escape save prompt when dirty. The buffer, source, and compiled artifact belong to the computer filesystem, while terminal state belongs only to the current VM lifetime. The playable in-computer loop is edit demo.kt -> kotlinc demo.kt -> demo; source and artifact survive machine reload and remain isolated by ComputerId.

/rom/vmbench cpu <rounds> runs the documented deterministic, allocation-free integer/branch workload through the ordinary foreground process and VM quota path. /rom/vmbench redstone <rounds> normalizes the local top output to zero and then emits one acknowledged weak-power 15 -> 0 pulse per round; every transition suspends through the ordinary Guest redstone API until its physical server-thread commit is confirmed. These workloads exist to measure aggregate in-world runtime cost and do not own a timing capability, privileged execution budget, or benchmark-only host path. See the in-world VM benchmark guide for the controlled scaling procedure. An operator-only harness can run up to 4096 internal headless artifacts through the same verified session and actor scheduler, including a phased capacity run that settles actors at terminal input, observes 100 idle ticks, samples their existing resource counters once, and wakes them with an ordinary Text event. It can also dispatch either ordinary /rom/vmbench workload to at most 1000 loaded physical computers in a bounded area. The harness owns no persistent computer identity, never loads chunks, and does not provide a guest-visible fleet protocol.

Terminal, standard output and error, redstone, sound, process, filesystem, and compiler declarations live in the base guest-platform bundle as separately identifiable modules. Optional integrations publish bounded addon Guest API bundles instead. The server admits those bundles, includes their content hashes in the target and compilation-cache identity, and sends their metadata and optional sources to the attached IDE. Analysis indexes the bounded catalog of available addons for completion, while K2 semantics and compilation activate only the modules selected in the project lock. Compiler and analysis workers decode only this data format: they never load or execute addon code. Exact admitted bindings extend intrinsic lowering, while a same-named Guest declaration remains ordinary Guest code. General stream handles, pipes, and process redirection remain later layers. Addon host handlers report failures with a broad HostFailureKind and a non-empty UTF-8 detail of at most 256 bytes. The kind remains suitable for runtime classification; the producer-owned detail is diagnostic text and is not a stable programmatic identifier.

Module ownership

Minecraft-independent Gradle modules are physically grouped under modules/common; all shared and version-specific Minecraft integration is grouped under modules/minecraft. These directories express source ownership only: the existing Gradle project paths and artifact names remain flat and stable.

Module Purpose
native-runtime-api Java 21 Kotlin-facing VM session, wire validation, opaque world-store lifecycle, and trusted host capabilities
native-runtime-ffm Explicit JDK 25 FFM transport, native resource loading, and FFM integration evidence
native-runtime-jni Explicit Java 21 JNI transport, native resource loading, and JNI-to-C-ABI integration evidence
platform-bundle Canonical platform bundle model, codec, module graph, identities, and default imports
addon-api Minecraft-independent public host contracts and self-contained compile-only SDK artifact
addon-guest-api Loader-independent addon bundle model, codec, capability schemas, bindings, and admission limits
addon-gradle-plugin Public external-build DSL, generated-source wiring, ABI-lock workflow, and Maven-coordinate SDK boundary
addon-guest-api-fixture Test-only neutral addon bundle used by common compiler and IDE verification
platform-k2 Shared K2 metadata and FIR integration for the Compukters platform
compiler-artifact Canonical executable artifact model, validation, and encoding
worker-client Generic bounded JVM worker processes, payload publication, framing, deadlines, and immutable values
tooling-runtime Packaged shared runtime containing the pinned compiler and analysis workers
compiler-client Compiler protocol, controller, project snapshots, compilation identities, and persistent cache
compiler-runtime Server-global scheduling, single-flight compilation, persistent cache, and compiler backend lifecycle
compiler-k2-engine Shared K2 FIR-to-IR pipeline, platform linking, trusted intrinsics, and Compukter lowering
compiler-k2 Isolated compiler worker entry point and packaged compiler payload
guest-platform Built-in Guest Kotlin declarations and canonical base platform-bundle inputs
ide-core Minecraft-independent project, editor, profile resolution, client compilation, and analysis models
ide-analysis-client K2-free analysis protocol, controller, scheduling, cancellation, and worker lifetime
ide-analysis-k2 Isolated K2 Analysis API worker and incremental project workspace
ide-client Minecraft-independent IDE workspace, controller, analysis coordination, target, and file-transfer logic
playground Standalone compile-and-run entry point with stdin and stdout
core Loader-independent server behavior and ProgramRuntimeHost
minecraft/shared/common Canonical loader-independent Minecraft sources, resources, and tests compiled against every supported game target
minecraft/shared/addon-neoforge-api Canonical public Minecraft registration adapter compiled into each supported target artifact
minecraft/shared/neoforge Canonical NeoForge integration sources, resources, and tests compiled against every supported loader target
v1_21_1-common Minecraft 1.21.1 compatibility adapters over the shared computer carrier
v1_21_1-addon-neoforge-api Thin NeoForge 1.21.1 addon SDK adapter
addons/create Standalone Gradle build for the independently packaged Create 6.0.x Guest API bundle, kinetic-device adapter, bounded host state, and focused tests
v1_21_1-neoforge NeoForge 1.21.1 compatibility adapters, Java 21 JNI packaging, and production archive
v26_1-common Minecraft 26.1 compatibility adapters over the shared computer carrier
v26_1-addon-neoforge-api Thin NeoForge 26.1.2 addon SDK adapter
v26_1-neoforge NeoForge 26.1 compatibility adapters, client UI, GameTests, resources, and production archive
host/compukter-vm Artifact verification, managed Rust execution runtime, and VM-owned versioned C ABI in its ffi workspace member

Ownership rules: