Tooling workers and IDE ownership

← Architecture

Worker isolation and analysis

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 under a private relocated Compukters namespace. Tomlj carries its own relocated ANTLR runtime, so no standalone ANTLR or Checker Qual dependency is needed. These libraries do not enter NeoForge’s module layer as separate automatic modules and therefore cannot collide with loader-provided versions.

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

Client preferences retain a bounded map of durable computer identities to the last successfully opened project (64 most recently remembered computers), independently of per-project editor state and the global last project used when opening the IDE without a target. Initial target attachment and project catalog loading may finish in either order; target openings restore only after both complete and do not first open the global last project. Unknown computers, removed projects and failed initial attachments leave the project selector available. Attaching from an already open workspace preserves documents and uses the existing save-before-switch path. Renaming a project updates all associated computers; deleting or forgetting it removes their associations. Preference format 4 stores these associations and still admits formats 1–3. This is client-local state and does not change a computer’s server filesystem.

The physical client runtime owns a lazy compiler backend across IDE screen sessions. Each screen retains its own compilation service, cache handle and backend lease. Closing the screen cancels its project compilations and releases its lease. Source-free library preparation belongs to the client owner and can finish after the screen closes; both prepared and completed compiler workers remain available for reopening the IDE. The final lease release starts a 120-second idle timeout; reopening cancels it. Expiry and replacement acquisition are serialized so workers cannot overlap on the same temporary directory. Client game shutdown closes the owner and timer immediately, including late tooling preparation. Active project-compilation cancellation retains the existing worker termination semantics. Source snapshots, pinned identities, artifact admission and result-cache verification are unchanged.

Opening a project with admitted manifest and lock starts background compiler-library preparation. Successful dependency resolution, observed configuration changes and attached target-profile changes request the matching preparation again. Configuration reads contain only manifest and lock bytes; source loading and project lowering remain exclusive to Build. The build coordinator admits preparation through the same lock validation and local/target profile resolver as compilation. Invalid manifests, missing/stale locks and unavailable profiles silently skip preparation, leaving editing and ordinary Build diagnostics available. No lock or artifact is written by preparation.

Compiler protocol 5 explicitly distinguishes preparation from compilation. The same worker validates selected library identities and retains one library frontend cache; it prepares metadata/artifacts and generic library FIR without project sources or IR. In-flight identical preparation is shared across backend leases, while completed acknowledgements are discarded so they cannot mask a worker restart. A screen cancellation detaches its observer; idle expiry and client shutdown still close the backend. The bounded worker queue gives compilation priority over queued preparation and lets an active preparation finish before processing the next request.

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. The worker queue retires completed requests atomically with its cancellation-acknowledgement decision. A cancellation accepted before retirement receives an acknowledgement; a later cancellation is acknowledged by the server as completed work. Response callbacks run outside the queue lock.

The IDE prepares admitted analysis snapshots on a screen-owned background scheduler, outside the input thread. Opening, reloading and edits that trigger automatic completion prepare immediately; other source edits coalesce for 25 ms before manifest/profile admission and snapshot hashing. Cancelled preparation is removed from the timer queue; only one preparation can run and one latest revision can wait. Caret and manual-completion changes are read at publication, while source, target-profile and lifecycle revisions reject obsolete preparation. Closing the screen cancels preparation and closes its scheduler. Rebased semantic tokens and diagnostics remain visible while a new snapshot is pending; interactive queries require an admitted snapshot. Query pipe writes run outside the worker-controller lock, and cancellation writes run on a separate executor after the query frame is sent, keeping pipe backpressure off the input thread. Request IDs and cancellation acknowledgements retain their existing protocol contract.

After preparation, automatic completion enqueues without a debounce delay. Presentation changes still coalesce for 100 ms and hover waits 150 ms. K2 processing, the client tick and the next rendered frame contribute to the visible completion response time. Manual completion enqueues immediately for an admitted snapshot and is retained while preparation is pending. Newer edits cancel obsolete work before their replacement snapshot is prepared.

Analysis protocol v18 carries a validated diagnostic highlight kind alongside severity and exact source ranges. K2 factory identities and compiler diagnostic codes classify unresolved references; presentation never parses message text. Hover types and declarations use K2 short-name renderers, preserving generic, nullable and function-type structure without package prefixes. Qualified declaration identities remain available to navigation and binding checks. The attached-source catalog uses separate packaged-platform and aggregate-addon file/byte pools, matching K2 source admission. The module limit still applies to their combined identities; chained addon attachments cannot reset the addon pool. Switching target profiles starts from the packaged catalog.

Analysis protocol v15 adds a peripheral-provider role to semantic tokens and completion kinds. The worker identifies providers through inheritance of PeripheralProvider, including companion values; type references retain their ordinary class role. The editor renders provider values in the dedicated palette color with a P completion badge.

The protocol also carries explicit Kotlin format, rename-admission, and parameter-information requests. Completion records carry immutable ordered UTF-16 label-relative name-match ranges, derived by the same worker-owned contiguous word-fragment matcher used for filtering and ranking. The client renders these ranges without repeating the matching algorithm. Expression information carries optional bounded KDoc text from the resolved project declaration or its exact attached platform source. Both fields are validated at the worker/client boundary, including Unicode boundaries, nonoverlapping ranges and negotiated UTF-8 documentation limits. Hover wraps documentation and bounds its visible height, with local scrolling that consumes wheel input inside the popup without moving the source. The shared KDocDocument parser keeps tag identities, subjects and descriptions separate from English presentation labels. Hover presents standard tag sections, Markdown paragraphs and lists, styled references and literal code examples. Comment extraction retains code indentation and Markdown hard breaks; indented examples cannot open or close fences or introduce documentation tags. The renderer does not execute HTML or fetch external content. Callable completions carry bounded independent presentation fields for the declared extension receiver, package and specialized return type. Type-scope member signatures retain K2 substitutions; extension applicability supplies receiver substitutions through the pinned K2 completion checker. Explicit receiver checks use a query-only contextual Kotlin expression fragment with a synthetic selector, so literals and incomplete safe-calls still receive K2 inference without mutating admitted source PSI or changing diagnostic scheduling. Safe-call lookup uses the non-null receiver type; unknown callable type parameters remain symbolic. Member functions have a distinct completion kind. Presentation and insertion metadata share the same resolved signature without making the client parse rendered Kotlin declarations. Presentation requests explicitly choose whether to include diagnostics. During an automatic or manual completion session the client cancels diagnostic work and requests semantic-only presentation; edited diagnostics are rebased outside the changed range rather than discarded wholesale. Empty completion results, dismissal, insertion and parameter-information transitions resume diagnostics for the latest snapshot. Semantic-only results cannot erase previous diagnostics, and the protocol rejects mismatched diagnostics policies. Completion items include an optional semantic call shape: presence of parameters, required arguments before a trailing lambda, and a final non-null function parameter. K2 derives this from resolved symbols (including type aliases), not rendered signatures; imports, callable references, type positions and shorthand string templates receive name-only insertions. The client uses that shape to add or reuse call delimiters and records the primary caret together with autoimports in one atomic editor history entry. Newly generated delimiters join smart typing’s tracked closers. Structural Enter expands an empty Kotlin brace pair even when it was inserted by completion or loaded from source, removing inner padding and preserving the document’s line endings and leading indentation. Protocol v13 workers are rejected at handshake; the platform bundle, compiler protocol and VM artifact ABI are unchanged. Rename returns bounded editable project locations after validating a single Kotlin identifier and speculatively checking project diagnostics and reference bindings. The worker restores its original PSI, source identity, and completion state even on cancellation; a failed restoration invalidates the workspace and requires a fresh snapshot open. Rename currently rejects projects with analysis errors, read-only library symbols, the main entry point, and inheritance or convention-based declarations rather than attempting partial refactoring. Presentation results include bounded method-name ranges with non-negative project usage counts. K2 gathers declarations in the active source and resolves references in one project traversal under the presentation session, keeping overloads and same-named methods separate. Counts share the semantic-token item bound, are checked against the correlated source, and appear as clickable same-line labels to the right of declarations; zero counts are hidden. During pending analysis, exact edits rebase the last known counts for untouched method names, avoiding flicker. Edits touching or extending a name discard its label; a fresh accepted presentation replaces provisional counts, while stale responses remain rejected. The previous worker protocol is intentionally incompatible and rejected at handshake. 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. Typing an opening parenthesis requests the same query after lexical context excludes strings and comments; an argument separator can reopen it, and Ctrl+P remains available. Each candidate carries optional bounded active-parameter KDoc, resolved through the same declaration/source mapper used by hover. Protocol 17 adds this nullable UTF-8 body; protocol 16 workers are rejected at handshake. The client owns Ctrl+Up/Down selection and retains it by signature within the same call start while source revisions refresh; dismissal, leaving the call or changing calls resets that selection. Shared popup wrapping preserves UTF-16 parameter spans and selects a bounded window containing the current parameter for long signatures. 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.

Client editor and terminal presentation

The IDE’s visual baseline is IntelliJ IDEA Islands Dark: follow its restrained colors, hierarchy, spacing and interaction states as closely as the Minecraft renderer permits. Use shared theme roles and window/control primitives rather than per-component palettes or font offsets. Floating dialogs, popups and the embedded terminal share thin borders, subtle rounded corners and shadows; the focused terminal uses the standard blue focus accent. The embedded and standalone terminal share TerminalSurfaceStyle.BACKGROUND_COLOR; the default grid background is transparent on that surface. Terminal program colors and the fixed Guest grid remain terminal-owned content. The reference is JetBrains’ Islands theme and Islands integration guidance.

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. Terminal windows, IDE terminal overlays and in-world text displays use packaged JetBrains Mono NL with 6x13 cells and no font selector. The former terminal font preference is no longer part of client configuration; old values are removed by NeoForge configuration correction without affecting IDE layout. The IDE code editor, completion list and hover information use the same bundled TrueType face without ligatures, with 6x12 editor cells (size 10 and line spacing 1.2); terminal/display cells remain 6x13. Editor and terminal drawing use the same font resource ID directly, without a reference alias: Minecraft 1.21 warms font IDs in parallel and its FreeType provider does not synchronize access to a shared native face. The editor metrics drive glyph placement, caret and selection geometry, and hit testing. IDE chrome, dialogs, menus and tooltips use the same face and six-pixel advances, with the matching glyph baseline offset. The Minecraft 1.21.1 adapter opts these explicit JetBrains Mono draws into linear texture filtering. A bounded render-type wrapper delegates vanilla shader, blend, depth and geometry state, applies filtering after vanilla’s nearest-filter setup, and restores the previous texture filters, binding and active unit after drawing. Other fonts remain unchanged. The 26.1 adapter submits prepared glyphs with a cached linear GUI sampler and uses NeoForge’s linear text render types for in-world displays, preserving clipping, transforms and polygon offset without global font or texture-state changes. Visual quality still requires an in-client comparison at the chosen UI scale. The IDE controller owns a bounded project-document cache (128 editor-limited documents by default), so returning to a file preserves its caret, viewport and undo history. Dirty background buffers participate in autosave and are drained before a build, project transition or ordinary close. Polling invalidates clean changed buffers and retains dirty conflicts. Analysis snapshots overlay unsaved project buffers under the admitted source limits and recompute their source identity; they never analyze a mix of renamed active text and stale background disk text. Explicit Find Usages reuses the serialized reference/declaration analysis lane and requires a single resolved declaration. The controller publishes bounded immutable source-context rows in the lower panel, navigates through the existing project navigation history, and rejects queued results invalidated by source edits or project changes. The controller also owns one deduplicated diagnostic view for the problems panel, gutter, counters and F2 navigation. Navigable rows carry exact source evidence; build diagnostics retain bounded admitted source text. Opening a target checks that evidence before applying a UTF-16 offset, including after an asynchronous open or implicit save. Stale build messages remain visible but do not contribute clickable locations or gutter markers. 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.

Client-local projects and Git

project-files owns protected filesystem-root opening shared by the project catalog and compiler snapshots. Supporting providers retain their native SecureDirectoryStream. The default Windows provider uses native no-follow entry handles through Minecraft’s existing JNA library. Directory pins deny delete sharing and retain their ancestor chain until the last dependent child/operation closes. Child opens, enumeration, metadata, byte channels, creation, deletion, rename and atomic replacement operate relative to those handles, so no operation reconstructs a pathname that could follow a concurrently installed reparse point. Write sharing remains enabled: Windows needs it to rename directory entries. Native channels preserve byte positions, truncation and force(true). An in-place reparse mutation invalidates the opened directory; subsequent operations reject it. Only direct, unambiguous Unicode entry names, no-follow reads and exclusive new-file writes are admitted. Reparse entries are classified as special for scanning; cleanup removes their entries without traversing them. Revision conflicts and atomic source/lock publication remain owned by the existing document and lock services. Other unsupported providers fail closed. The Windows backend reads the volume and 128-bit file ID from the pinned handle; project identity is bound to the opened directory, including after a rename. It is loaded lazily and does not change the native VM ABI or project formats. IDE initialization failures are contained at the opening boundary, preserving the parent terminal and reporting a localized notification with technical details in the log.

The Windows project filesystem workflow runs native Windows acceptance for Unicode paths, read/create/rename/replace/delete, cross-directory moves, ancestor pinning, junction rejection, live reparse mutation, large native-channel buffers and the complete catalog/document/ snapshot/lock/tree cycle. It requires non-skipped Windows test reports and builds a Minecraft 1.21.1 production-format candidate with the Runtime compiled from its pinned source. Its artifact is a Windows test candidate, not a tagged universal release. For a local Windows run, use JDK 25 with JDK 21 available to Gradle, initialize host/compukter-vm at its recorded revision, then run:

.\gradlew.bat :project-files:check :ide-core:test --tests '*ProjectFilesystemPlatformTest'

Linux skips of WindowsSecureDirectoriesTest and Wine runs do not replace native Windows acceptance. Canonical platform Kotlin inputs and bundled Guest programs are checked out with LF on every host. Bundles preserve their exact source bytes and declaration offsets; Windows Git autocrlf must not rewrite these build inputs.

ide-core owns backend-independent Git contracts and the project catalog. Owned projects live beneath the client catalog root; external roots are registered in place through bounded catalog records and retain their filesystem identity checks. .git components are reserved and excluded before tree/content budget accounting, including in compiler source scanning. .gitignore remains an ordinary editable file. Clone materializes an owned staging root, validates the manifest and tree, then publishes it through the catalog’s secure directory operation. Failure cleanup never follows directory symlinks. Unavailable external roots do not hide other catalog projects.

ide-git is the JGit implementation leaf; ide-client depends only on the contracts. The NeoForge client injects this backend into DefaultIdeWorkspace, whose bounded single I/O executor serializes Git and document operations. Git results carry controller generation/operation identities. The controller drains dirty buffers first, pauses edits while Git runs, rejects results from a departed project and invalidates affected tree/document/analysis/build state. A failed create/import/clone retains the current project instead of clearing its buffers prematurely. File/folder renames rebase cached descendant paths while preserving editor state.

Read-only inspectGit shares the bounded I/O executor but bypasses foreground save barriers and edit suspension. The controller admits at most one inspection, throttled to one second, and checks project generation, foreground operation identity and buffer revision before displaying line changes. SourceChanges compares the supplied buffer with HEAD using bounded HistogramDiff, normalizes CRLF/LF and leaves disk/index untouched. Binary, ignored new files and over-budget comparisons omit markers. Status colors share one projection for tree, parent folders, active-file title and Commit rows; gutter markers do not replace syntax or semantic coloring.

Remote operations admit HTTPS URLs without embedded credentials, query or fragment. Tokens live in masked client session input and an explicitly closeable credential object; no credential data enters preferences, project files, worker snapshots or server payloads. JGit operation copies are cleared after each operation. GitCredentialScope binds credentials to an exact normalized HTTPS host and effective port. The scoped provider checks each challenge URI; transport callbacks check the actual URI after Git URL rewrites, and a per-transport HTTP connection factory guards every connection before cached authorization headers can be attached, including redirects. HTTPS certificate verification cannot be disabled. Network exception messages and causes are replaced with bounded safe diagnostics; only known transport-policy errors retain their own messages. JVM/HTTP string copies cannot be reliably erased. Commit author identity is explicit. The UI commits selected saved working files, preserving unrelated staged content. The backend never force-pushes and disables commit/push hooks without saving its temporary hook-path override. Linked worktrees, submodule/LFS integration, SSH and merge UI are deferred.

Pull uses JGit FF_ONLY with rebase disabled and requires a clean working tree. Divergence preserves local HEAD, index and working files; tests exercise that exact failure boundary. Cancellation and a 60-second transport/progress timeout bound cooperative operations; already completed mutations are not rolled back. Git defaults bound status and branch lists to 4096 entries, history to 100 commits and diff output to 256 KiB. A separate 512 MiB repository metadata check runs before/after repository operations and after clone; it is post-transfer validation, not a streaming quota. Metadata validation also limits entries to 100,000 and depth to 32, rejecting symlinks.

The shared Git panel and prompt workflows feed both version-specific screens. Production archives relocate JGit, JavaEWAH and Commons Codec into the private vendor namespace and retain their license notices; SLF4J remains loader provided. Both archive gates load and exercise the relocated Git/resource runtime. Behavioral evidence lives in JGitBackendTest, GitWorkspaceIntegrationTest, catalog/compiler/controller tests and shared prompt/input/renderer tests. In-client layout and interaction still require an observed scenario under the verification policy.

The Git UI owns session-local checkbox selection and bounded commit/author drafts. It uses CommitSelected to commit current saved working files for exactly the selected changed paths, with JGit’s path-only commit preserving unrelated index entries. New selected files are admitted to the index first; failed operations can change selected index entries but never implicitly commit unrelated staged content. Preview compares HEAD with the working tree, including an unborn HEAD. The draft survives a commit failure and its message clears only on success. Author fields remain in the IDE session; project changes clear message/selection. Commit & Push submits push only after the matching successful commit reply. Draft editing reuses EditorDocument, and controller ownership/save barriers/generation checks remain authoritative.

The left tool stripe uses shared vector icons for Project, Commit, Terminal and Problems, with Git Log anchored at its bottom. IdeBottomPanelView owns the active Problems/GitLog tab (or a hidden window) and bounded history scroll. History is cached separately from the Commit result/preview and resets per project. Opening Git Log preserves both the editor and Commit sidebar; history replies cannot replace the saved-working-file preview. The bottom window reuses the diagnostics splitter/height across the full content width beneath the left tool window and editor. Commit replaces Project in the left tool window; selected diffs use the editor area and Source returns to editing. Explicit Git focus separates draft and source input, while a displayed diff consumes editing input. Git settings use shared modal surfaces and contain origin, author and session credential actions. The masked token prompt checks GitHub token validity asynchronously on the workspace executor through /user, never repository permissions; edits, cancellation and closure invalidate pending results. Redirects are disabled and requests have bounded timeouts. Read-only inspection refreshes Commit status and, while Git Log is open, bounded local history without clearing drafts, selections for still-changed paths, diff or history scroll. It never fetches remote refs. Both target screens derive bottom visibility from controller state; the splitter owns height only. Geometry fallback may collapse this window when there is insufficient room for the source editor.

Shared IDE screen presentation

minecraft/shared/common owns IdeScreenController: focus, prompt/project menus, panel interactions, terminal overlay routing, geometry and frame preparation. Its IdeScreenHost port supplies clipboard, viewport and parent/session lifecycle effects. IdeFramePainter composes the shared draw model and terminal overlay in one ordered frame; an IdeCanvas port executes fills, icons, text and terminal grids. Neutral viewport and terminal geometry live alongside these shared components. The client controller remains the authority for project/document state and asynchronous work.

IdeScreenController.presentation is the shared admission and extraction path for drawing, pointer input and keyboard focus. A per-event IdePresentationSnapshot links the admitted view, geometry, draw model and interaction projection; Git scroll limits and field reveal positions come from that model. Snapshots are not retained across ticks or domain state changes. Pointer projection is lazy within the snapshot so painting does not flatten another explorer tree.

IdeDrawBuilder collects primitives and interaction metadata, while IdeDrawLayers defines shared painter ordering. IdeWorkspaceHeader, IdeToolbar and IdeStatusBar independently project workspace chrome. The toolbar retains domain availability rules and reserves bounded control rectangles; expanded labels fall back to icons at narrow widths. Neutral and primary control styles share drawing and hit bounds through IdeControls. Text fitting stops at the visible code-point budget rather than allocating an entire long status message.

IdeLocalOverlays owns prompt, quick-open and status-details precedence and routes their input before workspace interaction. Domain dialogs and project transitions retire incompatible local details. IdeStatusDetails captures immutable messages on opening, owns only scrolling and closure, and copies the complete captured text. It never replicates compiler/editor state. Its renderer disables underlying controls; unsupported viewports defer its geometry without losing the captured contents.

IdeSourceColumns owns UTF-16/code-point/tab conversion; IdeSourceGeometry gives one admitted source view its shared gutter, row, caret, source anchor and pointer mapping. Its code bounds exclude line numbers; source text, selection, carets and underlines stay inside those bounds when horizontally scrolled. Styled fragments expand tabs from the line’s actual column. IdeEditorRenderer owns source, selection, highlights and gutter indicators; IdeFindRenderer owns search controls; IdeEditorPopups owns hover, declaration chooser, completion and parameter information. IdePopupText wraps source slices without changing UTF-16 offsets; its diagnostic display helpers normalize CR/LF, expand tabs and keep control characters out of Problems rows and tooltip glyphs. Named argument labels have a distinct semantic color, including calls still missing arguments; ordinary parameter and argument-expression colors remain unchanged. IdeDiagnosticRenderer clips source-evidenced diagnostic spans and IdeDiagnosticTooltip groups their messages. Disabled token tooltip targets preserve normal source clicks. These components emit directly into the existing frame collector and use the same source geometry for anchors.

IdeEditorInputRouter owns search, semantic navigation and ordinary editor keys and characters. Local screen overlays and domain dialogs are routed first, followed by focused Git/usage-panel input. Within editing, focused search takes precedence over the declaration chooser, completion navigation, parameter-info dismissal and ordinary editing. Completion takes precedence over argument hints in rendering and input; hint requests continue while identifiers are completed, so closing completion reveals the current parameter information. Screen-wide shortcuts retain their explicit screen-controller ordering. Clipboard bounds have one shared owner, IdeClipboardText, used by source editing, search and Git drafts. Authoritative document and analysis state remains in ide-client; these components do not cache or independently mutate it.

Remaining debt is explicit: Git, explorer and bottom-panel rendering still live in IdeRenderer.Builder, and domain hit-action routing remains substantial in IdeInputAdapter. Extract future feature owners using these draw primitives; do not duplicate geometry, cache mutable controller snapshots, or move compiler/project behavior into renderers. Version adapters remain limited to Minecraft API differences.

Version-specific IdeScreen shells translate Minecraft input and implement drawing, including font handles, texture pipelines, transforms and scissor semantics. Minecraft 1.21.1 converts logical clips to Minecraft GUI coordinates; 26.1 applies transformed clips through its extractor. The input-rejecting Minecraft keyboard focus proxy remains version-specific. Screen presentation contracts do not expose Minecraft graphics/event types to shared logic.

ide-client owns the ordered open-file list and safe pending tab closure. Text tabs reuse the existing editor sessions, including undo history and viewport state; binary tabs retain bounded metadata. Visible tabs cannot be silently evicted from the document cache. Closing a modified tab waits for its save or formatting operation, while a conflict keeps it open until a resolution. Rename, deletion and project transitions update the same list.

IdeControls produces each control’s drawing and hit target from one rectangle. IdeFileTabBar lays out the visible slice, active-file reveal, overflow controls, labels and close buttons. IdeRenderer composes these components; the shared screen controller owns tab-bar scrolling and passes the same model to drawing and input. New controls and screen interaction belong here; backend changes belong in the Minecraft version adapter only when its API requires them.

IdeTextField owns each modal prompt’s single-line EditorDocument, bounded undo history, selection and horizontal viewport. IdeTextFieldLayout supplies the same code-point geometry to painting and pointer input; IdePromptGeometry keeps the field rectangle shared with the dialog. Closing or advancing a prompt releases its document and history. The component uses the existing editor engine without Minecraft types or version-specific widgets.

Live named peripheral diagnostics

The analysis worker emits bounded immutable facts for successfully resolved platform PeripheralProvider.named and namedOrNull calls, unwrapping inherited symbols and evaluating constant string arguments through K2. It carries the substituted return type and exact UTF-16 argument range, so aliases, addon providers and const val work without inspecting source spelling. Dynamic names and unrelated/custom methods contribute no facts. Analysis protocol 19 carries these facts with the presentation; edits discard them until fresh analysis is accepted.

A shared client observer requests an opt-in network inspection every second, independently of the Devices window. Only one request is pending at a time; five-second expiry/timeout, attachment replacement and closure invalidate observations. Target payload registration 3 admits the flag and availability enum. The server validates the lease, uses existing discovery and saved-network membership on the server thread and never loads chunks for inspection. It includes reachable names plus unavailable saved members with existing availability reasons.

The IDE controller combines source facts with current matching-target observations. Complete observations can report absent names, incompatible lookup types, ambiguous reachable devices, range/dimension/chunk/replacement failures. Unknown contracts and truncated or failed observations never prove absence. Mandatory calls produce warnings; optional calls produce information with None highlight and retain hover/Problems navigation. Network observations remain advisory and never alter compilation, Build results or Guest execution.

IDE named peripheral selection

The workspace picker requests a bounded snapshot through the existing IDE target request broker. The server validates its player-owned target lease and exact profile before consulting the resolved computer’s discovery callback. Discovery uses the same direct/network reachability and name directory as Guest lookup on the server thread, without loading chunks. Each entry carries only its name, duplicate-name flag and block item registry ID; the client resolves and renders the item at 32×32. Request tag 12 and reply tag 13 extend the game transport additively; existing tags and Guest/worker ABIs are unchanged. At most 1024 entries, 64 UTF-16 name units and 256 item-ID units are admitted.

The screen owns the window and pending future, polls completion on the client tick, and rejects responses after a target change or screen closure. Refresh rebuilds the list; disconnected/local targets show an attachment prompt. Source insertion belongs to the IDE controller and the pure ide-core helper, retains headers/imports and line endings, and commits one undoable document edit. It uses the active writable Kotlin buffer and existing analysis/autosave flow.