Files
com_r35157_nenjim-hubd-impl…/openspec/changes/81-configure-ordered-process-autorun/design.md
T
2026-09-03 14:36:00 +02:00

7.6 KiB

Context

See proposal.md for motivation and the delta specifications for observable behavior. NenjimHubImpl currently mixes passive and active construction in shared initialization methods, then resolves and starts a fixed list of already-registered processes. Registry is the bootstrap exception, Journal currently starts before the rest of composition, and process dependencies are retained as concrete Hub fields.

The change must keep the public Hub, Registry, process, Journal, and JavaFX APIs unchanged. Human-written Java remains under src/main/tjava; Detag remains part of the normal Gradle build. Issue #81 excludes general automated tests, so verification uses strict OpenSpec validation, source inspection, and the complete Gradle build without running the production Hub.

Goals / Non-Goals

Goals:

  • Make configuration parsing and all statically knowable validation complete before Registry startup.
  • Make the passive/process boundary visible in composition structure and ensure no process constructor runs in the passive phase.
  • Represent process construction capability without preconstructing instances or encoding a second startup order.
  • Preserve the existing component IDs, concrete implementations, injected dependencies, single-attempt lifecycle, startup snapshots, online point, and wait behavior.
  • Let missing predecessor processes fail through the existing constructor/start contracts without adding implicit dependency behavior.

Non-Goals:

  • No reusable public configuration framework, process factory, graph resolver, dependency metadata, test hook, or alternative Hub constructor.
  • No refactoring of embedded operational configuration unrelated to process selection.
  • No cleanup of legacy Hub/process prototypes and no change to component-specific lifecycle behavior.

Decisions

Parse into one immutable internal startup plan

Add a package-private Hub configuration value and parser under com.r35157.nenjim.hubd.impl.ref. The parser reads conf/NenjimHub.conf as UTF-8, checks file kind/readability, and returns an immutable ordered list of canonical NenjimComponentId values. It receives the implementation's known selectable ID set so syntax, duplicates, capability, and mandatory placement are all rejected in one pre-start phase.

The parser uses an explicit state machine for before-version, before-section, inside-section, and after-section states. It distinguishes a whitespace-only source line from a comment-only line before removing trailing # content, which preserves the issue's unusual rule that blank lines terminate [AUTO_START] while comments do not. Error helpers attach path and one-based line details consistently.

Using the existing general format-version validator was considered. It does not by itself model section termination and would require a parallel preprocessing/state layer; keeping the complete grammar in the Hub parser avoids two competing interpretations and permits more precise diagnostics.

Use a constructor catalogue, not an autorun list

NenjimHubImpl owns an implementation-only map from every non-Registry selectable process ID to a no-argument constructor operation. Bound method references describe capability but do not create process instances. Registry remains an explicit bootstrap exception and is added only to the known-ID validation set. Map iteration order is never used; the validated configuration list is the sole startup order.

A switch statement was considered but would scatter selection, construction, and validation membership. A public or Registry-provided factory was rejected because discovery and extension APIs are outside scope.

Split eager passive composition from lazy process construction

Existing building blocks, price sources, Jupiter services, the swap service, and notification service remain eagerly constructed and registered after Registry startup. Ticker, both Evelyn services, the Burner, Jupiter Perps Alarm, JavaFX runtime, Mission Control, Process Manager, and Journal move to individual lazy constructor operations because their concrete implementations participate in NenjimProcess.

Each lazy operation assigns the same Hub field used by downstream constructors, but returns the newly constructed canonical process before registration. This preserves current dependency wiring: for example, Evelyn construction fails if Ticker has not already been selected, and Mission Control fails if its configured Evelyn or JavaFX predecessors are absent. No null placeholder is registered and no constructor is invoked speculatively.

Journal's lazy operation also retains its passive manager from the assembly. The Registry assembly remains special because Registry must start before any ordinary registration can be queried.

Apply one registration-before-start path to configured processes

For each configured ID after Registry, the Hub looks up its constructor operation, constructs the process, registers it under the configured ID, resolves the registered object through NenjimProcess, starts it, and records the ID only after successful return. A constructor failure therefore precedes registration; a start failure can leave the registered instance visible but cannot enter the successful-start snapshot. Exceptions propagate immediately, so later map entries are irrelevant and later configured IDs are untouched.

Registry retains its required exceptional order of start, self-registration, then recording. The online announcement and blocking wait remain after the configuration loop only.

Keep deployment configuration separate from its template

The runtime path stays a private constant in the reference Hub. conf/NenjimHub.conf.example is version controlled and documented, but no code references that example path. The example deliberately contains only the mandatory Registry and Journal entries so it is safe and generic for downstream users; operators add their own deployment-specific processes and dependency order after copying it. The real file remains deployment-specific under the existing *.conf ignore rule. Documentation shows copying explicitly and includes the same minimal headless bootstrap.

Risks / Trade-offs

  • [Constructor-order coupling remains implicit in fields] → Document that [AUTO_START] is the complete manual dependency closure and keep failures clear; dependency metadata is intentionally deferred.
  • [A selected process constructor may assign a Hub field before later registration fails] → Do not retry or roll back; fail-fast startup is single-attempt and the Registry remains the observable boundary.
  • [Parser edge cases around comments and blank lines are easy to conflate] → Decide termination from the original trimmed line before trailing-comment removal and keep parser states explicit.
  • [A future process may be added to construction but forgotten in validation] → Derive known non-Registry IDs from the same constructor catalogue used at runtime and add Registry programmatically.
  • [Headless verification could accidentally initialize JavaFX] → Do not run the production Hub during verification; inspect constructor placement and rely on the complete compilation/build path.

Migration Plan

  1. Deploy the new build together with an operator-created conf/NenjimHub.conf copied from the version 1 example and extended with the deployment's own ordered process selection.
  2. Validate that manually ordered dependencies precede their consumers; Registry and Journal must remain first and second.
  3. Start the Hub normally through the unchanged Main entry point.
  4. To roll back the software, restore the previous build and its prior deployment configuration expectations; the application never rewrites either runtime or example configuration.