Establish the canonical Nenjim process and context-loading foundation #74

Open
opened 2026-08-16 12:14:48 +02:00 by minimons · 0 comments
Owner

Parent roadmap

This issue implements the first isolated vertical slice of #73.

It establishes the canonical process lifecycle and proves the essential context-classloader architecture without migrating any existing business service or changing the current NenjimHub startup behavior.

Problem

The repository currently contains multiple obsolete or incomplete NenjimProcess, context, and classloader implementations.

The active NenjimHubImpl still imports implementation classes directly, constructs the complete object graph through constructors, and starts a hardcoded set of services and applications.

Before existing services can be migrated safely, Nenjim needs one small but genuine process-loading path that proves:

  • Each context owns a separate classloader.
  • The same class name loaded in two contexts represents two different Java classes.
  • A process can be resolved from its interface name.
  • The process can be instantiated through a public default constructor.
  • The process receives a context-bound NenjimHub.
  • The process remains inactive until start() is invoked.
  • Direct Java class references made by a context-loaded implementation are resolved through the same context classloader.

This foundation must work without implementing different module versions, journals, artifact downloads, or production process migration.

Goals

Implement a minimal context runtime that:

  1. Defines the canonical NenjimProcess lifecycle.
  2. Defines an immutable Nenjim context identity.
  3. Creates one real NenjimClassLoader instance per context.
  4. Loads the same current implementation bytes independently in two contexts.
  5. Resolves one minimal process implementation through a process-interface name.
  6. Instantiates that process through its public default constructor.
  7. Supplies a distinct context-bound NenjimHub facade to each instance.
  8. Calls start() only through the canonical NenjimProcess contract.
  9. Provides a deterministic manual verification path for the complete lifecycle and classloader invariants.
  10. Leaves all current business services, applications, and hardcoded startup behavior unchanged.

Canonical NenjimProcess contract

Introduce one canonical public NenjimProcess interface under the modern com.r35157.nenjim namespace. The intended package is:

com.r35157.nenjim.process.NenjimProcess

The contract must expose only the lifecycle required by this issue:

public interface NenjimProcess {
    void setNenjimHub(NenjimHub nenjimHub);

    void start();
}

A Nenjim process implementation must:

  • Be a public concrete class.
  • Provide a public default constructor.
  • Perform no active business work during construction.
  • Accept a non-null context-bound NenjimHub before startup.
  • Reject invalid lifecycle use clearly.
  • Remain inactive until start() is called.

setNenjimHub(...) represents one-time lifecycle initialization rather than ordinary mutable property assignment. The reference process used by this issue must reject:

  • A null Hub.
  • A second Hub assignment.
  • Startup before Hub assignment.
  • Repeated startup of the same instance.

These strict reference-process checks establish the intended lifecycle without yet migrating services whose existing restart semantics may require separate design decisions.

The existing legacy NenjimProcess interfaces must not become the canonical API. They may remain temporarily for compatibility and later cleanup under #73.

Context identity

Reuse or improve the existing modern ContextId value type rather than introducing a second competing context identifier.

A context ID must be:

  • Non-null.
  • Non-empty.
  • Non-blank.
  • Immutable.
  • Suitable for diagnostics without depending on object identity.

Create two independent contexts for verification, conceptually named:

Production
Test

This issue does not introduce NenjimHub.conf; the two contexts may be constructed explicitly by the diagnostic bootstrap.

Context-specific classloading

Create one genuine NenjimClassLoader instance per context.

For this first implementation:

  • Both contexts load the same current code version.
  • The implementation source may be the current application JAR or compiled runtime classes.
  • Process-interface-to-implementation binding may be hardcoded.
  • No journal or artifact registry is consulted.
  • No remote or local artifact installation is performed.
  • No context chooses a different module version.

Despite using identical class bytes, the context-managed implementation class must be defined independently by each context classloader.

For the same fully qualified implementation class name:

productionClass.getName().equals(testClass.getName())

must be true, while:

productionClass == testClass

and:

productionClass.getClassLoader()
        == testClass.getClassLoader()

must both be false.

The implementation must avoid accidentally resolving the context-managed implementation through the parent or system classloader first.

Shared classloader boundary

The minimum Nenjim contracts needed across the runtime boundary must be loaded once by the shared parent classloader.

At minimum, the parent boundary must permit the context-loaded process class to be safely treated as the canonical parent-loaded NenjimProcess type.

The design must explicitly document:

  • Which contracts are parent-loaded.
  • Which diagnostic implementation classes are context-loaded.
  • How parent delegation is prevented from defeating context isolation.
  • Why Class#asSubclass(NenjimProcess.class) and the subsequent cast remain valid.

This issue does not need to solve the complete future package and artifact boundary for every Nenjim module.

Process resolution

The diagnostic runtime must request a process by its fully qualified process-interface name, not by an arbitrary global instance name.

Conceptually:

Class<? extends NenjimProcess> processClass =
        contextClassLoader.resolve(processInterfaceName);

For this issue, each context may contain one hardcoded binding from the diagnostic process interface to its implementation class.

The binding mechanism must remain internal to the context runtime. Do not introduce the global deployment-specific model:

registerProcess("some.production.instance", process);

Process identity remains:

context ID + process-interface name

Default-constructor instantiation

After resolution, instantiate the context-loaded process through normal Java reflection:

NenjimProcess process =
        processClass.getDeclaredConstructor().newInstance();

Resolution and instantiation must fail clearly when:

  • The configured implementation class cannot be loaded.
  • The class does not implement the canonical parent-loaded NenjimProcess.
  • The class is abstract or otherwise not instantiable.
  • It lacks an accessible public default constructor.
  • Construction fails.

Do not use deprecated Class#newInstance().

Context-bound NenjimHub facade

Each context classloader must supply its process with a context-bound object implementing the canonical NenjimHub API used at the classloader boundary.

The Production and Test processes must not receive the same facade instance.

The facade must retain the context internally so that later getComponent(...), getPlugins(...), and getProcess(...) operations can resolve within the correct context without requiring the process to pass its context ID.

Actual component, plugin, and process dependency lookup is outside this issue. Unsupported dependency-resolution operations must fail clearly rather than return null or silently use another context.

Do not use call-stack inspection or Thread.currentThread().getContextClassLoader() to guess the caller's context.

Minimal reference process

Add or adapt one minimal, non-graphical, non-networked reference process solely to exercise the new runtime foundation.

It must:

  • Implement a dedicated process interface extending the canonical NenjimProcess.
  • Use a public default constructor.
  • Perform no work during construction.
  • Store its context-bound Hub during setNenjimHub(...).
  • Begin its observable diagnostic work only in start().
  • Create at least one context-loaded helper through an ordinary Java expression such as:
new ContextLoadedHelper();
  • Verify that the helper and process implementation were defined by the same context classloader.
  • Perform no file writes, network access, wallet access, GUI startup, worker-thread creation, or other business activity.

The reference process must not be added to the active NenjimHub autorun path.

Its package, purpose, and eventual removal or reuse must be documented clearly so that it is not mistaken for a production service.

Do not migrate Ticker, Burner, Evelyn, Mission Control, Jupiter Perps, alarms, NenjimTestTool, or another existing business process merely to provide this proof.

Manual verification

Provide a deterministic, explicitly invoked diagnostic entry point or equivalent manual smoke-verification path.

It must:

  1. Create Production and Test contexts.
  2. Create one NenjimClassLoader for each.
  3. Resolve the same process interface independently in both contexts.
  4. Verify different implementation Class identities and defining classloaders.
  5. Instantiate both process objects through their public default constructors.
  6. Verify that the two process instances receive different context-bound Hub facades.
  7. Call setNenjimHub(...) before start().
  8. Start both reference processes.
  9. Verify that each process's directly constructed helper uses its own defining context classloader.
  10. Exit successfully after verification instead of leaving background resources running.

A failed invariant must terminate the diagnostic with a clear exception and non-zero process exit.

The diagnostic must not run automatically during ordinary NenjimHub startup.

Do not add broad unit tests, migrate the general test setup, or begin implementing NenjimTestTool in this issue.

Existing runtime compatibility

This issue must not change the observable startup behavior of the active NenjimHubImpl.

In particular:

  • Existing components, plugins, services, and applications remain constructed as they are now.
  • The current hardcoded service and application startup remains unchanged.
  • No current business implementation is loaded through the new context runtime.
  • No existing process is removed from or added to autorun.
  • No GUI behavior changes.
  • No wallet, transaction, alarm, Ticker, Burner, or Evelyn behavior changes.
  • The normal Main entry point must not execute the diagnostic process.

The new runtime foundation exists alongside the legacy path until later roadmap issues migrate individual processes atomically.

Documentation

Document:

  • The new canonical process lifecycle.
  • The parent/context classloader boundary.
  • The fact that all contexts currently load the same code version.
  • How to invoke the manual diagnostic.
  • The expected successful diagnostic output or invariants.
  • Which legacy process and classloader implementations remain intentionally untouched.
  • Which behavior is deliberately deferred to later issues under #73.

Do not rewrite the complete long-term Nenjim documentation in this issue.

OpenSpec

Create one OpenSpec change for issue #74.

Suggested change ID:

74-establish-nenjim-process-context-loading-foundation

The change must introduce only the specifications required by this vertical slice.

It must not describe future functionality as already implemented, including:

  • Multiple module versions.
  • Journal-driven resolution.
  • General component or plugin lookup.
  • NenjimHub.conf.
  • Business-process migration.
  • Runtime stop, restart, upgrade, or unloading.
  • NenjimTestTool-based test execution.

Keep the change active after implementation. Do not synchronize or archive it until the implementation has been reviewed and accepted.

Non-goals

This issue does not implement:

  • Different module versions between contexts.
  • Artifact download, installation, or caching.
  • Journal-based dependency or version selection.
  • General component resolution.
  • General plugin discovery.
  • General process dependency lookup.
  • NenjimHub.conf.
  • Configurable autorun.
  • Runtime process stop or restart.
  • Hot reload.
  • Context unloading.
  • Classloader garbage-collection guarantees.
  • Migration of any current business service or application.
  • Removal of legacy Nenjim code.
  • General automated unit tests.
  • NenjimTestTool functionality.
  • Production deployment changes.

Acceptance criteria

  • A single canonical NenjimProcess API exists under the modern Nenjim namespace.
  • The canonical lifecycle contains Hub assignment followed by startup.
  • The reference implementation uses a public default constructor.
  • Production and Test use distinct NenjimClassLoader instances.
  • Both contexts load the same current implementation name from the same current code version.
  • The resulting implementation Class objects have equal names but different identities and defining classloaders.
  • Both classes remain assignable to the shared canonical NenjimProcess type.
  • Each process receives a distinct context-bound NenjimHub facade.
  • Each process starts only after Hub assignment.
  • A direct new inside each process resolves its helper through the correct context classloader.
  • Invalid lifecycle and loading conditions fail clearly.
  • A deterministic manual diagnostic proves the complete vertical slice and exits cleanly.
  • The diagnostic is never invoked by normal NenjimHub startup.
  • Existing business construction and startup behavior remain unchanged.
  • No business service or application is migrated.
  • No general unit tests or NenjimTestTool work is added.
  • The repository builds successfully.
  • OpenSpec artifacts describe only the functionality delivered by this issue.
## Parent roadmap This issue implements the first isolated vertical slice of #73. It establishes the canonical process lifecycle and proves the essential context-classloader architecture without migrating any existing business service or changing the current NenjimHub startup behavior. ## Problem The repository currently contains multiple obsolete or incomplete `NenjimProcess`, context, and classloader implementations. The active `NenjimHubImpl` still imports implementation classes directly, constructs the complete object graph through constructors, and starts a hardcoded set of services and applications. Before existing services can be migrated safely, Nenjim needs one small but genuine process-loading path that proves: * Each context owns a separate classloader. * The same class name loaded in two contexts represents two different Java classes. * A process can be resolved from its interface name. * The process can be instantiated through a public default constructor. * The process receives a context-bound `NenjimHub`. * The process remains inactive until `start()` is invoked. * Direct Java class references made by a context-loaded implementation are resolved through the same context classloader. This foundation must work without implementing different module versions, journals, artifact downloads, or production process migration. ## Goals Implement a minimal context runtime that: 1. Defines the canonical `NenjimProcess` lifecycle. 2. Defines an immutable Nenjim context identity. 3. Creates one real `NenjimClassLoader` instance per context. 4. Loads the same current implementation bytes independently in two contexts. 5. Resolves one minimal process implementation through a process-interface name. 6. Instantiates that process through its public default constructor. 7. Supplies a distinct context-bound `NenjimHub` facade to each instance. 8. Calls `start()` only through the canonical `NenjimProcess` contract. 9. Provides a deterministic manual verification path for the complete lifecycle and classloader invariants. 10. Leaves all current business services, applications, and hardcoded startup behavior unchanged. ## Canonical NenjimProcess contract Introduce one canonical public `NenjimProcess` interface under the modern `com.r35157.nenjim` namespace. The intended package is: ```java com.r35157.nenjim.process.NenjimProcess ``` The contract must expose only the lifecycle required by this issue: ```java public interface NenjimProcess { void setNenjimHub(NenjimHub nenjimHub); void start(); } ``` A Nenjim process implementation must: * Be a public concrete class. * Provide a public default constructor. * Perform no active business work during construction. * Accept a non-null context-bound `NenjimHub` before startup. * Reject invalid lifecycle use clearly. * Remain inactive until `start()` is called. `setNenjimHub(...)` represents one-time lifecycle initialization rather than ordinary mutable property assignment. The reference process used by this issue must reject: * A null Hub. * A second Hub assignment. * Startup before Hub assignment. * Repeated startup of the same instance. These strict reference-process checks establish the intended lifecycle without yet migrating services whose existing restart semantics may require separate design decisions. The existing legacy `NenjimProcess` interfaces must not become the canonical API. They may remain temporarily for compatibility and later cleanup under #73. ## Context identity Reuse or improve the existing modern `ContextId` value type rather than introducing a second competing context identifier. A context ID must be: * Non-null. * Non-empty. * Non-blank. * Immutable. * Suitable for diagnostics without depending on object identity. Create two independent contexts for verification, conceptually named: ```text Production Test ``` This issue does not introduce `NenjimHub.conf`; the two contexts may be constructed explicitly by the diagnostic bootstrap. ## Context-specific classloading Create one genuine `NenjimClassLoader` instance per context. For this first implementation: * Both contexts load the same current code version. * The implementation source may be the current application JAR or compiled runtime classes. * Process-interface-to-implementation binding may be hardcoded. * No journal or artifact registry is consulted. * No remote or local artifact installation is performed. * No context chooses a different module version. Despite using identical class bytes, the context-managed implementation class must be defined independently by each context classloader. For the same fully qualified implementation class name: ```java productionClass.getName().equals(testClass.getName()) ``` must be `true`, while: ```java productionClass == testClass ``` and: ```java productionClass.getClassLoader() == testClass.getClassLoader() ``` must both be `false`. The implementation must avoid accidentally resolving the context-managed implementation through the parent or system classloader first. ## Shared classloader boundary The minimum Nenjim contracts needed across the runtime boundary must be loaded once by the shared parent classloader. At minimum, the parent boundary must permit the context-loaded process class to be safely treated as the canonical parent-loaded `NenjimProcess` type. The design must explicitly document: * Which contracts are parent-loaded. * Which diagnostic implementation classes are context-loaded. * How parent delegation is prevented from defeating context isolation. * Why `Class#asSubclass(NenjimProcess.class)` and the subsequent cast remain valid. This issue does not need to solve the complete future package and artifact boundary for every Nenjim module. ## Process resolution The diagnostic runtime must request a process by its fully qualified process-interface name, not by an arbitrary global instance name. Conceptually: ```java Class<? extends NenjimProcess> processClass = contextClassLoader.resolve(processInterfaceName); ``` For this issue, each context may contain one hardcoded binding from the diagnostic process interface to its implementation class. The binding mechanism must remain internal to the context runtime. Do not introduce the global deployment-specific model: ```java registerProcess("some.production.instance", process); ``` Process identity remains: ```text context ID + process-interface name ``` ## Default-constructor instantiation After resolution, instantiate the context-loaded process through normal Java reflection: ```java NenjimProcess process = processClass.getDeclaredConstructor().newInstance(); ``` Resolution and instantiation must fail clearly when: * The configured implementation class cannot be loaded. * The class does not implement the canonical parent-loaded `NenjimProcess`. * The class is abstract or otherwise not instantiable. * It lacks an accessible public default constructor. * Construction fails. Do not use deprecated `Class#newInstance()`. ## Context-bound NenjimHub facade Each context classloader must supply its process with a context-bound object implementing the canonical `NenjimHub` API used at the classloader boundary. The Production and Test processes must not receive the same facade instance. The facade must retain the context internally so that later `getComponent(...)`, `getPlugins(...)`, and `getProcess(...)` operations can resolve within the correct context without requiring the process to pass its context ID. Actual component, plugin, and process dependency lookup is outside this issue. Unsupported dependency-resolution operations must fail clearly rather than return `null` or silently use another context. Do not use call-stack inspection or `Thread.currentThread().getContextClassLoader()` to guess the caller's context. ## Minimal reference process Add or adapt one minimal, non-graphical, non-networked reference process solely to exercise the new runtime foundation. It must: * Implement a dedicated process interface extending the canonical `NenjimProcess`. * Use a public default constructor. * Perform no work during construction. * Store its context-bound Hub during `setNenjimHub(...)`. * Begin its observable diagnostic work only in `start()`. * Create at least one context-loaded helper through an ordinary Java expression such as: ```java new ContextLoadedHelper(); ``` * Verify that the helper and process implementation were defined by the same context classloader. * Perform no file writes, network access, wallet access, GUI startup, worker-thread creation, or other business activity. The reference process must not be added to the active NenjimHub autorun path. Its package, purpose, and eventual removal or reuse must be documented clearly so that it is not mistaken for a production service. Do not migrate Ticker, Burner, Evelyn, Mission Control, Jupiter Perps, alarms, NenjimTestTool, or another existing business process merely to provide this proof. ## Manual verification Provide a deterministic, explicitly invoked diagnostic entry point or equivalent manual smoke-verification path. It must: 1. Create Production and Test contexts. 2. Create one `NenjimClassLoader` for each. 3. Resolve the same process interface independently in both contexts. 4. Verify different implementation `Class` identities and defining classloaders. 5. Instantiate both process objects through their public default constructors. 6. Verify that the two process instances receive different context-bound Hub facades. 7. Call `setNenjimHub(...)` before `start()`. 8. Start both reference processes. 9. Verify that each process's directly constructed helper uses its own defining context classloader. 10. Exit successfully after verification instead of leaving background resources running. A failed invariant must terminate the diagnostic with a clear exception and non-zero process exit. The diagnostic must not run automatically during ordinary NenjimHub startup. Do not add broad unit tests, migrate the general test setup, or begin implementing NenjimTestTool in this issue. ## Existing runtime compatibility This issue must not change the observable startup behavior of the active `NenjimHubImpl`. In particular: * Existing components, plugins, services, and applications remain constructed as they are now. * The current hardcoded service and application startup remains unchanged. * No current business implementation is loaded through the new context runtime. * No existing process is removed from or added to autorun. * No GUI behavior changes. * No wallet, transaction, alarm, Ticker, Burner, or Evelyn behavior changes. * The normal `Main` entry point must not execute the diagnostic process. The new runtime foundation exists alongside the legacy path until later roadmap issues migrate individual processes atomically. ## Documentation Document: * The new canonical process lifecycle. * The parent/context classloader boundary. * The fact that all contexts currently load the same code version. * How to invoke the manual diagnostic. * The expected successful diagnostic output or invariants. * Which legacy process and classloader implementations remain intentionally untouched. * Which behavior is deliberately deferred to later issues under #73. Do not rewrite the complete long-term Nenjim documentation in this issue. ## OpenSpec Create one OpenSpec change for issue #74. Suggested change ID: ```text 74-establish-nenjim-process-context-loading-foundation ``` The change must introduce only the specifications required by this vertical slice. It must not describe future functionality as already implemented, including: * Multiple module versions. * Journal-driven resolution. * General component or plugin lookup. * `NenjimHub.conf`. * Business-process migration. * Runtime stop, restart, upgrade, or unloading. * NenjimTestTool-based test execution. Keep the change active after implementation. Do not synchronize or archive it until the implementation has been reviewed and accepted. ## Non-goals This issue does not implement: * Different module versions between contexts. * Artifact download, installation, or caching. * Journal-based dependency or version selection. * General component resolution. * General plugin discovery. * General process dependency lookup. * `NenjimHub.conf`. * Configurable autorun. * Runtime process stop or restart. * Hot reload. * Context unloading. * Classloader garbage-collection guarantees. * Migration of any current business service or application. * Removal of legacy Nenjim code. * General automated unit tests. * NenjimTestTool functionality. * Production deployment changes. ## Acceptance criteria * [ ] A single canonical `NenjimProcess` API exists under the modern Nenjim namespace. * [ ] The canonical lifecycle contains Hub assignment followed by startup. * [ ] The reference implementation uses a public default constructor. * [ ] Production and Test use distinct `NenjimClassLoader` instances. * [ ] Both contexts load the same current implementation name from the same current code version. * [ ] The resulting implementation `Class` objects have equal names but different identities and defining classloaders. * [ ] Both classes remain assignable to the shared canonical `NenjimProcess` type. * [ ] Each process receives a distinct context-bound `NenjimHub` facade. * [ ] Each process starts only after Hub assignment. * [ ] A direct `new` inside each process resolves its helper through the correct context classloader. * [ ] Invalid lifecycle and loading conditions fail clearly. * [ ] A deterministic manual diagnostic proves the complete vertical slice and exits cleanly. * [ ] The diagnostic is never invoked by normal NenjimHub startup. * [ ] Existing business construction and startup behavior remain unchanged. * [ ] No business service or application is migrated. * [ ] No general unit tests or NenjimTestTool work is added. * [ ] The repository builds successfully. * [ ] OpenSpec artifacts describe only the functionality delivered by this issue.
minimons added the enhancement label 2026-08-16 12:14:48 +02:00
minimons self-assigned this 2026-08-16 12:14:48 +02:00
minimons added this to the AssetAZ project 2026-08-16 12:14:48 +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#74