Make configuration and compatibility contracts executable

This commit is contained in:
2026-11-12 08:27:53 +01:00
parent 306266ae40
commit 85a75383a0
28 changed files with 2284 additions and 8 deletions
+100
View File
@@ -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.
+4 -4
View File
@@ -1,6 +1,6 @@
# NeitzelFXDesigner initial project contract
Status: accepted architecture consolidated from decision rounds Q1–Q15 on 2026-11-12. Module and JSON field spellings below are proposed implementation names; public Java signatures and numerical protocol limits must pass their bootstrap validation before publication. This document refines roadmap stage 1, not the complete product specification.
Status: accepted architecture consolidated from decision rounds Q1–Q15 on 2026-11-12. Module names and configuration 1.0 fields are implemented by bootstrap work packages 3–4. Public preview Java signatures remain provisional; the draft wire codec and limits now have regression coverage. This document refines roadmap stage 1, not the complete product specification.
Decision provenance: [accepted workspace decisions](provenance.md). Execution order: [repository bootstrap plan](repository-bootstrap-plan.md). Existing authoring, preview, binding, generation and host requirements remain in the [broader roadmap context](provenance.md#broader-roadmap).
@@ -52,7 +52,7 @@ Each host owns its own engine/UI instances. The authoritative document and undo
`neitzel-fx-designer.json` lives at the application's Maven reactor root. It contains a schema version and named application contexts. A context selects an application module, Maven profiles, classpath/module-path launch mode, preview sources/module and examples. Relative paths resolve from the configuration location, never the process working directory. Validate modules and paths against the selected reactor. Obtain dependency graphs and ordinary source/resource roots from Maven rather than maintaining competing lists.
Illustrative shape for the first schema (field names are a concrete draft, not a released schema):
Configuration 1.0 is now executable; see the [schemas and validation contract](configuration-and-protocol.md). The following example is valid input, with Java requirements defaulting to 21:
```json
{
@@ -75,7 +75,7 @@ Illustrative shape for the first schema (field names are a concrete draft, not a
}
```
The schema must also cover `test` preview sources in the application module, module-path entry/access settings and runtime requirements. Example IDs belong to provider descriptors; configuration selects a default and may select/filter descriptors rather than duplicating their metadata. A provider class name identifies a typed implementation, not an arbitrary reflective factory method. Provider registration/descriptor extraction must be validated against the library before the public API is frozen.
The schema covers `test` preview sources in the application module, module-path entry/access settings and runtime requirements. Example IDs belong to provider descriptors; configuration selects a default and may select/filter descriptors rather than duplicating their metadata. A provider class name identifies a typed implementation, not an arbitrary reflective factory method. Provider registration/descriptor extraction must be validated against the library before the public API is frozen.
Select the context automatically when exactly one matches. Otherwise ask, show the active context and remember the choice locally. Opening a Component from a parent inherits that context. Shared source documents retain one host-owned draft even when usable in multiple contexts.
@@ -107,7 +107,7 @@ A parent rendering snapshot shares its Model graph across embedded Components; s
Track configuration schema, wire/metadata contracts and preview API compatibility independently from product releases. Major changes represent breaking contracts; additive features use declared optional capabilities. Equal product versions alone are not proof of compatibility. Verify discovery and rendering independently so one unavailable capability does not unnecessarily disable the other.
Use a dedicated loopback-only socket with a per-launch random token and version/capability handshake for worker messages. Standard output/error remain captured application logs and cannot carry protocol framing. Do not log tokens. Bind the session to the launched worker and reject messages before authentication/negotiation. Use explicit bounded framing, validate lengths and resource limits before allocation, correlate requests/replies and define cancellation/error outcomes. Exact encoding and limits are bootstrap protocol-specification tasks, not facts established by the prototype.
Use a dedicated loopback-only socket with a per-launch random token and version/capability handshake for worker messages. Standard output/error remain captured application logs and cannot carry protocol framing. Do not log tokens. Bind the session to the launched worker and reject messages before authentication/negotiation. Use explicit bounded framing, validate lengths and resource limits before allocation, correlate requests/replies and define cancellation/error outcomes. Encoding, limits and negotiation are now defined by the [executable draft wire contract](configuration-and-protocol.md#draft-wire-protocol-10); transport integration remains later work.
Every applicable request/result identifies its worker session, request, context and build snapshot; rendering also identifies document revisions and requested size. Obsolete results cannot mutate current document/UI state. Diagnostics identify operation and available source origin while preserving editing.
+2 -2
View File
@@ -8,7 +8,7 @@ Work package 3 implements a buildable service foundation. It does not register a
| --- | --- | --- |
| designer-contracts | 21 | Immutable draft and edit DTOs; no framework dependencies |
| designer-preview-api | 21 | Provisional example descriptor; no framework dependencies |
| designer-core | 21 | Contracts; host interfaces, runtime mappings, process builds, lifecycle scope |
| designer-core | 21 | Contracts; schema validation, context selection, host interfaces, runtime mappings, process builds, lifecycle scope |
| designer-kit-ui | 25 | Core and Scene Builder Kit 26.0.0 (JavaFX 26); FX-thread construction seam |
| designer-standalone | 25 | Shared Kit UI; file-backed drafts and undo adapter |
| designer-target-tools | 21 | Contracts, preview API, recorded NeitzelLib snapshot; linkage smoke entry point |
@@ -43,7 +43,7 @@ Run the single command documented in [README](../README.md). Shared modules use
Tests exercise atomic multi-range edits, stale and invalid requests, independent handles, standalone/native IDE undo and redo, disk conflicts, separate runtime roles, process output/cancellation and scope cleanup. The Java 21 target smoke checks linkage to the recorded library, contracts and preview descriptor; it does not initialize a Model or JavaFX rendering. Plugin ZIP creation and project configuration verification do not replace Plugin Verifier, manual IDE interaction, accessibility, or JavaFX exclusivity qualification.
The next work package makes configuration/context resolution and compatibility executable. The first full two-host workflow remains work package 7.
Configuration/context resolution and compatibility are now [implemented](configuration-and-protocol.md). The first full two-host workflow remains work package 7.
Validation on 2026-11-12: eight Maven tests and two native IntelliJ tests passed, Java 21 linkage passed, and the clean plugin build passed. A deliberately injected JavaFX dependency in contracts was rejected by Maven Enforcer and then removed. The plugin archive is checked for host JavaFX implementation JARs and exclusion of target artifacts. Gradle needs explicit macOS classifier dependencies because it does not select Maven's OpenJFX platform profiles. Other OS packaging is not qualified yet.
+3 -1
View File
@@ -1,6 +1,6 @@
# NeitzelFXDesigner repository bootstrap plan
Status: repository foundation implemented by the initial commit; work package 2 has a verified development artifact; work package 3 implements the module/service foundation; work packages 4–7 remain planned. Consolidated 2026-11-12. Scope is the ordered implementation start for the [accepted initial contract](initial-project-contract.md); the initial commit creates the foundation only and does not publish a library release.
Status: repository foundation implemented by the initial commit; work package 2 has a verified development artifact; work package 3 implements the module/service foundation; work package 4 implements configuration and protocol validation; work packages 5–7 remain planned. Consolidated 2026-11-12. Scope is the ordered implementation start for the [accepted initial contract](initial-project-contract.md); the initial commit creates the foundation only and does not publish a library release.
## Starting-point evidence before bootstrap
@@ -58,6 +58,8 @@ Build shared libraries/fixtures using the Maven structure; verify and pin the ac
### 4. Make configuration and compatibility executable
Implemented: packaged schemas, profile-qualified effective-reactor context selection, local runtime mappings, shared compatibility gating, bounded wire codec and authentication/correlation state. See [implementation and validation](configuration-and-protocol.md). Effective Maven model acquisition, worker transport and setup UI retain their later implementation gates.
Implement JSON schema, context resolution and local runtime mappings. Turn the illustrative fields into a versioned v1 schema and fixtures. Check invalid paths/modules, missing runtimes, unknown schema versions/capabilities, multiple matching contexts and parent-to-Component context inheritance. Define protocol framing, bounded message/image payloads and negotiation/error fixtures before worker implementation depends on them.
**Exit:** common configuration tests pass in both host adapters; incompatible setup leaves source editing available; protocol specification is testable and independently versioned.