Files
NeitzelFXDesigner/docs/configuration-and-protocol.md
konrad 2fe408bb4d Discover Model paths and compatible binding targets in both hosts
Add non-constructing target metadata and a shared read-only browser for the Person fixtures. Negotiate binding discovery independently, preserve FXML, and explain direction/type mismatches. Record remaining manual navigation checks as explicitly skipped.
2026-11-13 08:57:35 +01:00

12 KiB
Raw Permalink Blame History

Executable configuration and compatibility

Implementation history: this page records its original bootstrap work package. The first connected workflow now supplies worker transport, draft overlays and runnable minimal hosts; historical test counts below remain scoped to that earlier step.

Bootstrap work package 4 supplies the packaged schemas, shared configuration/context/runtime implementation, and a draft wire codec/session validator. Both host adapters use the same HostServices interface; the same configuration-failure/editing contract runs against standalone files and real IntelliJ Documents. No configuration check changes document writability or closes a draft.

Configuration 1.0

The authoritative application schema and local settings schema use JSON Schema Draft 2020-12. They ship in contracts without a validator dependency. Core executes them using networknt json-schema-validator 1.5.9. The upstream validation interface is the basis for that integration.

ConfigurationFiles.load reads neitzel-fx-designer.json at the effective Maven reactor root. Inputs are capped at 1 MiB and 32 nesting levels. Duplicate keys, trailing JSON values, unknown fields, missing required fields and versions other than 1.0 are rejected. Validation uses only bundled schemas, never schemas or references supplied by application JSON. No automatic migration occurs.

Named contexts require applicationModule, launchMode, buildRuntime, targetRuntime and preview. mavenProfiles and requiredCapabilities default to empty lists; buildJava and targetJava default to minimum feature 21. preview.sourceSet is either module (requiring a reactor-relative module) or test (using the application module, forbidding that extra field). A provider name and optional example IDs are metadata only: validation does not load classes or construct Models. Public provider/session signatures remain work package 6.

module-path requires modulePath.entryModule and permits explicit addReads and addOpens declarations. classpath forbids the module-path block. These are structured declarations, not an arbitrary JVM argument list. Actual module existence/access is validated with the target fixtures later; this step validates declaration syntax and does not silently add opens.

Validated examples cover classpath with a preview module and module-path with test previews.

Effective reactor and context selection

ContextCatalog consumes ReactorView values keyed by context. Each view carries the requested Maven profile list and the effective reactor's modules, source/resource roots and reactor dependency relationships. Profile lists must match exactly. The convenience collection constructor is for unprofiled contexts only. The Build and Refresh helper now obtains the selected effective closure from Maven; no raw-POM profile inheritance is guessed.

All module selections resolve relative to the configuration's canonical directory. Modules must exist, contain a POM and belong to the supplied reactor. Traversal/symlink escapes are rejected. Source roots and dependencies are validated, and matching includes the application's reactor dependency closure plus its preview module. The current model requires existing roots inside their module directories; externally generated/out-of-reactor roots need an explicit later support decision.

Selection returns a selected name plus candidates:

  • One matching context selects automatically.
  • Several matches return a choice unless a still-matching local remembered selection exists.
  • No match returns an empty selection for setup; no arbitrary default is chosen.
  • An explicit inherited parent context takes precedence for an opened Component, including a Component outside ordinary matching roots. Unknown inherited names fail.

Context choice does not create another draft. Both hosts retain their existing one-document ownership semantics.

Local runtime mappings

Local settings remain outside shared application configuration. Example (paths are illustrative):

{
  "schemaVersion": "1.0",
  "runtimes": {
    "build-java": { "build": "/local/jdk-25/Contents/Home" },
    "application-java": { "target": "/local/jdk-21/Contents/Home" }
  },
  "rememberedContexts": {}
}

LocalSettings.load requires absolute installation paths. Resolution runs off the UI thread, checks executable bin/java and bin/javac, and reads the JDK's release metadata. Build and target roles have separate mappings, even if they share a logical name or installation. Missing, invalid or too-old runtimes fail explicitly; the designer JVM is never substituted. This checks installation metadata, not a cryptographic/runtime-launch identity; target startup checks remain required.

LocalSettings.remember atomically saves an explicitly chosen context under the canonical configuration file URI and preserves other settings. Reload to obtain the new immutable mapping. It is a single-writer settings operation, not the future coordinated host-handoff protocol.

ConfigurationGate.check checks required capabilities and each runtime's minimum version, returns the resolved pair, and reports errors through host diagnostics. Parsing/catalog construction also expose stable ConfigurationException.code() values (CONFIG_*, REACTOR_*, MODULE_*, PATH_MISSING, SOURCE_PATH, CONTEXT_UNKNOWN, RUNTIME_*, CAPABILITY_MISSING). Callers display these tooling failures while retaining normal source editing. The editor/setup presentation is work package 7.

Draft wire protocol 1.0

FrameCodec and ProtocolSession are executable framing and host-side negotiation/correlation rules. They deliberately contain no socket launcher or UI dependency. Schema, wire, metadata and preview compatibility are independent; a product version is not a compatibility test. Version 1 here is a bootstrap wire/metadata capability family, not a released Java preview API.

The transport implementation must bind a dedicated loopback socket, associate it with the launched worker handle, keep stdout/stderr as logs, and apply bounded handshake/read deadlines. It must close the connection on malformed frames or authentication/negotiation failure. The reserved owner-lifetime channel remains independent of FX responsiveness. Socket lifecycle, worker launch and owner-death cleanup are not implemented in this step.

Framing

All integers are signed, big-endian: 32 bits except worker PID and document revisions (64 bits). Counts must be nonnegative. Each frame is a 32-bit byte length excluding that prefix, followed by magic 0x4e465831 (NFX1), a one-byte message type and these fields:

Type Fields in wire order
0 HELLO session text, worker PID, token text, wire version, metadata version, preview version, advertised capability set, required capability set
1 PACKET kind byte, operation byte, session/request/context/snapshot texts, revision-map count and text-key/int64 pairs, width, height, payload length, payload bytes
2 WELCOME session text, selected wire version, operation-name set

A version is two int32 values (major, minor). Text is an int32 UTF-8 byte length then strict UTF-8 bytes. A set is an int32 count then unique texts. Packet kinds are REQUEST=0, RESULT=1, ERROR=2, CANCEL=3. Operations are DISCOVER=0 and RENDER=1. Unknown discriminators, negative versions/revisions, duplicate map/set entries, invalid UTF-8, truncation and trailing bytes fail. A fixed independently encoded wire fixture locks down byte order and field order.

Limits are checked before allocating nested fields:

Resource Limit
Entire frame body 17 MiB
Individual text field 4 KiB UTF-8
Metadata payload 1 MiB
Raw image payload 16 MiB
Image dimensions 1–8192 each, additionally width × height × 4 ≤ 16 MiB
Revisions or capability set 128 entries
Pending requests per session 128
Request timeout Positive, at most five minutes

RENDER results carry exactly width × height × 4 bytes, row-major RGBA8 (top-left origin, straight alpha). There is no compressed image decoder in this protocol. Other payloads are strict UTF-8; discovery/request metadata semantics are intentionally reserved for the real target adapter. ERROR payloads are CODE + newline + human detail, using INVALID_REQUEST, LOAD_FAILED, TIMEOUT, CANCELLED or INTERNAL_ERROR. CANCEL has no payload. Log text cannot be accepted as a frame.

Negotiation and request lifetime

The launcher generates 32 cryptographically random bytes encoded as 64 lowercase hex characters, unique per launch. Worker HELLO must match the expected session, launched PID and token over a loopback connection. Token comparison is constant-time for equal-length credentials; HELLO's string representation redacts credentials. Credentials must not enter logs. Authentication is single-attempt; failure closes the validator state.

Wire major 1 is required; additive peer minor versions negotiate down to 1.0 in WELCOME. Unknown optional capabilities are ignored; unmet required capabilities reject negotiation. discover-v1 additionally requires metadata major 1. render-rgba-v1 additionally requires preview major 1. These operations are enabled independently, so an incompatible preview family can leave discovery usable. The host emits WELCOME after authentication; workers must wait for it before processing requests.

Each host-created request has a session-unique increasing ID, context and immutable snapshot identity. Renders also identify document revisions and requested dimensions. A result/error is accepted once only when every identity field and operation match a live pending request. Unrelated or stale replies return false. On any input, context, snapshot or size change, the caller cancels the old request before issuing its replacement; this explicit step invalidates the old result. Cancel, timeout, error and close retire pending requests. expire(now) returns cancellation packets for transport delivery; timer ownership and actual worker interruption belong to the future transport. Request/accept also retire expired entries defensively but do not themselves write to a socket.

Validation and remaining scope

The standard bootstrap command runs the configuration/schema/context/runtime tests, framing/negotiation/correlation tests, and the shared setup-failure/editing test in both hosts. Existing bytecode, artifact identity, dependency isolation and plugin packaging gates remain in force. The IDE compatibility restriction warning documented in the previous step is unchanged.

Verified on 2026-11-12: 33 tests passed (30 Maven, three IntelliJ), with zero failures, errors or skipped tests. The full clean bootstrap check also passed Java 21 target linkage, bytecode/dependency checks, plugin packaging and documentation validation. No socket/worker or rendered UI qualification is implied.

Scoped Maven build planning and immutable successful-build snapshots are now implemented. Real provider discovery, metadata payload schemas, target rendering, socket integration, setup UI, OS qualification and the first connected two-host workflow retain their later gates.

Stage-3 additive preview capabilities

The runtime session implementation adds negotiated layout-v1 (MEASURE/OCCURRENCES) and interactive-v1 operations without changing existing operation ordinals. The optional Preview configuration fields requestTimeoutSeconds and readinessTimeoutSeconds are schema-validated and default to 30 and 20 seconds. Readiness cannot exceed the request deadline. Measurement metadata and images retain full session/build/document/size correlation.

Binding discovery capability

The optional bindings-v1 metadata capability negotiates the appended BINDINGS operation. Requests carry context/build identity and non-empty document revisions, zero image dimensions, and bounded metadata fields fxml, path, and direction. Results use binding metadata format 1 (Model, Attributes, targets, compatibility and diagnostics). Unsupported metadata majors reject required capability negotiation. See the shared discovery slice.