13 KiB
ADDED Requirements
Requirement: Components and processes have minimal orthogonal contracts
NenjimComponent SHALL remain a pure marker with no component-ID or lifecycle property. The one canonical com.r35157.nenjim.process.NenjimProcess SHALL extend NenjimComponent and SHALL expose only start() throws Exception as its common lifecycle operation. Service and application SHALL remain independent domain or orchestration roles: an implementation in either role SHALL additionally implement NenjimProcess only when it has a Hub-managed active startup lifecycle. Passive components SHALL not implement NenjimProcess, and no NenjimApplication compatibility type SHALL remain.
Scenario: One object has more than one registration identity
- WHEN the same component object is registered under two different unused component IDs
- THEN both IDs identify the same object because IDs are Registry metadata rather than object properties
Scenario: A service implementation is startable
- WHEN a service implementation has a no-argument active startup lifecycle managed by the Hub
- THEN the implementation is discoverable as
NenjimProcesswithout adding lifecycle operations solely to its domain service interface
Scenario: Application role and process lifecycle are independent
- WHEN a component performs a user-facing or orchestration application role
- THEN that role alone neither makes it a process nor changes its domain interfaces
Scenario: Common stop is unavailable
- WHEN a caller uses
NenjimProcess - THEN the contract exposes no stop, restart, health, context, or dependency-lookup operation
Requirement: Registry construction preserves the administrative boundary
The reference Registry construction boundary SHALL create one Registry object exposed through both NenjimRegistryService and NenjimProcess, plus one distinct NenjimRegistryServiceManager bound to that Registry. The service and process views SHALL preserve object identity. The construction helper SHALL own no lifecycle, SHALL be neither a component nor a process, SHALL not be Registry-discoverable, and SHALL expose neither the package-private administration interface nor a way to derive the active Registry's manager from an ordinary service reference.
Scenario: Registry views preserve identity
- WHEN the Hub obtains a newly constructed Registry assembly
- THEN its service and process views reference the identical Registry object while its manager is a different object
Scenario: Ordinary service reference remains read-only
- WHEN an ordinary consumer receives the public Registry service view
- THEN the consumer cannot name or derive the package-private administration view or obtain the manager bound to that active Registry
Requirement: Registry manager is a passive registration handle
NenjimRegistryServiceManager SHALL expose exactly registration by component ID and component. It SHALL be neither a Nenjim component nor a Nenjim process, SHALL remain privately retained by the Hub, and SHALL delegate registration to the Registry's internal administration view while preserving duplicate-ID, interface-indexing, and post-start registration behavior. It SHALL not construct the business graph, own lifecycle, start or stop any process, expose the Registry service, or be registered under nenjim.registry.service-manager or any other component ID.
Scenario: Hub registers a component
- WHEN the Hub passes an unused component ID and a non-null component to the manager
- THEN the component becomes available through the Registry's existing typed interface index without being activated
Scenario: Registry manager is not discoverable
- WHEN Registry registrations are inspected after Hub composition
- THEN no component ID identifies the manager and no lookup returns it as a component or process
Requirement: NenjimHub owns composition and runtime startup
NenjimHub SHALL be the global runtime owner and SHALL expose start() throws Exception plus an immutable ordered snapshot of successfully started process IDs. It SHALL be neither a component nor a process and SHALL not register itself. Its only accepted startup attempt SHALL construct the Registry subsystem, start the Registry process first, register it as nenjim.registry.service, construct and register the complete hardcoded component set from issue #76 in dependency order, resolve only the explicitly enabled IDs through NenjimProcess, start them in dependency-safe order, announce the existing online point, and preserve the existing blocking wait. Registration SHALL never imply startup.
Scenario: Registry starts before graph construction
- WHEN the Hub performs its first startup attempt
- THEN it starts the Registry, registers it as
nenjim.registry.service, and records that ID before constructing components whose constructors may query the Registry
Scenario: Registry is process-discoverable
- WHEN the Registry registration has completed
- THEN the same object is retrievable as both
NenjimRegistryServiceandNenjimProcess
Scenario: Required hardcoded bindings are registered
- WHEN Hub composition completes
- THEN all component IDs and implementation choices enumerated by issue #76 are present exactly as configured, including intentionally inactive components and excluding the former Registry-manager ID
Scenario: Explicit startup subset preserves order
- WHEN the Hub starts processes after the Registry
- THEN it starts
assetaz.ticker.default,evelyn.service.prod,evelyn.service.test,evelyn.iou-burner.prod, andevelyn.mission-control.defaultin that order
Scenario: Deliberately inactive processes remain inactive
- WHEN Hub startup reaches its online point
- THEN
jupiter-perps-alarm.defaultand the dormant Composer, Process Manager, Test Tool, Soda Task Manager, and Suwimo Client paths have not been started
Scenario: Hub start is not retried
- WHEN any first Hub startup attempt has begun and
start()is requested again - THEN the later request fails with
IllegalStateExceptionwhether the first attempt succeeded or failed
Scenario: Startup failure is fail-fast
- WHEN a process startup throws
- THEN the exception propagates, no later process starts, the Hub neither announces online nor enters its final wait, and no generic rollback or retry is attempted
Requirement: Started-process snapshots are ordered immutable and truthful
NenjimHub.getStartedProcessIds() SHALL be safe before, during, and after startup and SHALL return an immutable point-in-time snapshot in successful startup order. A process ID SHALL be appended only after that process's start() call returns successfully. Earlier successful entries SHALL remain visible after a later failure, while a failing process SHALL never be included. The snapshot SHALL make no health or continued-liveness guarantee.
Scenario: Snapshot before process startup
- WHEN the snapshot is requested before any process has started successfully
- THEN it is an empty immutable list
Scenario: Successful process is recorded
- WHEN a process's
start()call returns successfully - THEN its ID appears after all previously successful process IDs in every later snapshot
Scenario: Failed process is not recorded
- WHEN a process's
start()call throws - THEN its ID is absent while all earlier successful process IDs remain visible
Scenario: Snapshot is a point-in-time value
- WHEN the Hub starts another process after a caller obtained a snapshot
- THEN the earlier snapshot is unchanged and cannot be mutated by its caller
MODIFIED Requirements
Requirement: Registry startup is single-use and gates queries
The Registry process's first start() call SHALL be the only accepted attempt and SHALL enable queries only after successful startup. A later attempt SHALL fail with IllegalStateException, including when the first attempt failed. Registration SHALL remain allowed before and after startup, and starting the Registry SHALL not construct, register, start, or stop another component.
Scenario: Query before startup
- WHEN either lookup operation is called before successful Registry startup
- THEN it fails with
IllegalStateException
Scenario: Registry starts once
- WHEN the Registry is started successfully and start is requested again
- THEN the second request fails with
IllegalStateException
Scenario: Failed start is not retried
- WHEN the first Registry start attempt fails
- THEN every later start request fails with
IllegalStateException
Requirement: Main is a thin outer bootstrap
com.r35157.nenjim.hubd.Main SHALL be a final non-component class. Its main(String[]) SHALL construct com.r35157.nenjim.hubd.impl.ref.NenjimHubImpl through a variable typed as NenjimHub and SHALL call start() exactly once. The Gradle application entry point SHALL continue to name this class. The bootstrap SHALL not construct the Registry, manager, or business graph.
Scenario: Start through the outer bootstrap
- WHEN the configured Java entry point is invoked
- THEN
Main.main(...)constructs one reference Hub through the public Hub interface and delegates startup to it exactly once
Scenario: Bootstrap has no composition responsibility
- WHEN the outer bootstrap source is inspected
- THEN composition, process tracking, online announcement, and the blocking wait are absent from
Mainand owned by the Hub implementation
Requirement: Registry terminology and package boundaries are consistent
Public component contracts SHALL live in com.r35157.nenjim.component; the canonical process contract SHALL live in com.r35157.nenjim.process; the global Hub contract SHALL live in com.r35157.nenjim.hubd; public Registry service contracts SHALL live in com.r35157.nenjim.service.registry; and the component ID SHALL live in its .valuetypes package. Registry storage and its package-private administration view SHALL live in .impl.ref, where an implementation-only construction boundary MAY also live without exposing administration. Directly affected Ticker price-source API and implementation packages SHALL continue to use .pricesource. Public reference-type parameters, return values, and record components added or changed by this capability SHALL declare explicit nullability.
Scenario: Ticker source packages are inspected
- WHEN source packages and imports are searched after migration
- THEN no directly affected Ticker price-source package uses the
pluginssegment
Scenario: Internal Registry administration is inspected
- WHEN an ordinary consumer compiles against the public Registry packages
- THEN the package-private administration interface and Registry implementation are inaccessible, and the public service exposes no path to its active manager
Scenario: Canonical process API is inspected
- WHEN modern source packages are searched after migration
- THEN exactly one canonical
com.r35157.nenjim.process.NenjimProcesscontract exists and noNenjimApplicationtype remains
Requirement: Deferred runtime features remain absent
The Registry SHALL represent one catalogue of already constructed components and SHALL not implement Contexts, artifact-version identity, resolution, classloading, automatic discovery, contributor APIs, public runtime loading or registration, removal, persistence, events, subscribers, user configuration, automatic selection, automatic start-all, ownership tracking, usage counts, permissions, scopes, filtered views, or a common stop lifecycle. The Hub SHALL not add configurable autorun, process-manager integration, stop, restart, upgrade, health monitoring, recovery, or context-bound dependency access.
Scenario: Consumer inspects the runtime APIs
- WHEN a consumer examines the public Registry, Registry-manager, process, and Hub contracts
- THEN only read-only Registry lookup, passive registration, minimal process startup, and Hub startup/snapshot operations are available and no deferred feature is exposed
REMOVED Requirements
Requirement: Components and applications have minimal common contracts
Reason: NenjimApplication incorrectly combines application role with active lifecycle classification and is replaced by the canonical NenjimProcess contract.
Migration: Modern startable implementations and typed Registry lookups use com.r35157.nenjim.process.NenjimProcess; service and application remain descriptive roles rather than common lifecycle interfaces.
Requirement: Registry manager owns hardcoded composition and startup
Reason: Composition, startup policy, started-process state, the online point, and the blocking runtime lifecycle belong to one global NenjimHub, not to the Registry subsystem.
Migration: NenjimHubImpl owns the existing graph and startup sequence; NenjimRegistryServiceManager becomes an unregistered passive registration handle.