Make JournalService a Hub-managed Nenjim process and integrate it into startup #78

Closed
opened 2026-09-02 12:59:23 +02:00 by minimons · 0 comments
Owner

Dependency

This issue depends on completion and acceptance of issue #77, which introduces:

  • The global NenjimHub runtime owner.
  • The canonical NenjimProcess.
  • Hub-owned construction, registration and startup.
  • The ordered getStartedProcessIds() snapshot.
  • Passive, unregistered service-manager handles.

Do not implement this issue on the pre-#77 architecture.

Background

Issue #75 implemented the Journal format, immutable domain model, strict parser, chain ingestion, read-only query service and filesystem lifecycle manager.

The current lifecycle differs from the runtime architecture established by #77:

  • JournalServiceManagerImpl constructs JournalServiceImpl.
  • JournalServiceManager.start() performs initial loading.
  • The manager owns start and stop.
  • The manager exposes the service only after startup.
  • JournalService is not a Nenjim component or process.
  • The Journal subsystem is not constructed, registered or started by NenjimHub.

The intended common model is now:

  • The Hub initiates construction of services and their passive managers.
  • The service implementation is the active NenjimProcess.
  • The Hub registers the service in the Registry.
  • The Hub starts the service and records its process ID.
  • The manager is only an administrative indirection for operations that ordinary consumers must not receive.

This issue migrates the Journal subsystem to that model without changing the Journal format, model, parsing or query semantics.

Goals

This issue must:

  1. Make JournalService a Nenjim component.
  2. Make JournalServiceImpl the active Journal NenjimProcess.
  3. Move initial filesystem loading into the Journal process startup.
  4. Reduce JournalServiceManager to a passive administrative handle.
  5. Make the Hub construct the Journal service and manager.
  6. Register the Journal service as nenjim.journal.service.
  7. Start Journal immediately after the Registry and before business processes.
  8. Include Journal in the Hub’s started-process snapshot.
  9. Preserve all Journal format, validation, chain and refresh behavior.
  10. Fail Hub startup clearly if runtime Journal data is invalid.

JournalService remains the read-only domain API

Change the service declaration so it is registrable:

public interface JournalService extends NenjimComponent {
    // Existing query API unchanged.
}

The existing query operations and their semantics must remain unchanged:

  • artifactCoordinates()
  • revisions(...)
  • latest(...)
  • revision(...)
  • revisionAtOrBefore(...)
  • release(...)

The public service interface must still expose no:

  • Lifecycle operation.
  • Filesystem path.
  • Refresh operation.
  • Parser.
  • Ingestion method.
  • Mutable chain state.
  • Administrative capability.

Ordinary consumers receive only JournalService.

JournalServiceImpl becomes the process

The reference service object must implement both:

JournalService
NenjimProcess

The same object must therefore be indexed under both interfaces when registered.

Its start() operation must:

  1. Accept only the configured Journal data root supplied during construction.
  2. Validate that the root exists, is a directory and is readable.
  3. Discover runtime Journal files using the existing strict layout.
  4. Ignore .journal.example files.
  5. Load runtime .journal files chronologically per artifact.
  6. Validate directory names, filenames, metadata coordinates and Journal versions.
  7. Preserve the existing parser and chain invariants.
  8. Complete the entire initial load before returning successfully.
  9. Make query state available only after complete successful initialization.

A failed start must:

  • Propagate the original meaningful failure.
  • Leave the service unavailable for queries.
  • Expose no partially initialized Journal worldview.
  • Prevent the Hub from adding nenjim.journal.service to its started-process snapshot.
  • Prevent the Hub from continuing to business-process startup.
  • Prevent a later start attempt on the same instance.

Query operations before successful startup or after failed startup must fail clearly with IllegalStateException.

This issue introduces no background watcher or polling thread.

Passive JournalServiceManager

Replace the current manager API with:

package com.r35157.nenjim.service.journal;

import org.jetbrains.annotations.NotNull;

import java.io.IOException;
import java.nio.file.Path;

public interface JournalServiceManager {
    @NotNull
    Path journalRoot();

    void refresh() throws IOException;
}

The manager:

  • Does not extend NenjimComponent.
  • Does not implement NenjimProcess.
  • Has no start() method.
  • Has no stop() method.
  • Has no journalService() accessor.
  • Is not registered in the Registry.
  • Is retained privately by the Hub.
  • Is bound to exactly one Journal service instance.
  • Exposes no Journal query operations.
  • Delegates refresh through a non-public administrative view.
  • Returns the normalized absolute configured root.
  • Rejects refresh before successful Journal startup.
  • Preserves explicit idempotent refresh after startup.

Removal of the old lifecycle operations is intentional. Do not add deprecated aliases, forwarding wrappers or compatibility methods.

A common stop operation will be designed later for NenjimProcess; this issue must not invent a Journal-specific replacement stop lifecycle.

Safe reference construction boundary

The Hub must initiate construction of:

  • One Journal service/process object configured with:

    data/nenjim/journals
    
  • One distinct passive Journal manager bound to that service.

The reference implementation may use a stateless implementation-only factory or assembly under:

com.r35157.nenjim.service.journal.impl.ref

The construction mechanism must provide the Hub with:

  • A JournalService view.
  • A NenjimProcess view of the same Journal object.
  • A distinct JournalServiceManager.

The construction helper:

  • Owns no lifecycle.
  • Is neither a component nor a process.
  • Is not registered.
  • Must not expose parser, ingestion, chain-state or filesystem administration to ordinary service consumers.
  • Must not allow a JournalService reference to derive the manager for the active service.

The service and process views must have identical object identity.

Any internal Journal administration interface must remain non-public and must explain in JavaDoc why it must not be exposed through the read-only service API.

Hub integration

Extend NenjimHubImpl startup with the Journal subsystem.

After Registry startup and self-registration, the Hub must:

  1. Initiate construction of the Journal service/process and its manager.

  2. Register the Journal service as:

    nenjim.journal.service
    
  3. Start it through its NenjimProcess view.

  4. Append its ID to the started-process snapshot only after successful startup.

  5. Continue constructing and starting the existing business graph only after Journal startup succeeds.

The resulting successful process order must be:

nenjim.registry.service
nenjim.journal.service
assetaz.ticker.default
evelyn.service.prod
evelyn.service.test
evelyn.iou-burner.prod
evelyn.mission-control.default

The manager must not receive a component ID and must not appear in the Registry or started-process list.

The current repository contains only .journal.example files beneath the configured root. These files must remain ignored, and startup with only those examples must succeed with an empty queryable Journal service.

Failed Journal startup

If the configured root or any runtime Journal entry is invalid:

  • JournalServiceImpl.start() fails clearly.
  • nenjim.journal.service is not added to the started-process snapshot.
  • Registry remains the only successfully started infrastructure process.
  • Ticker, Evelyn, Burner and Mission Control are not started.
  • The Hub does not announce itself online.
  • The Hub does not enter its blocking wait.
  • No invalid or partially loaded Journal state becomes queryable.
  • No directory or Journal file is created, changed or deleted.

Generic rollback and Registry shutdown remain outside this issue because NenjimProcess does not yet define stop.

Refresh behavior

The existing refresh guarantees must be preserved:

  • Refresh is explicit.
  • Unchanged files are idempotent.
  • Valid newly added successor revisions are discovered.
  • Runtime files are read in deterministic chronological order.
  • .journal.example remains ignored.
  • Missing, unreadable or malformed runtime data fails clearly.
  • Journal files are never created, overwritten or deleted.
  • There is no watcher, polling or remote synchronization.
  • The existing per-ingestion atomicity and chain validation remain unchanged.

Refresh must be available only through JournalServiceManager, not through JournalService or NenjimProcess.

Existing Journal behavior that must remain unchanged

Do not change:

  • Journal format version 1.
  • Artifact-coordinate rules.
  • JournalVersion.
  • SemanticVersion and VersionExpression.
  • Policy semantics.
  • Release metadata.
  • Dependency representation.
  • Exact-text SHA-256 chain identity.
  • Genesis and predecessor validation.
  • Immutable snapshots.
  • Query ordering.
  • Empty-result behavior.
  • Parser diagnostics.
  • Internal ingestion semantics.
  • Version-controlled example contents or placement, except documentation corrections required by this lifecycle refactor.

This is a lifecycle and runtime integration issue, not a Journal-domain redesign.

OpenSpec and documentation

Create a separate OpenSpec change for this issue.

The change must:

  • Modify the canonical nenjim-journal-management lifecycle requirements.
  • Extend the Hub runtime specification introduced by #77.
  • Remove canonical claims that JournalServiceManager constructs, starts, stops or exposes the service.
  • Specify JournalServiceImpl as the active process.
  • Specify Hub construction, registration, startup order and failure propagation.
  • Preserve every unaffected Journal-domain requirement.
  • Keep the change active after implementation.
  • Not synchronize or archive it until implementation review and acceptance.

Update all directly affected documentation, including:

  • docs/Nenjim.md
  • docs/Terminologi.md
  • Any directly affected public HTML documentation.
  • JavaDoc for JournalService, JournalServiceManager and their reference implementation boundary.

Documentation must present the common target model:

  • Service means read-only or domain-facing API.
  • Process means Hub-managed active lifecycle.
  • Manager means a passive administrative capability retained by the Hub.
  • One concrete object may be both a service implementation and a process.
  • The manager is a separate object and is not a component.

Out of scope

This issue does not implement:

  • Journal format changes.
  • New Journal fields or policies.
  • Contexts.
  • Dependency resolution.
  • Classloading.
  • Artifact downloading or publishing.
  • Remote Journal synchronization.
  • Filesystem watching or polling.
  • Generic process stop or restart.
  • Health monitoring.
  • NenjimHub.conf.
  • Configurable autorun.
  • Process Manager GUI integration.
  • Registry removal.
  • Business-service migrations.
  • NenjimTestTool.
  • New general unit tests.

Acceptance criteria

  • Issue #77 is implemented and accepted first.
  • JournalService extends NenjimComponent.
  • The Journal service object implements the canonical NenjimProcess.
  • The service and process views have identical object identity.
  • Initial filesystem loading occurs during Journal process startup.
  • Queries are unavailable before successful startup.
  • Failed startup exposes no partial Journal worldview.
  • JournalServiceManager exposes only journalRoot() and refresh().
  • The Journal manager is passive, unregistered and not a process.
  • Ordinary consumers cannot derive Journal administration from JournalService.
  • The Hub owns Journal construction, registration and startup.
  • Journal is registered as nenjim.journal.service.
  • Journal is the second successfully started process after Registry.
  • Invalid runtime Journals fail Hub startup before business processes start.
  • A repository containing only .journal.example files starts successfully with an empty Journal service.
  • Existing refresh behavior remains explicit and idempotent.
  • Existing Journal format, parser, model, chain and query semantics remain unchanged.
  • OpenSpec and documentation describe the new lifecycle ownership.
  • No Context, classloader, resolver, download or process-stop work is added.
  • No new general unit tests are added.
  • The repository builds successfully.
## Dependency This issue depends on completion and acceptance of issue #77, which introduces: * The global `NenjimHub` runtime owner. * The canonical `NenjimProcess`. * Hub-owned construction, registration and startup. * The ordered `getStartedProcessIds()` snapshot. * Passive, unregistered service-manager handles. Do not implement this issue on the pre-#77 architecture. ## Background Issue #75 implemented the Journal format, immutable domain model, strict parser, chain ingestion, read-only query service and filesystem lifecycle manager. The current lifecycle differs from the runtime architecture established by #77: * `JournalServiceManagerImpl` constructs `JournalServiceImpl`. * `JournalServiceManager.start()` performs initial loading. * The manager owns start and stop. * The manager exposes the service only after startup. * `JournalService` is not a Nenjim component or process. * The Journal subsystem is not constructed, registered or started by `NenjimHub`. The intended common model is now: * The Hub initiates construction of services and their passive managers. * The service implementation is the active `NenjimProcess`. * The Hub registers the service in the Registry. * The Hub starts the service and records its process ID. * The manager is only an administrative indirection for operations that ordinary consumers must not receive. This issue migrates the Journal subsystem to that model without changing the Journal format, model, parsing or query semantics. ## Goals This issue must: 1. Make `JournalService` a Nenjim component. 2. Make `JournalServiceImpl` the active Journal `NenjimProcess`. 3. Move initial filesystem loading into the Journal process startup. 4. Reduce `JournalServiceManager` to a passive administrative handle. 5. Make the Hub construct the Journal service and manager. 6. Register the Journal service as `nenjim.journal.service`. 7. Start Journal immediately after the Registry and before business processes. 8. Include Journal in the Hub’s started-process snapshot. 9. Preserve all Journal format, validation, chain and refresh behavior. 10. Fail Hub startup clearly if runtime Journal data is invalid. ## JournalService remains the read-only domain API Change the service declaration so it is registrable: ```java public interface JournalService extends NenjimComponent { // Existing query API unchanged. } ``` The existing query operations and their semantics must remain unchanged: * `artifactCoordinates()` * `revisions(...)` * `latest(...)` * `revision(...)` * `revisionAtOrBefore(...)` * `release(...)` The public service interface must still expose no: * Lifecycle operation. * Filesystem path. * Refresh operation. * Parser. * Ingestion method. * Mutable chain state. * Administrative capability. Ordinary consumers receive only `JournalService`. ## JournalServiceImpl becomes the process The reference service object must implement both: ```java JournalService NenjimProcess ``` The same object must therefore be indexed under both interfaces when registered. Its `start()` operation must: 1. Accept only the configured Journal data root supplied during construction. 2. Validate that the root exists, is a directory and is readable. 3. Discover runtime Journal files using the existing strict layout. 4. Ignore `.journal.example` files. 5. Load runtime `.journal` files chronologically per artifact. 6. Validate directory names, filenames, metadata coordinates and Journal versions. 7. Preserve the existing parser and chain invariants. 8. Complete the entire initial load before returning successfully. 9. Make query state available only after complete successful initialization. A failed start must: * Propagate the original meaningful failure. * Leave the service unavailable for queries. * Expose no partially initialized Journal worldview. * Prevent the Hub from adding `nenjim.journal.service` to its started-process snapshot. * Prevent the Hub from continuing to business-process startup. * Prevent a later start attempt on the same instance. Query operations before successful startup or after failed startup must fail clearly with `IllegalStateException`. This issue introduces no background watcher or polling thread. ## Passive JournalServiceManager Replace the current manager API with: ```java package com.r35157.nenjim.service.journal; import org.jetbrains.annotations.NotNull; import java.io.IOException; import java.nio.file.Path; public interface JournalServiceManager { @NotNull Path journalRoot(); void refresh() throws IOException; } ``` The manager: * Does not extend `NenjimComponent`. * Does not implement `NenjimProcess`. * Has no `start()` method. * Has no `stop()` method. * Has no `journalService()` accessor. * Is not registered in the Registry. * Is retained privately by the Hub. * Is bound to exactly one Journal service instance. * Exposes no Journal query operations. * Delegates refresh through a non-public administrative view. * Returns the normalized absolute configured root. * Rejects refresh before successful Journal startup. * Preserves explicit idempotent refresh after startup. Removal of the old lifecycle operations is intentional. Do not add deprecated aliases, forwarding wrappers or compatibility methods. A common stop operation will be designed later for `NenjimProcess`; this issue must not invent a Journal-specific replacement stop lifecycle. ## Safe reference construction boundary The Hub must initiate construction of: * One Journal service/process object configured with: ```text data/nenjim/journals ``` * One distinct passive Journal manager bound to that service. The reference implementation may use a stateless implementation-only factory or assembly under: ```text com.r35157.nenjim.service.journal.impl.ref ``` The construction mechanism must provide the Hub with: * A `JournalService` view. * A `NenjimProcess` view of the same Journal object. * A distinct `JournalServiceManager`. The construction helper: * Owns no lifecycle. * Is neither a component nor a process. * Is not registered. * Must not expose parser, ingestion, chain-state or filesystem administration to ordinary service consumers. * Must not allow a `JournalService` reference to derive the manager for the active service. The service and process views must have identical object identity. Any internal Journal administration interface must remain non-public and must explain in JavaDoc why it must not be exposed through the read-only service API. ## Hub integration Extend `NenjimHubImpl` startup with the Journal subsystem. After Registry startup and self-registration, the Hub must: 1. Initiate construction of the Journal service/process and its manager. 2. Register the Journal service as: ```text nenjim.journal.service ``` 3. Start it through its `NenjimProcess` view. 4. Append its ID to the started-process snapshot only after successful startup. 5. Continue constructing and starting the existing business graph only after Journal startup succeeds. The resulting successful process order must be: ```text nenjim.registry.service nenjim.journal.service assetaz.ticker.default evelyn.service.prod evelyn.service.test evelyn.iou-burner.prod evelyn.mission-control.default ``` The manager must not receive a component ID and must not appear in the Registry or started-process list. The current repository contains only `.journal.example` files beneath the configured root. These files must remain ignored, and startup with only those examples must succeed with an empty queryable Journal service. ## Failed Journal startup If the configured root or any runtime Journal entry is invalid: * `JournalServiceImpl.start()` fails clearly. * `nenjim.journal.service` is not added to the started-process snapshot. * Registry remains the only successfully started infrastructure process. * Ticker, Evelyn, Burner and Mission Control are not started. * The Hub does not announce itself online. * The Hub does not enter its blocking wait. * No invalid or partially loaded Journal state becomes queryable. * No directory or Journal file is created, changed or deleted. Generic rollback and Registry shutdown remain outside this issue because `NenjimProcess` does not yet define stop. ## Refresh behavior The existing refresh guarantees must be preserved: * Refresh is explicit. * Unchanged files are idempotent. * Valid newly added successor revisions are discovered. * Runtime files are read in deterministic chronological order. * `.journal.example` remains ignored. * Missing, unreadable or malformed runtime data fails clearly. * Journal files are never created, overwritten or deleted. * There is no watcher, polling or remote synchronization. * The existing per-ingestion atomicity and chain validation remain unchanged. Refresh must be available only through `JournalServiceManager`, not through `JournalService` or `NenjimProcess`. ## Existing Journal behavior that must remain unchanged Do not change: * Journal format version 1. * Artifact-coordinate rules. * `JournalVersion`. * `SemanticVersion` and `VersionExpression`. * Policy semantics. * Release metadata. * Dependency representation. * Exact-text SHA-256 chain identity. * Genesis and predecessor validation. * Immutable snapshots. * Query ordering. * Empty-result behavior. * Parser diagnostics. * Internal ingestion semantics. * Version-controlled example contents or placement, except documentation corrections required by this lifecycle refactor. This is a lifecycle and runtime integration issue, not a Journal-domain redesign. ## OpenSpec and documentation Create a separate OpenSpec change for this issue. The change must: * Modify the canonical `nenjim-journal-management` lifecycle requirements. * Extend the Hub runtime specification introduced by #77. * Remove canonical claims that `JournalServiceManager` constructs, starts, stops or exposes the service. * Specify `JournalServiceImpl` as the active process. * Specify Hub construction, registration, startup order and failure propagation. * Preserve every unaffected Journal-domain requirement. * Keep the change active after implementation. * Not synchronize or archive it until implementation review and acceptance. Update all directly affected documentation, including: * `docs/Nenjim.md` * `docs/Terminologi.md` * Any directly affected public HTML documentation. * JavaDoc for `JournalService`, `JournalServiceManager` and their reference implementation boundary. Documentation must present the common target model: * Service means read-only or domain-facing API. * Process means Hub-managed active lifecycle. * Manager means a passive administrative capability retained by the Hub. * One concrete object may be both a service implementation and a process. * The manager is a separate object and is not a component. ## Out of scope This issue does not implement: * Journal format changes. * New Journal fields or policies. * Contexts. * Dependency resolution. * Classloading. * Artifact downloading or publishing. * Remote Journal synchronization. * Filesystem watching or polling. * Generic process stop or restart. * Health monitoring. * `NenjimHub.conf`. * Configurable autorun. * Process Manager GUI integration. * Registry removal. * Business-service migrations. * NenjimTestTool. * New general unit tests. ## Acceptance criteria - [ ] Issue #77 is implemented and accepted first. - [ ] `JournalService` extends `NenjimComponent`. - [ ] The Journal service object implements the canonical `NenjimProcess`. - [ ] The service and process views have identical object identity. - [ ] Initial filesystem loading occurs during Journal process startup. - [ ] Queries are unavailable before successful startup. - [ ] Failed startup exposes no partial Journal worldview. - [ ] `JournalServiceManager` exposes only `journalRoot()` and `refresh()`. - [ ] The Journal manager is passive, unregistered and not a process. - [ ] Ordinary consumers cannot derive Journal administration from `JournalService`. - [ ] The Hub owns Journal construction, registration and startup. - [ ] Journal is registered as `nenjim.journal.service`. - [ ] Journal is the second successfully started process after Registry. - [ ] Invalid runtime Journals fail Hub startup before business processes start. - [ ] A repository containing only `.journal.example` files starts successfully with an empty Journal service. - [ ] Existing refresh behavior remains explicit and idempotent. - [ ] Existing Journal format, parser, model, chain and query semantics remain unchanged. - [ ] OpenSpec and documentation describe the new lifecycle ownership. - [ ] No Context, classloader, resolver, download or process-stop work is added. - [ ] No new general unit tests are added. - [ ] The repository builds successfully.
minimons added the enhancement label 2026-09-02 12:59:23 +02:00
minimons self-assigned this 2026-09-02 12:59:23 +02:00
minimons added this to the Nenjim project 2026-09-02 12:59:23 +02:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: r35157/com_r35157_nenjim-hubd-impl_ref#78