diff --git a/README.md b/README.md index 109a174..27bf17d 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,20 @@ Nenjim Hub reads only `conf/NenjimHub.conf`; copy `conf/NenjimHub.conf.example` before first use and edit the ordered process list for the deployment role. The example is never loaded as runtime input. +The Evelyn status processes use `evelyn.status.prod` and `evelyn.status.test`. +When upgrading an existing deployment, replace `evelyn.service.prod` and +`evelyn.service.test` with those IDs in private `conf/NenjimHub.conf`, preserving +their order and enabled/commented selections. The instance names `Production` +and `Test`, their `data/evelyn//status.log` files, and history +format version 1 remain unchanged. + +`com.r35157.evelyn.service.status.EvelynStatusService` exposes immutable status +history and `AssetPrice getReferencePrice(Instant timestamp)`. The reference +price is calculated on demand for the supplied instant and carries the canonical +EVE/USDT trading pair. It works before startup and while sampling is stopped, +without accessing Ticker or history storage. See [Evelyn](docs/Evelyn.md) for the +curve and service contract. + ## Release log: | Date | Version | Description | |:-----------------|:--------------:|----------------------| diff --git a/docs/Evelyn.md b/docs/Evelyn.md index 7be7f92..037acf4 100644 --- a/docs/Evelyn.md +++ b/docs/Evelyn.md @@ -28,6 +28,7 @@ * [8. Risici, fail-safes og antifragilitet](#8-risici-fail-safes-og-antifragilitet) * [9. Udvikling, YouTrack-integration og offentlig gennemsigtighed](#9-udvikling-youtrack-integration-og-offentlig-gennemsigtighed) * [10. Fremtidige emner / TODO](#10-fremtidige-emner--todo) + * [11. Statusservice og referencepris](#11-statusservice-og-referencepris) ## 1. Introduktion formål og vision @@ -301,3 +302,35 @@ kapitalallokering over tid (AI-drevet tuning). ekstreme markedsbevægelser. - Open API for ekstern integration: Gør det muligt at tilføje eksterne datafeeds, udveksle signaler med andre systemer, eller automatisere funding-opgaver. + +## 11. Statusservice og referencepris + +Evelyn er navnet på det samlede system. `com.r35157.evelyn.service.status.EvelynStatusService` indsamler og +persisterer statusindeks for én navngiven instans og giver consumers adgang til den samme referencepris, som +Evelyn Price Index bruger. Referenceimplementeringen er `EvelynStatusServiceImpl` under +`com.r35157.evelyn.service.status.impl.ref`. Hub'en ejer fortsat startup af de enkelte processer. + +`AssetPrice getReferencePrice(Instant timestamp)` beregner prisen for det angivne tidspunkt: + +```text +Start: 2026-07-31T22:00:00Z +Startpris: 15.00 USDT pr. EVE +Årlig faktor: 1.20 +Årslængde: 31,536,000 sekunder +referencePrice = 15.00 × 1.20^(elapsedSeconds / 31,536,000) +``` + +Resultatet indeholder det kanoniske EVE/USDT-tradingpar, så quote-valutaen er eksplicit. Prisen er 15 ved start og +18 efter ét 365-dages år. Statusindekset bruger `((actualPrice / referencePrice) - 1) × 10` med samme samplingtidspunkt +og samme beregning og konstanter. Den eksisterende konvention om at behandle USDT som USD i indekset bevares. + +Referenceprisen beregnes ved hvert kald uden tickeropslag, historikadgang, workers eller ændring af historikken. +Kaldet virker før sampling starter og mens den er stoppet, også uden provisionerede historikfiler. `null` afvises +med `NullPointerException`, og tidspunkter før kurvens start afvises med `IllegalArgumentException`. + +Production og Test bevarer separate navne, historikker, livscyklusser og Mission Control-charts. Historikfilerne +ligger fortsat i `data/evelyn//status.log` med formatversion 1. Sampling sker fortsat med cirka ét +minuts fast forsinkelse, og et punkt persisteres før publicering i et immutable snapshot. `stop()` rydder kun +hukommelsen og beholder navnereservationen, restart genindlæser filen, og `close()` frigiver navnet efter sikker +terminering. `EvelynStatusIndexPoint` bliver i sin eksisterende package med alle seks indeksfelter; kun Evelyn +Price Index beregnes og vises, mens de øvrige fem felter fortsat er nul. Legacy-rebalancering er fortsat inaktiv. diff --git a/docs/Nenjim.md b/docs/Nenjim.md index c4e58d1..c41b6b3 100644 --- a/docs/Nenjim.md +++ b/docs/Nenjim.md @@ -268,14 +268,21 @@ nenjim.registry.service nenjim.journal.service assetaz.ticker.default jupiter-perps-alarm.default -evelyn.service.prod -evelyn.service.test +evelyn.status.prod +evelyn.status.test evelyn.iou-burner.prod nenjim.javafx-runtime.service evelyn.mission-control.default nenjim.process-manager.default ``` +Ved opgradering erstattes `evelyn.service.prod` med `evelyn.status.prod` og +`evelyn.service.test` med `evelyn.status.test` i deploymentets private `conf/NenjimHub.conf`, hvor de forekommer. +Bevar rækkefølge og aktive/udkommenterede valg. De gamle ID'er accepteres ikke længere. Hub'en konstruerer samme +navngivne `Production`- og `Test`-instanser gennem `EvelynStatusServiceImpl` og injicerer deres +`EvelynStatusService`-views i Mission Control. Process Manager viser de nye ID'er fra Hub'ens startup-snapshot. +Historik ligger fortsat i `data/evelyn//status.log` med formatversion 1 og kræver ingen migration. + Efter validering konstruerer og starter Hub'en Registry, registrerer det som `nenjim.registry.service` og registrerer startup-milepælen. Derefter konstrueres og registreres hele den nuværende passive graph uden at starte workers eller processer. Først herefter behandles resten af `[AUTO_START]` i den angivne rækkefølge, begyndende med Journal. Hver diff --git a/docs/Terminologi.md b/docs/Terminologi.md index c23e6d6..3c53b40 100644 --- a/docs/Terminologi.md +++ b/docs/Terminologi.md @@ -48,6 +48,7 @@ | JavaFxRuntimeService | Registrerbart service-view med kun non-null `runLater(Runnable)`. Samme referenceimplementation er en Hub-styret `NenjimProcess`; service-consumers får den gennem constructor injection, men får ingen lifecycle-, status-, configuration-, window-, event-, stop- eller restart-operationer. | | TickerService | Lifecycle-frit domain/query-view med kun latest-price lookup. Samme `TickerServiceImpl`-objekt er separat en Hub-styret `NenjimProcess`; det ejer `PriceSource`-startup internt og stopper allerede startede sources som rollback, hvis Ticker-start fejler, men eksponerer ingen almindelig start- eller stop-operation. | | EvelynIOUBurnerService | Tomt, lifecycle-frit domain-service-view, som bevarer typed Registry- og dependency-identitet, indtil eksterne domain-operationer findes. Samme `EvelynIOUBurnerServiceImpl`-objekt er separat en Hub-styret `NenjimProcess`; service-consumers får ingen start-, stop- eller restart-operationer. | +| EvelynStatusService | Service for én navngiven Evelyn-instans, som indsamler og persisterer statushistorik og udleverer den beregnede EVE-referencepris med eksplicit USDT-quote. Den bevarer sit eksisterende start/stop/close-contract; samme `EvelynStatusServiceImpl`-objekt startes af Hub'en som `NenjimProcess` under `evelyn.status.prod` eller `evelyn.status.test`. Evelyn er fortsat navnet på det samlede system. | | Context | Fremtidig beskrivelse af ønskede/resolverede artifactvalg og isoleret runtime; ikke implementeret af Journal-laget eller første Registry-version. | | Resolver | Fremtidig komponent for dependency graph og release-selection; Journal-laget repræsenterer reglerne, men vælger ikke en graf. | diff --git a/openspec/specs/evelyn-status-index-history/spec.md b/openspec/specs/evelyn-status-index-history/spec.md index c4af829..54527a0 100644 --- a/openspec/specs/evelyn-status-index-history/spec.md +++ b/openspec/specs/evelyn-status-index-history/spec.md @@ -7,7 +7,7 @@ Provide typed process-local Evelyn status measurements and visualize the current ## Requirements ### Requirement: Evelyn calculates the live price index -Evelyn SHALL obtain the actual EVE/USDT price from the latest AssetAZ Ticker observation for the canonical EVE/USDT trading pair. It SHALL treat USDT as USD for this calculation and SHALL NOT access Raydium directly. At sampling instant t, on or after 2026-07-31T22:00:00Z, the expected price SHALL be 15 × 1.20^(elapsedSeconds / 31,536,000) and the Evelyn Price Index SHALL be ((actualPrice / expectedPrice) - 1) × 10, calculated with adequate decimal precision and without arbitrary history rounding. +`EvelynStatusService` SHALL obtain the actual EVE/USDT price from the latest AssetAZ Ticker observation for the canonical EVE/USDT trading pair. It SHALL treat USDT as USD for this calculation and SHALL NOT access Raydium directly. At sampling instant t, on or after 2026-07-31T22:00:00Z, the reference price SHALL be 15 × 1.20^(elapsedSeconds / 31,536,000) and the Evelyn Price Index SHALL be ((actualPrice / referencePrice) - 1) × 10, calculated with adequate decimal precision and without arbitrary history rounding. Sampling and the public reference-price operation SHALL use the same reference calculation and constants at the same sampling instant. #### Scenario: Calculate expected price at the configured start - **WHEN** the sampling instant is 2026-07-31T22:00:00Z @@ -26,7 +26,7 @@ Evelyn SHALL obtain the actual EVE/USDT price from the latest AssetAZ Ticker obs - **THEN** the respective index is 0, 1, -1, 10, or -5 ### Requirement: Evelyn instances have safe unique persistent names -Every Evelyn instance SHALL have an NFC-normalized Unicode name whose preserved spelling and capitalization selects the direct child directory data/evelyn/. A name SHALL be rejected if it is empty or whitespace-only, has leading or trailing whitespace, is . or .., ends with a period, contains a control character or any of < > : " / \ | ? *, is a case-insensitive Windows-reserved filename including a reserved base with an extension, or normalizes as a path to anything other than one direct child of data/evelyn. Within one JVM, normalized names SHALL be reserved atomically and compared case-insensitively using locale-independent rules. +Every `EvelynStatusService` instance SHALL have an NFC-normalized Unicode name whose preserved spelling and capitalization selects the direct child directory data/evelyn/. A name SHALL be rejected if it is empty or whitespace-only, has leading or trailing whitespace, is . or .., ends with a period, contains a control character or any of < > : " / \ | ? *, is a case-insensitive Windows-reserved filename including a reserved base with an extension, or normalizes as a path to anything other than one direct child of data/evelyn. Within one JVM, normalized names SHALL be reserved atomically and compared case-insensitively using locale-independent rules. #### Scenario: A portable Unicode name is accepted - **WHEN** an instance is constructed with a valid Unicode name containing Danish characters @@ -41,10 +41,10 @@ Every Evelyn instance SHALL have an NFC-normalized Unicode name whose preserved - **THEN** exactly one reserves the persistent identity and the other fails ### Requirement: Evelyn distinguishes restartable stop from permanent close -Stopping Evelyn SHALL terminate sampling, clear memory, preserve its persistent file, and retain its name reservation so the same object can restart. Evelyn SHALL support permanent, idempotent close; closing an active instance SHALL first stop it safely, a successfully closed instance SHALL never start again, and its name SHALL be released only after its sampling thread has definitely terminated. A termination failure SHALL be reported and SHALL retain the reservation. +Stopping `EvelynStatusService` SHALL terminate sampling, clear memory, preserve its persistent file, and retain its name reservation so the same object can restart. `EvelynStatusService` SHALL support permanent, idempotent close; closing an active instance SHALL first stop it safely, a successfully closed instance SHALL never start again, and its name SHALL be released only after its sampling thread has definitely terminated. A termination failure SHALL be reported and SHALL retain the reservation. #### Scenario: A stopped instance restarts -- **WHEN** a stopped but not closed Evelyn instance is started again +- **WHEN** a stopped but not closed `EvelynStatusService` instance is started again - **THEN** it retains exclusive ownership of the same name and reloads the complete persisted history before sampling #### Scenario: An active instance closes successfully @@ -60,7 +60,7 @@ Stopping Evelyn SHALL terminate sampling, clear memory, preserve its persistent - **THEN** it completes without changing state or failing ### Requirement: Evelyn uses externally provisioned per-instance storage -Each named instance SHALL use the existing regular file data/evelyn//status.log. Before every start, Evelyn SHALL require the direct instance path to be an existing readable and writable directory and status.log to be an existing readable and writable regular file. Evelyn SHALL NOT create, replace, repair, truncate, rename, or otherwise provision either path. Any validation or actual open failure SHALL identify the affected path, leave memory unpublished, and prevent sampling from starting. +Each named instance SHALL use the existing regular file data/evelyn//status.log. Before every start, `EvelynStatusService` SHALL require the direct instance path to be an existing readable and writable directory and status.log to be an existing readable and writable regular file. `EvelynStatusService` SHALL NOT create, replace, repair, truncate, rename, or otherwise provision either path. Any validation or actual open failure SHALL identify the affected path, leave memory unpublished, and prevent sampling from starting. #### Scenario: Required storage is absent or unsuitable - **WHEN** the instance directory or status file is missing, the path has the wrong type, or required access is unavailable @@ -73,11 +73,11 @@ Each named instance SHALL use the existing regular file data/evelyn/:1:. It SHALL have exactly three fields. Unknown types, invalid timestamps or decimals, missing or surplus fields, and decreasing timestamps SHALL fail the complete load; equal timestamps SHALL be accepted. A valid declaration with no records SHALL represent empty history. Applicable failures SHALL identify the file path, one-based line number, and original line, and Evelyn SHALL neither skip, sort, partially publish, nor repair invalid content. +Type 1 SHALL encode :1:. It SHALL have exactly three fields. Unknown types, invalid timestamps or decimals, missing or surplus fields, and decreasing timestamps SHALL fail the complete load; equal timestamps SHALL be accepted. A valid declaration with no records SHALL represent empty history. Applicable failures SHALL identify the file path, one-based line number, and original line, and `EvelynStatusService` SHALL neither skip, sort, partially publish, nor repair invalid content. #### Scenario: A valid versioned history loads - **WHEN** the file has a valid first actual version declaration and valid type-1 records in non-decreasing timestamp order -- **THEN** Evelyn restores every record in file order with zero values for the five unimplemented indexes +- **THEN** the status service restores every record in file order with zero values for the five unimplemented indexes #### Scenario: The format declaration is invalid - **WHEN** the declaration is missing, duplicated, malformed, misplaced, or not version 1 @@ -88,7 +88,7 @@ Type 1 SHALL encode :1: