Make configuration and compatibility contracts executable
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
# Executable configuration and compatibility
|
||||
|
||||
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](../designer-contracts/src/main/resources/schema/configuration-1.0.json) and [local settings schema](../designer-contracts/src/main/resources/schema/local-settings-1.0.json) 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](https://github.com/networknt/json-schema-validator/tree/1.5.9#usage) 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](../designer-core/src/test/resources/configuration/classpath-module.json) and [module-path with test previews](../designer-core/src/test/resources/configuration/module-path-test.json).
|
||||
|
||||
## 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. Obtaining these effective models from Maven belongs to Build and Refresh (work package 5); this implementation does not parse raw POMs and guess profile inheritance.
|
||||
|
||||
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):
|
||||
|
||||
```json
|
||||
{
|
||||
"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](../designer-contracts/src/test/resources/protocol/discovery-request.hex) 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.
|
||||
|
||||
Next: scoped Maven build planning and immutable successful-build snapshots. 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.
|
||||
Reference in New Issue
Block a user