## Context See `proposal.md` for motivation and the three delta specs for observable behavior. The current `NenjimHubImpl` constructor eagerly creates one hardcoded object graph, while `start()` activates five objects in a specific dependency-safe order and then waits forever on a latch. Component interfaces are unrelated Java interfaces, startable implementations do not share a lifecycle marker, and the Ticker still receives source instances through a temporary varargs constructor. Human-written Java remains in `.tjava`; Detag runs through the normal Gradle build. The implementation must retain the current concrete production/test bindings and active subset, including financially relevant wallet/service choices and the deliberately inactive alarm. The Registry is only a catalogue of constructed objects. It must remain injectable and mutable after startup, but it must not grow into an authorization, Context, discovery, resolution, persistence, loading, removal, or lifecycle-ownership system. ## Goals / Non-Goals **Goals:** - Establish small extractable public component and Registry API packages with reference-only administration and storage. - Guarantee deterministic typed lookup, immutable snapshots, alias registrations, recursive interface indexing, and internally consistent concurrent registration/read behavior. - Make the Registry manager the only current composition root while preserving hardcoded choices, activation order, and blocking behavior. - Make component selection explicit where it belongs to an application/service, beginning with the Ticker's configured Raydium source ID. - Remove the duplicate Hub composition surface and directly affected plugin terminology without migrating unrelated prototypes. **Non-Goals:** - No general dependency-injection framework, reflection-based construction, start-all loop, lifecycle graph, rollback/stop orchestration, or manager getter for the Registry. - No redesign of existing domain lifecycle APIs or constructor wiring where the application does not own a selectable component choice. - No migration of legacy `crypto.r35157` prototypes, Context/runtime roadmap contracts, or Journal service startup. - No permanent automated tests or production-main validation. ## Decisions ### Synchronized ordered storage provides the first Registry's consistency model `NenjimRegistryServiceImpl` uses a `LinkedHashMap` as the primary catalogue and a map from component-interface token to ordered mutable ID lists as the secondary index. Registration and both queries synchronize on the Registry object. `getComponentIds(...)` returns `List.copyOf(...)`, so callers receive immutable point-in-time snapshots and later registration cannot mutate an earlier result. This coarse lock is preferred to concurrent maps plus independently updated lists because registration must update both logical structures atomically and the hardcoded catalogue is small. Copy-on-write structures were considered, but would add more coordination without improving this startup-heavy workload. ### Interface discovery walks class and interface inheritance Before mutating storage, registration walks the implementation class and its superclasses. For every declared interface, it recursively walks parent interfaces and collects each interface assignable to `NenjimComponent` in insertion-preserving set order. The marker itself is therefore indexed, inherited component interfaces are included, and duplicate inheritance paths add an ID only once. Concrete classes never become keys. `NenjimRegistryServiceAdmin` is naturally excluded because it intentionally does not extend the marker. Reflection-time discovery is performed once per registration, not during each query. Scanning the primary map on lookup was rejected because issue #76 explicitly requires a secondary type index. ### Validation precedes lifecycle gating and mutation Lookup arguments are null/type validated before checking started state, giving explicit API-contract failures for malformed calls. Valid queries then require successful Registry startup. Registration validates and computes all index keys before adding the primary record or any index entry; duplicate detection therefore leaves both structures unchanged. The Registry and manager each retain a separate `startAttempted` flag set at the beginning of the synchronized first call. The Registry publishes `started` only after its start body succeeds. The manager has no restart recovery: any construction or activation failure leaves the attempt consumed, as required. This is separate from existing component-specific restart behavior. ### The manager composes through explicit IDs without exposing administration The manager creates one Registry implementation and retains public service, package-private admin, and application views of that same object. It starts the application view, then registers the Registry once and the manager once before registering the remaining components in the issue's exact dependency order. Explicit `NenjimComponentId` constants name every binding. Existing constructor dependencies remain ordinary interface-typed wiring owned by the composition root unless an application/service owns a configurable choice. The Ticker is the first such consumer: its public constructor receives only `NenjimRegistryService` plus explicit price-source IDs and resolves each ID as `PriceSource`. This preserves the selected Raydium source while leaving the registered hardcoded source inactive. Passing the admin view, selecting the first interface match, or adding automatic discovery was rejected because each would violate the ownership boundary. ### Application activation is an explicit ordered ID list After all registrations, the manager resolves exactly the five enabled IDs through `NenjimApplication` and starts them in the existing order. This both exercises multi-interface indexing and prevents accidental activation of the Registry, manager, alarm, or dormant applications. Registration itself performs no lifecycle work. A generic query-and-start-all loop was rejected because not every registered application is enabled. ### The bootstrap is a class, not a service surface The final `com.r35157.nenjim.hubd.Main` class is only the outer Java entry point. It constructs a public Registry manager through the `NenjimApplication` view and delegates once; it is neither a `NenjimComponent` nor a `NenjimApplication`. The former composition entry point `com.r35157.nenjim.hubd.impl.ref.Main` and its `NenjimHubImpl` composition implementation remain removed, so the new class does not reinstate that architecture or create a second composition path. Gradle points directly to `com.r35157.nenjim.hubd.Main`. ### Package and terminology migration remains narrow Only `com.r35157.assetaz.services.ticker.plugins.pricesource` and its implementation subpackages move to `.ticker.pricesource`. The public `PriceSource` becomes a component query interface; `PriceSink` remains an incidental callback interface. Other legacy packages or types containing plugin terminology are untouched. ## Risks / Trade-offs - [The manager still hardcodes operational component choices] → This change deliberately centralizes rather than generalizes composition; configuration, Context, discovery, persistence, and runtime loading remain separate designs. - [One Registry lock serializes all reads with rare registrations] → The catalogue is small and read operations are short; the simple lock guarantees consistency and can be revisited only if profiling warrants it. - [A manager failure can leave already-started components active] → The required lifecycle has no common stop and explicitly forbids retry; preserve fail-fast behavior and do not invent rollback semantics. - [Adding `NenjimComponent` to cross-module interfaces is source-visible] → Limit the marker to the exact registered query interfaces and do not mark records, value objects, callback sinks, or incidental helpers. - [Moving composition code could accidentally expose embedded operational values] → Preserve values without including them in Registry diagnostics or documentation, and inspect diff output with sensitive values redacted. - [The public Ticker constructor changes incompatibly] → The old constructor was explicitly documented as temporary; replace its one production call and retain the package-private data-root seam used by existing verification. ## Migration Plan 1. Add component/Registry contracts and the synchronized package-private Registry implementation. 2. Mark the exact registered query interfaces and active implementations, then move Ticker price-source packages and replace Ticker source injection with Registry-plus-ID selection. 3. Move the hardcoded construction graph into the Registry manager, register every required ID, activate only the explicit subset, and retain the online wait. 4. Replace the old Hub API/implementation/main with the thin bootstrap and update Gradle and directly affected documentation. 5. Validate OpenSpec, compile through Detag/Java, run temporary package-scoped Registry probes, inspect package references and the redacted complete diff, and audit every issue criterion. Rollback before publication is normal source-control reversal of the uncommitted change. The implementation does not migrate persistent state or mutate external Registry state.