Implement isolated Build and Refresh with immutable leased snapshots

This commit is contained in:
2026-11-12 09:28:25 +01:00
parent 85a75383a0
commit e97af99412
27 changed files with 2066 additions and 15 deletions
+80
View File
@@ -0,0 +1,80 @@
# Build and Refresh with immutable snapshots
Work package 5 implements the shared build coordinator, effective Maven capture helper and persistent snapshot store. This is usable through host services; wiring the editor action, discovery restart and preview workers remains work package 7. No application class runs inside the IDE or shared UI.
## Workflow
`BuildRefresh.start` returns a cancellable run with a completion stage. It accepts a configuration/context, an optional explicitly selected fallback Maven executable, exact approved document revisions, and an output callback. Callers must not block an IDE/UI thread waiting for the result. Source editing remains available throughout.
```mermaid
flowchart LR
Inputs[Known drafts / runtime choices] --> Gate{Unsaved build inputs?}
Gate -->|Yes| Save[Review exact revisions: Save and build or Cancel]
Save --> Inputs
Gate -->|No| Copy[Isolated reactor copy]
Copy --> Plan[Maven effective plan]
Plan --> Build[Scoped clean / dependencies / package / capture]
Build --> Verify[Check inputs and dependency identities]
Verify --> Publish[Atomic snapshot publication]
Publish --> Lease[Lease one build identity for discovery and preview]
Build -->|Failure or cancellation| Retain[Retain old build as outdated]
Retain --> Explicit[Explicit Preview with last successful build]
```
The result is READY, NEEDS_SAVE, CANCELLED, FAILED or STALE. NEEDS_SAVE includes the document URI and revision list and launches no Maven process. A later call must approve exactly the current list; newer edits require another review. The host saves through its own Document services. An autosave that already persisted the approved IntelliJ revision is accepted. Hosts without unsaved-input inventory fail closed instead of assuming disk is current.
The initial input closure is deliberately conservative: known non-FXML drafts throughout the reactor, plus user Maven settings/toolchains. It can request saving an unrelated reactor draft, although Maven builds only selected modules and dependencies. This avoids guessing profile-dependent build inputs before Maven has evaluated the model. Another process's unsaved buffers are not observable. Unsaved FXML is reserved for the later draft-overlay mechanism and does not by itself require Java rebuilding; edits to saved resources do affect freshness.
## Maven integration and scope
`designer-maven-plugin` is a separate Java 21 Maven plugin, installed by the bootstrap command and excluded from IDE/application runtime dependencies. It runs in the selected build JDK. It uses [MavenProject's effective model and runtime/test classpaths](https://maven.apache.org/ref/3.9.11/maven-core/apidocs/org/apache/maven/project/MavenProject.html), rather than reconstructing dependency graphs from POM text or parsing console output.
The coordinator prefers the reactor wrapper. Without one, an absolute executable Maven path must be selected; there is no PATH fallback. Maven 3.9.11 is the qualified baseline. JAVA_HOME and the leading PATH entry are set from the named build runtime; the target runtime is resolved and recorded separately. Profiles are explicitly passed, and `-pl` selects the application and optional preview module with `-am` for required reactor dependencies. The command and output are observable.
The helper goals run in this order:
1. `plan`: inspect the selected effective projects, check profile activation, and reject configured source/resource/output paths outside each isolated module before executing application lifecycle phases.
2. `clean`: remove copied stale outputs, including custom in-module output directories.
3. `dependencies`: capture resolved dependency coordinates, content hashes and file attributes before compilation for each project.
4. `package`, with tests skipped but test compilation enabled: supports both a dedicated preview module and application test-source previews.
5. `capture`: verify dependency identities again and emit effective runtime/test classpaths, source/resource roots, profiles, reactor relationships and coordinates as machine-readable properties outside the build outputs.
The returned `ReactorView` contains the selected effective closure for context matching. Generated roots outside the original source tree are not offered as editable source roots. Module-path settings are retained in the snapshot's context specification; real module-path preview launch/access remains work package 6.
## Consistency and capture
Each run creates a private copy below `.neitzel-fx-designer/work`. Conventional Maven `target` outputs, Git/editor metadata and designer caches are excluded. Ordinary source packages named `target` remain inputs. The original reactor's output directories are not modified by this build.
User `settings.xml` and `toolchains.xml` are copied into the private run directory and selected explicitly. If absent, empty settings are used. Their fingerprints are included in the input identity, but their contents are not published into the snapshot. Standard Maven distribution/global configuration and plugin-controlled external state remain build-environment inputs; this implementation is not a sandbox or a promise of reproducible arbitrary custom plugins.
Input content hashes, file identity/size/mtime and filesystem change notifications are checked around isolation, compilation and publication. Watch overflow or detected changes invalidate the build. A private source copy prevents later original edits from changing the files being compiled. This combines checks across the operation; it does not rely on one timestamp taken after compilation. Filesystem observations cannot provide an adversarial, atomic multi-file transaction against external tools that deliberately restore content and all metadata; supported usage assumes ordinary editor/build activity.
After Maven succeeds, runtime class directories, packaged reactor dependencies, external JARs, compiled resources and test output (when selected) are copied into a separate snapshot staging directory. No runtime entry points at a mutable original `target` directory or dependency-cache file. File hashes and complete directory membership are rechecked after capture. Dependency signatures recorded before compilation must still match at publication. Cancellation or changed inputs prevent current activation; partial staging is never a selectable build.
A JSON manifest records:
- Snapshot UUID, context and creation time.
- Source/settings input identities.
- Ordered snapshot-relative runtime entries and per-file SHA-256 hashes.
- Effective project/dependency coordinates and the context configuration.
- Build/target runtime paths and feature versions, command, manifest and wire versions.
Publication moves the complete staging directory atomically and then atomically updates the context's last-successful pointer. If interrupted between those operations, an unreferenced complete snapshot can be evicted; a partial snapshot cannot become the current build. Snapshot data is immutable through the store interface and independent of later build outputs. The cache is not a security boundary against a process intentionally modifying its files; lease acquisition verifies its contents and file set.
## Freshness, explicit fallback and lifetime
Starting a build marks previous results outdated. A failed, cancelled or invalidated build preserves the last successful ID and reason. `previewCurrent` rechecks source identities, known unsaved inputs, configuration identity and resolved runtime mappings before returning a lease. It throws for outdated state. Only `previewLastSuccessful` explicitly opts into the retained build; no failed build silently activates old output.
A lease supplies ordered private runtime paths and one manifest/build identity for discovery and rendering. Each lease holds a filesystem lock in a unique ticket; eviction checks those locks across threads, store instances and processes. Published last-successful snapshots are retained even without a lease. Closing a lease permits eviction of superseded snapshots. Process death releases its OS lock; abandoned tickets and staging directories are recoverable. Normal run workspaces are removed when the run finishes; abandoned private workspaces after a host crash are ignored, not activated, and may be removed when no build process owns them. There is no automatic age/size quota yet.
Cancellation stops the owned Maven process and observed descendants through the shared process runner. Capture checks cancellation between files. Cancellation after the publication commit point can complete as READY; it does not retroactively delete a successfully published build. The caller must keep the lease alive for the full worker lifetime and release it after worker shutdown; worker integration is a later gate.
## Validation
The normal [bootstrap check](../README.md#validate-the-foundation) installs the helper, runs the tests and then executes a real Maven smoke fixture using Java 21. Its four-module reactor includes a deliberately uncompilable unrelated module. Checks prove that only the selected application/dependency/preview scope builds, profile-selected module previews and test-source previews work, resources are present, original outputs remain untouched, old snapshots keep running after a rebuild, and compiler failure preserves explicit last-successful preview.
Unit/integration regressions additionally cover exact save approval, FXML draft freshness, input changes during build, cancellation, dependency/output changes during capture, staging recovery, snapshot integrity, same-JVM and separate-process leases, owner death, native IntelliJ dirty-Java inventory and autosave. The earlier configuration, protocol, bytecode, runtime artifact identity and plugin packaging checks remain required.
Acceptance on 2026-11-12: **45 tests passed** (41 Maven, four IntelliJ), with no failures/errors/skips, plus **14 real Maven/Java 21 smoke checks**. The full clean bootstrap command also passed artifact identity, bytecode/dependency isolation, target linkage, plugin packaging and documentation checks. The previously documented intentional IntelliJ until-build warning remains.
The next bootstrap step validates the typed preview API and target adapter against real NeitzelLib classpath/module-path fixtures. Setup UI, worker transport, discovery/render restart, draft overlays and the connected two-host workflow remain later gates.
+2 -2
View File
@@ -16,7 +16,7 @@ Validated examples cover [classpath with a preview module](../designer-core/src/
## 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.
`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](build-and-refresh.md) 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.
@@ -97,4 +97,4 @@ The standard bootstrap command runs the configuration/schema/context/runtime tes
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.
Scoped Maven build planning and immutable successful-build snapshots are now [implemented](build-and-refresh.md). 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.
+3
View File
@@ -27,6 +27,7 @@ The artifact names below are established by work package 3. See [implemented bou
| `designer-intellij` | FXML editor, IDE Documents/commands, project/build/navigation integration and focus | Adapts the same core/UI; qualify build and packaging against the selected IDE SDK |
| `designer-preview-api` | Typed example descriptors and fresh preview-session lifecycle | Small project-facing artifact; no Kit or IntelliJ dependency |
| `designer-target-tools` | Discovery and NeitzelLib loading/rendering adapter in the application runtime | Uses contracts/preview API and target-compatible library dependencies; never execute application classes in a host |
| `designer-maven-plugin` | Effective project/dependency/runtime capture in the selected Maven build JVM | No Kit/IntelliJ or target application runtime dependency |
| `integration-fixtures/` | Real classpath and module-path Maven applications, examples and failure cases | Same scenarios consumed by both hosts; normal artifact dependencies |
Shared contracts may be Java artifacts without implying identical JDK/JavaFX versions in hosts and workers. Select bytecode/API baselines per module from the supported target fixtures; verify that application-facing artifacts load on the oldest initial target before publishing them. The IntelliJ packaging toolchain must be pinned and tested; a Maven reactor for shared artifacts does not assume that ordinary Maven JAR packaging produces a supported plugin.
@@ -83,6 +84,8 @@ Local settings hold installed JDK mappings, absolute machine paths and remembere
## Build and snapshot contract
Work package 5 now implements the shared [Build and Refresh workflow](build-and-refresh.md), including its validation and explicitly bounded input closure. Worker activation and UI wiring remain work package 7.
1. Resolve the selected context and required reactor dependencies, including configured preview sources. Prefer the Maven wrapper; expose the plan, command/output and cancellation. Avoid unrelated applications unless dependency resolution requires them.
2. Identify known unsaved Java, Maven and designer-configuration inputs affecting the build. Offer **Save affected files and build** or **Cancel**, listing the affected files. Do not implicitly build older disk versions. Account for IDE autosave; another process's unsaved buffers are not observable.
3. Capture the relevant input identity/revision manifest and execute the build with the selected build runtime. FXML-only draft edits have separate revisions and need not trigger Java compilation.
+2 -1
View File
@@ -12,6 +12,7 @@ Work package 3 implements a buildable service foundation. It does not register a
| 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 |
| designer-maven-plugin | 21 | Maven 3.9.11 provided APIs; effective plan/dependency/runtime capture inside the build JVM |
| designer-intellij | 25 | Shared Kit UI; actual IDE Documents, commands, navigation, project disposal |
```mermaid
@@ -35,7 +36,7 @@ Document operations return completion stages. Callers must not block UI threads
Save checks the expected revision and compares disk content with the captured baseline. Conflicts preserve both draft and external content. This is initial conflict detection, **not** atomic coordination with arbitrary external writers: another writer can race the check. IntelliJ autosave/external reload reconciliation and complete encoding/line-ending fidelity need the later document integration gate. Standalone currently reads/writes UTF-8 and retains history for the lifetime of its host.
Build execution accepts an already resolved command/environment/working directory, drains both output streams and supports cancellation of the process and observed descendants, with forced termination after a bounded grace period. The lifecycle scope closes owned resources once in reverse order and continues cleanup after individual failures. Runtime lookup requires an explicit name/role mapping and has no fallback JVM. Navigation and diagnostics remain host callbacks. Build planning, immutable snapshots, worker authentication and owner-death IPC are not implemented by this process runner.
Build execution accepts an already resolved command/environment/working directory, drains both output streams and supports cancellation of the process and observed descendants, with forced termination after a bounded grace period. The lifecycle scope closes owned resources once in reverse order and continues cleanup after individual failures. Runtime lookup requires an explicit name/role mapping and has no fallback JVM. Navigation and diagnostics remain host callbacks. The shared [Build and Refresh coordinator](build-and-refresh.md) now owns planning and snapshots above this process runner. Worker transport and owner-death IPC remain later work.
## Validation and packaging
+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 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.
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 package 5 implements Build and Refresh with immutable snapshots; work packages 6–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
@@ -66,6 +66,8 @@ Implement JSON schema, context resolution and local runtime mappings. Turn the i
### 5. Implement Build and Refresh with immutable snapshots
Implemented: shared coordinator, scoped effective Maven capture, explicit draft save approval, isolated builds, consistent private runtime copies, atomic publication, persistent outdated/last-successful state and cross-process snapshot leases. See [implementation and limits](build-and-refresh.md). The UI and worker restart consume these interfaces in work package 7.
Resolve scoped Maven builds, preview source arrangements, explicit save/cancel and runtime choices. Capture successful outputs/resources/dependencies with an input/output manifest. Validate consistency and publish atomically; pin active snapshots against eviction. Provide explicit last-successful-build selection and stale diagnostics.
**Exit:** success, cancellation, failed build, input edits during build/capture, mutable output replacement, and snapshot-lifetime tests pass. Known unsaved build inputs cannot silently fall back to disk.