Compare commits
18
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1af26cb9a0 | ||
|
|
805e2e7e6e | ||
|
|
cfb49ed896 | ||
|
|
e71a8daa8d | ||
|
|
2f30712004 | ||
|
|
28640f3203 | ||
|
|
7e36b7ef95 | ||
|
|
df5cc0eda7 | ||
|
|
0ec22d5215 | ||
|
|
7aab307381 | ||
|
|
1ab439b578 | ||
|
|
2dce841cf4 | ||
|
|
cc7003ac94 | ||
|
|
59c5c62170 | ||
|
|
f1f31ab8f0 | ||
|
|
d057b0f3e6 | ||
|
|
e283f0b8f2 | ||
|
|
00a7f13c6f |
@@ -1,24 +1,34 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
|
cd -- "$(dirname -- "${BASH_SOURCE[0]}")"
|
||||||
|
|
||||||
|
LOG4J_CONFIG="conf/log4j2.xml"
|
||||||
|
|
||||||
|
if [[ ! -r "$LOG4J_CONFIG" || ! -s "$LOG4J_CONFIG" ]]; then
|
||||||
|
echo "ERROR: Log4j2 configuration file is missing, unreadable, or empty: $PWD/$LOG4J_CONFIG" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
shopt -s nullglob
|
shopt -s nullglob
|
||||||
|
|
||||||
jars=(libs/*.jar)
|
jars=(libs/*.jar)
|
||||||
if (( ${#jars[@]} == 0 )); then
|
if (( ${#jars[@]} == 0 )); then
|
||||||
echo "ERROR: No JARs found in libs/" >&2
|
echo "ERROR: No JARs found in $PWD/libs/" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
CLASSPATH=$(IFS=:; echo "${jars[*]}")
|
CLASSPATH=$(IFS=:; echo "${jars[*]}")
|
||||||
|
|
||||||
DEBUG_PORT="${DEBUG_PORT:-5005}"
|
DEBUG_PORT="${DEBUG_PORT:-5005}"
|
||||||
DEBUG_SUSPEND="${DEBUG_SUSPEND:-y}"
|
DEBUG_SUSPEND="${DEBUG_SUSPEND:-n}"
|
||||||
|
|
||||||
echo "Starting NenjimHub in DEBUG mode on port ${DEBUG_PORT} (suspend=${DEBUG_SUSPEND})"
|
echo "Starting NenjimHub in DEBUG mode on port ${DEBUG_PORT} (suspend=${DEBUG_SUSPEND})"
|
||||||
|
|
||||||
exec java \
|
exec java \
|
||||||
|
--enable-native-access=javafx.graphics \
|
||||||
--enable-preview \
|
--enable-preview \
|
||||||
"-agentlib:jdwp=transport=dt_socket,server=y,suspend=${DEBUG_SUSPEND},address=*:${DEBUG_PORT}" \
|
"-agentlib:jdwp=transport=dt_socket,server=y,suspend=${DEBUG_SUSPEND},address=*:${DEBUG_PORT}" \
|
||||||
-Dlog4j.configurationFile=conf/log4j2.xml \
|
"-Dlog4j.configurationFile=$LOG4J_CONFIG" \
|
||||||
-cp "$CLASSPATH" \
|
-cp "$CLASSPATH" \
|
||||||
com.r35157.nenjim.hubd.Main
|
com.r35157.nenjim.hubd.Main
|
||||||
|
|||||||
+242
-9
@@ -99,10 +99,27 @@ sikre tokenens pris eller muliggøre ny funktionalitet.
|
|||||||
|
|
||||||
### 2.2 Buybacks og burn
|
### 2.2 Buybacks og burn
|
||||||
En af de vigtigste mekanismer i Evelyn er det automatiske tilbagekøb og efterfølgende burn af 'Evelyn IOU' tokens.
|
En af de vigtigste mekanismer i Evelyn er det automatiske tilbagekøb og efterfølgende burn af 'Evelyn IOU' tokens.
|
||||||
En fast procentdel af overskuddet anvendes på at købe tokens på markedet – uanset den aktuelle pris – og brænde
|
En fast procentdel af overskuddet anvendes på at købe tokens på markedet, når
|
||||||
dem permanent. Dette reducerer det samlede udbud og skaber et kontinuerligt deflationært pres, hvilket typisk er
|
Evelyn Price Index er under nul, og brænde dem permanent. Det reducerer udbuddet,
|
||||||
positivt for investorerne. Buyback-strategien sikrer, at investorerne får en god pris for deres tokens, når det
|
når den aktuelle EVE-pris er lavere end Evelyns referencepris.
|
||||||
udnyttes at ikke alle investorer sælger på samme tid, da de har forskellige forventninger til prisen.
|
|
||||||
|
Når et input ellers er klar til køb, læser burneren en frisk kanonisk EVE/USDT-pris
|
||||||
|
fra `TickerService` og kalder den tilsvarende `EvelynStatusService.getReferencePrice()`
|
||||||
|
med det aktuelle beslutningstidspunkt. Køb tillades kun ved
|
||||||
|
`aktuel pris < referencepris`; lighed giver intet køb. Der bruges samme eksisterende
|
||||||
|
maksimale prisalder som til burn-værdiansættelsen, uden nye prisindstillinger.
|
||||||
|
Referencekurven beregnes af status-servicen; hverken den sidst gemte historikværdi
|
||||||
|
eller grafens punkter bruges til købsbeslutningen.
|
||||||
|
|
||||||
|
Ved lighed, højere pris eller manglende, ugyldige, for gamle eller utilgængelige
|
||||||
|
prisdata springes alle nye køb over i iterationen, og årsagen logges. Forløbet
|
||||||
|
fortsætter til EVE-saldo og mulig burn efter de selvstændige burn-regler. En fejl i
|
||||||
|
referenceopslaget blokerer derfor ikke burn, når dens egen friske værdiansættelse
|
||||||
|
og øvrige krav er opfyldt. Priserne vurderes igen ved næste ellers berettigede
|
||||||
|
iteration; et prisskip sætter ingen permanent suspension.
|
||||||
|
|
||||||
|
Med referencepris 15 USDT/EVE tillader 14,99 et ellers berettiget køb; 15,00 og
|
||||||
|
15,01 springer købet over. I alle tilfælde fortsætter EVE-burn-vurderingen.
|
||||||
|
|
||||||
Produktionsprocessen `evelyn.iou-burner.prod` accepterer følgende inputs i rækkefølge:
|
Produktionsprocessen `evelyn.iou-burner.prod` accepterer følgende inputs i rækkefølge:
|
||||||
|
|
||||||
@@ -131,15 +148,45 @@ ingen inputtoken/USDC-pris eller nye Ticker-kilder og historikfiler.
|
|||||||
Højst ét input forsøges vekslet pr. iteration, med en times pause efter hver
|
Højst ét input forsøges vekslet pr. iteration, med en times pause efter hver
|
||||||
afsluttet iteration. Jupiter vælger pool og rute til EVE. USDT har fortsat første
|
afsluttet iteration. Jupiter vælger pool og rute til EVE. USDT har fortsat første
|
||||||
prioritet, så en vedvarende spendabel USDT-saldo kan udsætte SyrupUSDC-veksling.
|
prioritet, så en vedvarende spendabel USDT-saldo kan udsætte SyrupUSDC-veksling.
|
||||||
Hele den synlige EVE-saldo er derefter berettiget til burn, også ved direkte
|
Efter det mulige buyback, også når prisbetingelsen springer købet over,
|
||||||
EVE-indbetalinger. Burn kræver stadig en frisk EVE/USDT-observation gennem det
|
genlæses hele den synlige EVE-saldo, også ved direkte
|
||||||
uændrede `TickerService.getLatestPrice()` og bekræftes ved `FINALIZED` før det
|
EVE-indbetalinger. Ejeren har valgt et produktionsminimum på **0,1 EVE pr. burn**
|
||||||
eneste notifikationsforsøg. Inputgrænserne begrænser ikke EVE-burn.
|
og en **reserve på 0,01 EVE**. Hub'en angiver begge som `MoneyAmount` med EVE's
|
||||||
|
kanoniske UUID. Minimum skal være positivt, reserve skal være ikke-negativ;
|
||||||
|
en reserve på nul er gyldig. Null-værdier og andre valutaer afvises ved oprettelse.
|
||||||
|
|
||||||
|
Burnbeløbet beregnes som `max(EVE-saldo - EVE-reserve, 0)`, afrundet ned til
|
||||||
|
EVE-mintens præcision. Der brændes kun, når dette faktiske beløb er positivt og
|
||||||
|
mindst minimum. Når tærsklen nås, brændes hele det berettigede beløb over
|
||||||
|
reserven; minimum er en tærskel, og der er intet burn-maksimum. Eventuelle
|
||||||
|
afrundingsrester bliver i wallet'en sammen med reserven. Små buybacks fortsætter,
|
||||||
|
når købspris og inputkrav tillader det, og samler EVE op mellem burns.
|
||||||
|
Under tærsklen sker hverken burn eller
|
||||||
|
burn-notifikation.
|
||||||
|
|
||||||
|
| EVE-saldo | Produktionshandling | EVE tilbage efter succes |
|
||||||
|
| ---: | --- | ---: |
|
||||||
|
| 0,105 | Ingen burn | 0,105 |
|
||||||
|
| 0,11 | Burn 0,1 EVE | 0,01 |
|
||||||
|
| 0,135 | Burn 0,125 EVE | 0,01 |
|
||||||
|
|
||||||
|
Issue #93 bruger minimum 1 EVE og reserve 0,1 EVE som adfærdseksempler:
|
||||||
|
1,05 EVE giver ingen burn, 1,1 EVE giver burn af 1 EVE, og 1,35 EVE giver burn af
|
||||||
|
1,25 EVE. Produktionsværdierne ovenfor blev valgt særskilt den 2026-10-06.
|
||||||
|
|
||||||
|
Burn kræver stadig en frisk EVE/USDT-observation gennem det uændrede
|
||||||
|
`TickerService.getLatestPrice()` og bekræftes ved `FINALIZED` før det eneste
|
||||||
|
notifikationsforsøg. Pending burn og notifikationens EVE-beløb og dollarværdi
|
||||||
|
bruger det faktisk indsendte burnbeløb uden reserven. Et pending burn afklares
|
||||||
|
med sin oprindelige signatur uden ny tærskelvurdering eller genindsendelse.
|
||||||
|
Inputgrænserne begrænser ikke EVE-burn.
|
||||||
|
|
||||||
Ved ukendt swap-resultat gemmes den oprindelige signatur, inputvaluta, beløb og
|
Ved ukendt swap-resultat gemmes den oprindelige signatur, inputvaluta, beløb og
|
||||||
validitetsoplysninger i processen. Senere iterationer undersøger samme signatur
|
validitetsoplysninger i processen. Senere iterationer undersøger samme signatur
|
||||||
uden at gensende transaktionen. Bekræftet succes eller fejl ved mindst `CONFIRMED`
|
uden at gensende transaktionen. Bekræftet succes eller fejl ved mindst `CONFIRMED`
|
||||||
frigiver nye swaps med frisklæste saldi. Udløb frigiver kun et swap, når den
|
frigiver mulig ny inputvurdering med frisklæste saldi; ethvert nyt køb skal stadig
|
||||||
|
bestå den aktuelle prisbetingelse. Afklaringen er read-only og fortsætter også
|
||||||
|
ved pris lig med eller over referenceprisen. Udløb frigiver kun et swap, når den
|
||||||
oprindelige transaktions validitet er udløbet, og frisk finalized historik for
|
oprindelige transaktions validitet er udløbet, og frisk finalized historik for
|
||||||
signerens adresse med tilstrækkelig lokal dækning beviser, at den ikke blev udført.
|
signerens adresse med tilstrækkelig lokal dækning beviser, at den ikke blev udført.
|
||||||
En begrænset historikside, der ikke dækker hele perioden, er fortsat uafklaret.
|
En begrænset historikside, der ikke dækker hele perioden, er fortsat uafklaret.
|
||||||
@@ -384,3 +431,189 @@ minuts fast forsinkelse, og et punkt persisteres før publicering i et immutable
|
|||||||
hukommelsen og beholder navnereservationen, restart genindlæser filen, og `close()` frigiver navnet efter sikker
|
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
|
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.
|
Price Index beregnes og vises, mens de øvrige fem felter fortsat er nul. Legacy-rebalancering er fortsat inaktiv.
|
||||||
|
|
||||||
|
## 12. Perps-service og Mission Control
|
||||||
|
|
||||||
|
`EvelynPerpsService` udleverer et immutable `PerpsSnapshot` gennem det parameterløse
|
||||||
|
`getPositionsSnapshot()`. Læsning foretager ingen integrationskald. Hub'en starter
|
||||||
|
separate processer `evelyn.perps.prod` og `evelyn.perps.test`, hver med sin eksisterende
|
||||||
|
wallet-liste: seks PROD-adresser og én TEST-adresse fra issue #98. Begge får en fælles
|
||||||
|
read-only `JupiterPerpsOverview` og en fælles `PhoenixPerpsService`; sidstnævnte er
|
||||||
|
registreret som `phoenix.perps.default`. De eksisterende Jupiter-services til handel
|
||||||
|
og alarmer beholder deres afhængigheder og cache.
|
||||||
|
|
||||||
|
Servicen læser asynkront ved start og venter 60 sekunder efter hvert fuldført gennemløb.
|
||||||
|
Runder overlapper ikke og afhænger ikke af EMC's synlighed. Jupiter-overblikket bruger
|
||||||
|
ét REST-opslag pr. wallet. Phoenix bruger trader-state og ét TWAP-opslag for wallets
|
||||||
|
med eksisterende subaccounts, også uden åbne positioner. Metadata hentes ved positioner eller fyldte
|
||||||
|
TWAP-ordrer med resterende delordrer; én mark price hentes pr. unikt åbent marked,
|
||||||
|
og der foretages et risikolæs pr. synlig konto. Lokal trader-adresseafledning bruger
|
||||||
|
den eksisterende Solana-afhængighed.
|
||||||
|
Begge har 10 sekunders request-timeout; Phoenix bevarer fælles
|
||||||
|
pacing og 429-cooldown. En fuld opdatering tager opslagstid plus pausen, ikke nødvendigvis
|
||||||
|
ét minut. Implementeringens `close()` afbryder worker'en; consumer-API'et har kun læsning.
|
||||||
|
|
||||||
|
Snapshot'et adskiller isolerede positioner fra Cross-konti med egne balancer. Pengebeløb
|
||||||
|
er præcise USD-decimaler, og null betyder utilgængelig. Før første succes indeholder
|
||||||
|
hver konfigureret wallet/venue en ukendt scope, ingen syntetiske positioner. Succes
|
||||||
|
publiceres pr. wallet/venue; en fejl beholder den kombinations sidste observation og
|
||||||
|
oprindelige succes-tidspunkt. Andre wallets/venues forsøges stadig. Et gyldigt tomt svar
|
||||||
|
rydder den pågældende scope og kan totalisere nul; HTTP-/formatfejl kan ikke. En gyldig
|
||||||
|
Cross-konto med kapital bevares, selv om dens sidste position lukkes.
|
||||||
|
|
||||||
|
EMC viser **Position | Risk | Direction | Margin | Collateral | Leverage | Exposure | Value | uPnL | TWAP**
|
||||||
|
under den synlige rod **Perpetual Contracts**. Isolerede positioner har wallet → venue →
|
||||||
|
position; Phoenix Cross har wallet → venue → Cross → position. Positionens konto-ID
|
||||||
|
bestemmer identitet og tilhørsforhold, aldrig instrumentteksten. Wallets følger den
|
||||||
|
hidtidige konfiguration med Jupiter før Phoenix. En venue uden positioner, Cross-konti eller TWAP-advarsler
|
||||||
|
skjules og vises igen, når der kommer indhold. Skjulte venues indgår fortsat i snapshot
|
||||||
|
og totaler; deres læsestatus findes på wallet-rækkens tooltip. En Cross-konto med kapital
|
||||||
|
holder derfor Phoenix synlig, selv uden åbne positioner. Eksisterende foldestatus, markering og
|
||||||
|
rækkeobjekter bevares; kun ændrede rækkeværdier eller børn opdateres, også når risikodata
|
||||||
|
ældes i et ellers uændret snapshot. Vinduesstart er 1100 × 600,
|
||||||
|
og smallere vinduer kan scrolle vandret, når kolonnernes minimumsbredder kræver det.
|
||||||
|
|
||||||
|
TWAP-progress vises på en eksisterende Phoenix-position, når ordren selv har et faktisk
|
||||||
|
fill og resterende delordrer. Ufyldte ordrer opretter ingen rækker og vises heller
|
||||||
|
ikke på en position, som allerede fandtes. Progressbaren måler udførte delordrer;
|
||||||
|
teksten er estimeret resterende tid fra seneste TWAP-observation, fx `≈ 28:32:10`
|
||||||
|
eller `≈ 128:32:10`, uden timerollover eller live-nedtælling. Tooltip viser
|
||||||
|
`Estimated completion`, totalstørrelse, næste delordrestørrelse, fyldt mængde og
|
||||||
|
observationstid. Restordrer kan være mindre. Dollarstørrelser bruger samme Phoenix-
|
||||||
|
markpris som positionen og kræver ikke Ticker-priser; uden markpris bevares mængderne,
|
||||||
|
mens berørte dollarværdier og totaler bliver utilgængelige. En TWAP-læsefejl beholder
|
||||||
|
hele venue-observationen og viser `Retained` i cellen. Se
|
||||||
|
[TWAP-beregninger og begrænsninger](PhoenixPerps.md#twap-observations) for slots,
|
||||||
|
forfaldne tidsplaner og prisestimater.
|
||||||
|
|
||||||
|
Exposure er brutto: 100 USD Long + 100 USD Short giver 200 USD. Dollar-uPnL summeres
|
||||||
|
med fortegn. Collateral/Value tæller hver isoleret position og hver delt Cross-konto én
|
||||||
|
gang, inklusive ubrugt kapital på de inkluderede Cross-konti. Cross-positionernes
|
||||||
|
marginkrav lægges ikke til faktisk kontokapital. Mangler en nødvendig del, er netop
|
||||||
|
feltets total utilgængelig; en delsum præsenteres aldrig som total. Ingen summering eller
|
||||||
|
gennemsnitsberegning af leverage eller PnL-procenter. Totalerne afhænger ikke af foldning.
|
||||||
|
|
||||||
|
Isoleret leverage er Exposure / positiv Collateral; uPnL-procent er 100 × pris-uPnL /
|
||||||
|
positiv Collateral. På Cross-positioner er Collateral et initialt marginkrav i USD,
|
||||||
|
ikke særskilt ejet kapital. Cross-gruppens leverage er bruttoeksponering / udiskonteret
|
||||||
|
equity før estimerede exitgebyrer. Cross-positionernes leverage og ROI-procent er
|
||||||
|
utilgængelige, fordi Phoenix-konventionerne ikke er verificeret; dollar-uPnL vises.
|
||||||
|
Cross-positioners Value er ikke entydigt defineret og vises som —. Risk viser særskilt
|
||||||
|
vedligeholdelsesmarginens udnyttelse; det er ikke samlet initial-marginudnyttelse eller fri købekraft.
|
||||||
|
|
||||||
|
Value er et estimat ved referencepriser, ikke et eksekverbart tilbud. Phoenix medregner
|
||||||
|
collateral, pris-uPnL og signed uafregnet funding og fratrækker estimerede takergebyrer
|
||||||
|
med kontoens fee override. Slippage, price impact, swaps, netværksgebyrer og kontorent
|
||||||
|
indgår ikke. Ukendt spot-collateral gør de berørte værdiansættelser utilgængelige.
|
||||||
|
Jupiters rapporterede `valueUsd` vises uændret som et estimat, fx `≈$123.23`, efter
|
||||||
|
præcis konvertering fra heltals-mikro-USD. Tooltip forklarer, at det faktiske provenu
|
||||||
|
ved lukning kan afvige. En tidligere observation trak åbningsgebyrer fra, selvom
|
||||||
|
collateral allerede var registreret efter gebyrer; gebyrbehandlingen er derfor
|
||||||
|
usikker, og collateral-/profitloft-semantikken er fortsat ikke verificeret. Det
|
||||||
|
beviser ikke, at alle API-estimater er forkerte, eller at forskellen kun er små gebyrer.
|
||||||
|
Der tilføjes ingen gebyrer tilbage og foretages ingen korrigeret netberegning.
|
||||||
|
|
||||||
|
Tilgængelige Value-estimater fra både Jupiter og Phoenix samt deres venue-, wallet-
|
||||||
|
og **Perpetual Contracts**-totaler har ét `≈` foran beløbet. Totalens tooltip forklarer
|
||||||
|
de bidragende estimatkilder, også ved blandede venues og sammenfoldede grupper.
|
||||||
|
Nul og negative beløb bevares. Manglende/null Value gør fortsat den berørte total `—`
|
||||||
|
uden numerisk estimatmarkering; en succesfuldt læst tom gruppe kan give nul. Ugyldige
|
||||||
|
beløb fejler hele observationen, så tidligere data og oprindelig læsetid bevares med
|
||||||
|
fejlstatus. En ny succes erstatter den tidligere værdi, også når den nye Value mangler. Se
|
||||||
|
[Jupiter-overblikket](JupiterPerpsOverview.md) og [Phoenix-integrationen](PhoenixPerps.md)
|
||||||
|
for datakilder, enheder og beregninger.
|
||||||
|
|
||||||
|
Beløb har tusindtalsseparator og to decimaler, leverage × og pris-uPnL et synligt fortegn.
|
||||||
|
Positiv uPnL er grøn, negativ rød, afrundet nul neutral uden negativt nul. De svage
|
||||||
|
Long/Short-baggrunde bevares; grupper har tydeligere skrift og ingen retningsfarve.
|
||||||
|
Tooltips forklarer definitioner, manglende værdier og sidste succesfulde læsetidspunkt.
|
||||||
|
Efter fejl markeres en synlig venue som retained; læsestatus for skjulte venues findes
|
||||||
|
på wallet-rækkens tooltip. Gamle tal beskrives ikke som nye.
|
||||||
|
|
||||||
|
EMC spørger hver miljøservice straks ved åbning og hvert tredje sekund uden at blokere
|
||||||
|
JavaFX-tråden. Skjul/luk stopper kun EMC-polling. Der er ingen sample-data, Refresh-knap,
|
||||||
|
handel, signering, kontoaktivering eller automatisk genstart af Hub'en i denne læsevej.
|
||||||
|
|
||||||
|
### TWAP-advarsler
|
||||||
|
|
||||||
|
En fyldt Phoenix-TWAP med resterende delordrer, som mangler markedsmetadata eller
|
||||||
|
ikke matcher en åben position, bevares som en advarsel. TWAP-cellen viser en lille
|
||||||
|
orange trekant med udråbstegn og kort tekst. Advarslen står på positionen, når
|
||||||
|
ordren sikkert kan tilknyttes den; ellers på den ejende Cross-gruppe. Gruppen
|
||||||
|
bevares også uden kapital eller positioner. En umatchet isoleret ordre står på
|
||||||
|
Phoenix med sit korrekte subaccount i tooltip, aldrig på en opdigtet Cross-konto.
|
||||||
|
En matchet ordre uden brugbart estimat for næste udførelse viser en timing-advarsel
|
||||||
|
sammen med sin progressbar; det er ikke bevis på, at udførelsen er stoppet.
|
||||||
|
|
||||||
|
Advarsler samles op gennem **position → Cross → Phoenix → wallet → Perpetual
|
||||||
|
Contracts**, eller den faktiske isolerede gren uden Cross. Hvert niveau tæller
|
||||||
|
hver underliggende ordre én gang, også når undergrene er foldet sammen. Flere
|
||||||
|
ordrer vises med antal. Tooltip kan rulles og bliver stående, mens markøren er over
|
||||||
|
cellen eller detaljerne. Det identificerer wallet, konto/position, årsag,
|
||||||
|
TWAP-adresse, asset/subaccount, rå tællere/fills og oprindeligt observationstidspunkt.
|
||||||
|
Ukendte symboler, enheder, dollarværdier og tider opfindes ikke. Marginrisiko og
|
||||||
|
kapitaltotaler ændres ikke af advarslerne.
|
||||||
|
|
||||||
|
Ordren skal stadig selv have faktiske fills og resterende delordrer. Ufyldte og
|
||||||
|
færdige ordrer er skjult, også i gruppeoptællinger. Succesfulde opslag rydder løste
|
||||||
|
advarsler helt op gennem træet og bevarer de øvrige. Fejl bevarer tidligere data og
|
||||||
|
markerer berørte advarsler og opsummeringer `retained`; efter fem minutter eller ved
|
||||||
|
fremtidigt tidsstempel markeres de `stale`. Originale tider bevares, også ved fejl i
|
||||||
|
EMC's snapshot-læsning. Alderskontrol bruger eksisterende UI-polling; fem minutter
|
||||||
|
er en datakvalitetsgrænse, ikke en udførelsesfrist. Se [Phoenix-læsningen](PhoenixPerps.md#twap-observations).
|
||||||
|
|
||||||
|
### Risiko i Perps
|
||||||
|
|
||||||
|
Risk står ved positionsnavnet og bruger tekst og en statusprik, mens Long/Short-
|
||||||
|
baggrundene bevares. På isolerede positioner vises rapporteret likvideringsafstand:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Long: 100 × (venue-referencepris − likvideringspris) / venue-referencepris
|
||||||
|
Short: 100 × (likvideringspris − venue-referencepris) / venue-referencepris
|
||||||
|
```
|
||||||
|
|
||||||
|
Ejeren valgte 24. oktober 2026 **neutral positiv afstand og rød ved nået/overskredet
|
||||||
|
grænse**. Der er ingen generelle grøn/gul/rød-grænser pr. marked. Positiv uPnL eller
|
||||||
|
lav leverage tildeler ikke en sikkerhedsstatus. Negativ afstand bevarer fortegnet;
|
||||||
|
det er en observeret grænseoverskridelse, ikke bevis for gennemført likvidering.
|
||||||
|
Manglende, nul og negative sentinel-priser er ukendte, ikke uendelig sikkerhed.
|
||||||
|
Tooltip viser venue-priser, præcis afstand, eventuel rapporteret tier og observationstid.
|
||||||
|
|
||||||
|
Phoenix' egne tiers bruger grøn **OK**, gul **At risk**, orange **Cancellable** og
|
||||||
|
rød **Liquidatable**, **Backstop** eller **High risk**. En selvstændigt gyldig
|
||||||
|
kontoklasse kan bruges, selv om en positiv likvideringspris mangler. På Cross-kontoen:
|
||||||
|
|
||||||
|
```text
|
||||||
|
MM utilization = 100 × maintenance margin / positiv effective collateral
|
||||||
|
USD headroom = effective collateral − maintenance margin
|
||||||
|
```
|
||||||
|
|
||||||
|
MM er marginudnyttelse, ikke likvideringssandsynlighed eller en prisafstand. Phoenix'
|
||||||
|
præcise tier styrer likvideringsstatus; visningsafrunding bruges ikke til klassifikation.
|
||||||
|
Ved ikke-positiv effektiv sikkerhed er procenten utilgængelig. En kendt tom konto
|
||||||
|
uden vedligeholdelseskrav vises som **No positions**; det lover ikke noget om fremtidige
|
||||||
|
ordrefills. Manglende nødvendige data eller modstridende
|
||||||
|
Safe-data må ikke vises grønt. Cross-positioner viser **Shared** med konto-ID i tooltip,
|
||||||
|
og kontoens risiko tælles kun én gang uanset antallet af positioner.
|
||||||
|
|
||||||
|
Grupper viser værste kendte risiko og antal ukendte/forældede scopes uden gennemsnit
|
||||||
|
af afstande eller MM-procenter. En neutral afstand gør ikke hele gruppen grøn; **Distance
|
||||||
|
only** angiver, at der ikke er tildelt en sikkerhedsklasse. Før første succes er en
|
||||||
|
venue ukendt. En tidligere tom venue kan også blive forældet, selv om dens overskrift
|
||||||
|
er skjult. Kendt kritisk risiko forbliver rød med usikkerhedsmarkering efter fejl/aldring.
|
||||||
|
|
||||||
|
Risikodata bliver **Stale** ved fem minutter fra ældste nødvendige observation,
|
||||||
|
straks ved venue- eller snapshot-læsefejl eller ved et fremtidigt tidsstempel. Også
|
||||||
|
et fejlet `getPositionsSnapshot()` genviser den beholdte observation med fejlstatus.
|
||||||
|
Fem minutter er en grænse
|
||||||
|
for datakvalitet, ikke en garanti for sikkerhed i perioden. Langsomme normale runder
|
||||||
|
kan derfor give Stale. Den eksisterende tresekunders UI-polling genvurderer alder,
|
||||||
|
også når servicen returnerer samme snapshot, uden en ny timer eller flere venue-kald.
|
||||||
|
Forældet eller ufuldstændig ikke-kritisk risiko får ingen grøn statusprik.
|
||||||
|
Tidsstempler er modtagelsestider; API-lag og separate pris-/kontolæs begrænser aktualiteten.
|
||||||
|
Visningen viser senest observeret risiko og er ikke en livealarm. Datakilderne er
|
||||||
|
beskrevet i [Jupiter-overblikket](JupiterPerpsOverview.md) og
|
||||||
|
[Phoenix-risikolæsningen](PhoenixPerps.md#venue-reported-account-risk).
|
||||||
|
|
||||||
|
Den kanoniske systembeskrivelse findes i
|
||||||
|
[Evelyn: services og status](../../r35157_docs/projects/evelyn/README.md#evelynperpsservice).
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
# Jupiter read-only Perps overview
|
||||||
|
|
||||||
|
`JupiterPerpsOverview` is an overview-only interface under
|
||||||
|
`com.r35157.libs.jupiter.perps`; `impl.ref.RestJupiterPerpsOverview` reads
|
||||||
|
`GET https://perps-api.jup.ag/v2/positions?walletAddress=<address>` once per wallet.
|
||||||
|
The Hub injects this reader into both Evelyn Perps processes with their existing
|
||||||
|
separate wallet lists. It has a 10-second request timeout, no immediate retries
|
||||||
|
and no portfolio cache. Signing, transactions, alarms, their existing Solana cache
|
||||||
|
and entry-price behavior remain in the existing `JupiterPerpsService`.
|
||||||
|
|
||||||
|
## Response contract and definitions
|
||||||
|
|
||||||
|
The official [CLI response type](https://github.com/jup-ag/cli/blob/main/src/clients/PerpsClient.ts)
|
||||||
|
and [API reference](https://perps-api.jup.ag/v1/docs) describe the position list.
|
||||||
|
The implementation requires an integer `count` matching the complete `dataList`,
|
||||||
|
unique valid position accounts, valid asset mints, Long/Short side and positive
|
||||||
|
size. HTTP/schema failures throw; only an explicit count-zero empty list clears
|
||||||
|
the scope. Missing optional valuation fields become unavailable; malformed
|
||||||
|
amounts fail the read.
|
||||||
|
|
||||||
|
| Display | Source / definition |
|
||||||
|
| --- | --- |
|
||||||
|
| Exposure | `sizeUsd / 10^6`; preserves the existing recorded USD-size basis |
|
||||||
|
| Collateral | `collateralUsd / 10^6`, the venue's recorded post-fee USD collateral |
|
||||||
|
| Leverage | Exposure / positive Collateral; not equity-based leverage |
|
||||||
|
| uPnL | `pnlBeforeFeesUsd / 10^6`, price PnL before separately reported fees |
|
||||||
|
| uPnL percent | 100 × uPnL / positive Collateral |
|
||||||
|
| Value | `valueUsd / 10^6`, Jupiter's reported estimate, unchanged and displayed with `≈` |
|
||||||
|
| Risk reference price | Positive `markPriceUsd / 10^6` from the same response |
|
||||||
|
| Reported liquidation price | Positive `liquidationPriceUsd / 10^6`; missing/nonpositive values are unavailable |
|
||||||
|
|
||||||
|
All amounts are parsed as decimal integers and converted exactly. The API's
|
||||||
|
`pnlBeforeFeesPct` is already percentage points (95.48 means 95.48%), but the
|
||||||
|
overview derives the requested percentage against precise collateral itself.
|
||||||
|
The API's rounded `leverage` is likewise not used for the derived ratio.
|
||||||
|
|
||||||
|
The overview preserves one original response observation time on all positions.
|
||||||
|
It does not treat a position's `updatedTime` as proof of current price freshness.
|
||||||
|
Risk uses the venue's reported prices, with no new liquidation-price formula or
|
||||||
|
Ticker dependency. Missing/sentinel prices retain the position and its independent
|
||||||
|
metrics. Present malformed amounts still fail the read. Isolated signed distance,
|
||||||
|
the owner's neutral-distance policy and expiration are described in
|
||||||
|
[Perps risk presentation](Evelyn.md#risiko-i-perps).
|
||||||
|
|
||||||
|
## Reported Value and its uncertainty
|
||||||
|
|
||||||
|
The overview preserves optional `valueUsd` exactly as supplied, including zero
|
||||||
|
and signed values. EMC displays it with `≈` and two USD decimals (for example,
|
||||||
|
`≈$123.23`). Its tooltip explains: “Estimated position value reported by Jupiter.
|
||||||
|
Actual proceeds on closing may differ.” Missing/null values remain unavailable;
|
||||||
|
present malformed amounts fail the complete observation. Successful observations
|
||||||
|
replace earlier values, including replacing an estimate with unavailable Value.
|
||||||
|
Failed reads retain the original observation, success time and failure indication.
|
||||||
|
|
||||||
|
Available Jupiter, wallet and Perpetual Contracts Value totals inherit the estimate
|
||||||
|
qualification independently of expansion state. Mixed Phoenix/Jupiter totals
|
||||||
|
identify both estimate sources; Phoenix's existing account-aware closing estimates
|
||||||
|
and single counting of shared Cross capital are preserved. A missing required Value
|
||||||
|
still makes the affected total `—`, without a numeric approximation marker. A
|
||||||
|
successfully read empty scope can total zero.
|
||||||
|
|
||||||
|
This reports a venue estimate, not verified closing proceeds. It does not add
|
||||||
|
opening fees back, correct the API number, introduce closing quotes or combine
|
||||||
|
additional on-chain observations with the response.
|
||||||
|
|
||||||
|
On 24 October 2026 the funded configured wallet's six API sizes and collateral
|
||||||
|
amounts matched read-only on-chain position data. For its SOL Short, the response
|
||||||
|
contained these integer micro-USD values:
|
||||||
|
|
||||||
|
```text
|
||||||
|
collateralUsd = 18559 pnlBeforeFeesUsd = 17721
|
||||||
|
openFeesUsd = 361 closeFeesUsd = 361
|
||||||
|
borrowFeesUsd = 7 valueUsd = 35551
|
||||||
|
```
|
||||||
|
|
||||||
|
`35551 = 18559 + 17721 - 361 - 361 - 7`. The on-chain collateral was also
|
||||||
|
18559; the official [position account documentation](https://developers.jup.ag/docs/perps/position-account)
|
||||||
|
defines collateral as after fees. This observation raises uncertainty about
|
||||||
|
subtracting a settled opening fee again; it does not prove that every API estimate
|
||||||
|
is wrong. Adding that fee back alone would not establish
|
||||||
|
the full contract: collateral-currency valuation and the `lockedAmount` profit
|
||||||
|
cap remain unverified. The response does not expose that cap. The possible difference
|
||||||
|
from actual closing proceeds is not known to be limited to small fees. Issue #100
|
||||||
|
therefore withheld Value; [issue #104](https://git.r35157.com/r35157/com_r35157_nenjim-hubd-impl_ref/issues/104)
|
||||||
|
replaces that presentation with the explicitly qualified venue estimate.
|
||||||
|
|
||||||
|
There is no verified closing-value parity with Jupiter. Counts, amounts and prices
|
||||||
|
above are historical observations, not fixed expectations.
|
||||||
|
See the change's `verification.md` for observation times and current checks.
|
||||||
@@ -177,7 +177,9 @@ nenjim.journal.service</code></pre>
|
|||||||
<code>start()</code>, and recorded only after successful return. An omitted process is therefore neither
|
<code>start()</code>, and recorded only after successful return. An omitted process is therefore neither
|
||||||
constructed, registered, started, nor displayed by Process Manager.</p>
|
constructed, registered, started, nor displayed by Process Manager.</p>
|
||||||
<p><code>[AUTO_START]</code> is the complete manually ordered process dependency closure. The Hub does not infer,
|
<p><code>[AUTO_START]</code> is the complete manually ordered process dependency closure. The Hub does not infer,
|
||||||
insert, recursively construct, or reorder dependencies; JavaFX runtime must precede selected JavaFX
|
insert, recursively construct, or reorder dependencies. Ticker and Production Evelyn status must precede
|
||||||
|
a selected Production IOU Burner; the Hub injects that status service for reference-price decisions before
|
||||||
|
new buybacks and fails clearly if the predecessor is unavailable. JavaFX runtime must precede selected JavaFX
|
||||||
applications. Construction, registration, and startup remain fail-fast without rollback. Earlier successful
|
applications. Construction, registration, and startup remain fail-fast without rollback. Earlier successful
|
||||||
snapshot entries remain truthful after a later failure, and the online point plus blocking wait are reached
|
snapshot entries remain truthful after a later failure, and the online point plus blocking wait are reached
|
||||||
only after every configured process succeeds.</p>
|
only after every configured process succeeds.</p>
|
||||||
|
|||||||
@@ -271,6 +271,8 @@ jupiter-perps-alarm.default
|
|||||||
evelyn.status.prod
|
evelyn.status.prod
|
||||||
evelyn.status.test
|
evelyn.status.test
|
||||||
evelyn.iou-burner.prod
|
evelyn.iou-burner.prod
|
||||||
|
evelyn.perps.prod
|
||||||
|
evelyn.perps.test
|
||||||
nenjim.javafx-runtime.service
|
nenjim.javafx-runtime.service
|
||||||
evelyn.mission-control.default
|
evelyn.mission-control.default
|
||||||
nenjim.process-manager.default
|
nenjim.process-manager.default
|
||||||
@@ -283,6 +285,26 @@ navngivne `Production`- og `Test`-instanser gennem `EvelynStatusServiceImpl` og
|
|||||||
`EvelynStatusService`-views i Mission Control. Process Manager viser de nye ID'er fra Hub'ens startup-snapshot.
|
`EvelynStatusService`-views i Mission Control. Process Manager viser de nye ID'er fra Hub'ens startup-snapshot.
|
||||||
Historik ligger fortsat i `data/evelyn/<instance-name>/status.log` med formatversion 1 og kræver ingen migration.
|
Historik ligger fortsat i `data/evelyn/<instance-name>/status.log` med formatversion 1 og kræver ingen migration.
|
||||||
|
|
||||||
|
Mission Control kræver både `evelyn.perps.prod` og `evelyn.perps.test` før `evelyn.mission-control.default`
|
||||||
|
i den private `conf/NenjimHub.conf`. Erstat den tidligere `evelyn.perps.default`-linje med de to nye ID'er;
|
||||||
|
det gamle ID accepteres ikke længere. Hub'en konstruerer, registrerer og starter to separate
|
||||||
|
`EvelynPerpsServiceImpl`-processer med separate hardcodede wallet-lister og tilhørende Jupiter-integrationer.
|
||||||
|
Begge får desuden den fælles `PhoenixPerpsService` uden positionscache, som Hub'en registrerer passivt som
|
||||||
|
`phoenix.perps.default`; den skal ikke tilføjes til `[AUTO_START]`.
|
||||||
|
Hub'en injicerer deres read-only
|
||||||
|
service-views til de tilsvarende miljøfaner i EMC. Process Manager viser begge ID'er efter vellykket startup.
|
||||||
|
Den lokale udviklingskonfiguration er opdateret; andre deployments med EMC skal migreres tilsvarende.
|
||||||
|
Udeladt eller forkert rækkefølge giver en tydelig konstruktionsfejl uden automatisk indsættelse.
|
||||||
|
Det minimale versionsstyrede bootstrap-eksempel er uændret. Hver service ejer sit eget snapshot og
|
||||||
|
opdaterer det fra Jupiter og Phoenix i baggrunden med 60 sekunders pause efter fuldførte gennemløb.
|
||||||
|
Sidste vellykkede data bevares separat pr. wallet og venue. EMC-læsninger bruger kun
|
||||||
|
det publicerede snapshot. Wallet-listerne fra issue #98 indeholder seks PROD-adresser og én TEST-adresse.
|
||||||
|
|
||||||
|
Hvis `evelyn.iou-burner.prod` vælges, skal både `assetaz.ticker.default` og
|
||||||
|
`evelyn.status.prod` stå før den. Hub'en injicerer Production status-servicen til
|
||||||
|
referenceprisopslag før nye buybacks; en manglende eller senere status-forgænger
|
||||||
|
giver en tydelig konstruktionsfejl uden implicit oprettelse eller omordning.
|
||||||
|
|
||||||
Efter validering konstruerer og starter Hub'en Registry, registrerer det som `nenjim.registry.service` og registrerer
|
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
|
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
|
processer. Først herefter behandles resten af `[AUTO_START]` i den angivne rækkefølge, begyndende med Journal. Hver
|
||||||
|
|||||||
@@ -0,0 +1,271 @@
|
|||||||
|
# Phoenix Perps read-only integration
|
||||||
|
|
||||||
|
`PhoenixPerpsService`, `PhoenixPerpsAccount`, `PhoenixPerpsPosition`, `PhoenixAccountRisk`, `PhoenixTwapOrder` and `PhoenixTwapWarning` live in
|
||||||
|
`com.r35157.libs.phoenix.perps`; the REST implementation is in `impl.ref`.
|
||||||
|
They contain no Evelyn/UI types or signing/trading operations. NenjimHub creates
|
||||||
|
one adapter without a portfolio cache as `phoenix.perps.default` and injects it into both Evelyn
|
||||||
|
Perps processes. Each process owns its own wallet list, refresh worker and snapshot.
|
||||||
|
The adapter also receives the existing `SolanaBlockChain` for local trader PDA
|
||||||
|
derivation only; risk lookup does not use its RPC or signing operations.
|
||||||
|
See [Evelyn](Evelyn.md#12-perps-service-og-mission-control) for consumer behavior and
|
||||||
|
[the canonical system overview](../../r35157_docs/projects/evelyn/README.md#evelynperpsservice)
|
||||||
|
for configuration and deployment status.
|
||||||
|
|
||||||
|
## REST reads and account model
|
||||||
|
|
||||||
|
The implementation uses these official endpoints at `https://perp-api.phoenix.trade`:
|
||||||
|
|
||||||
|
1. [`GET /v1/trader/state/{authority}?traderPdaIndex=0`](https://docs.phoenix.trade/api/trader/get-trader-state)
|
||||||
|
returns the wallet authority's version-1 snapshot and subaccounts. The
|
||||||
|
[account model](https://docs.phoenix.trade/sdk/accounts) currently uses trader PDA
|
||||||
|
index 0 for user accounts. Subaccount 0 is cross-margin; other returned subaccounts
|
||||||
|
are isolated. `getAccounts()` returns actual balances separately from positions;
|
||||||
|
`getOpenPositions()` remains a convenience flattening of those accounts. Keep funded
|
||||||
|
empty Cross accounts in the overview and include only nonzero `basePositionLots`
|
||||||
|
as position rows. An omitted `positions` field is documented as optional and
|
||||||
|
means empty; null or a wrong type is rejected.
|
||||||
|
2. [`GET /v1/view/exchange/markets`](https://docs.phoenix.trade/api/exchange/list-markets)
|
||||||
|
supplies each symbol's `baseLotsDecimals`, `tickSize`, leverage tiers and taker fee.
|
||||||
|
3. [`GET /v1/market/{symbol}/mark-price`](https://docs.phoenix.trade/api/exchange/get-mark-price)
|
||||||
|
supplies `markPrice.price` in USD. The API permits an absent mark price; a held
|
||||||
|
position remains visible with unknown price-dependent metrics when the mark is absent.
|
||||||
|
A present nonpositive or malformed price still fails the lookup.
|
||||||
|
4. [`GET /v1/trader/{authority}/twap?traderPdaIndex=0`](https://github.com/Ellipsis-Labs/rise-public/blob/master/ts/src/api/traders/client.ts)
|
||||||
|
supplies TWAP snapshots for every wallet with existing subaccounts, including accounts
|
||||||
|
without positions. Filled outstanding orders become matched progress or warning observations;
|
||||||
|
zero-fill and completed orders remain invisible. See [TWAP observations](#twap-observations).
|
||||||
|
5. [`GET /v1/view/trader/{pubkey}`](https://docs.phoenix.trade/api/trader/get-trader)
|
||||||
|
supplies risk for each visible account, including funded empty Cross accounts.
|
||||||
|
Its key is the derived trader account, not the wallet or a TWAP key. See
|
||||||
|
[venue-reported account risk](#venue-reported-account-risk).
|
||||||
|
|
||||||
|
A trader state with no subaccounts needs only the first request. Otherwise TWAP is
|
||||||
|
read immediately after state, with its original response receipt time preserved.
|
||||||
|
Metadata is fetched once if there are held positions or filled outstanding TWAPs;
|
||||||
|
marks are fetched only for distinct held symbols, followed by each visible account's
|
||||||
|
risk read. Registered empty accounts without relevant TWAPs need no metadata or
|
||||||
|
marks; funded or warning-only empty Cross accounts also receive their risk read.
|
||||||
|
Request starts are spaced at least one second apart across both environments to avoid
|
||||||
|
bursts of per-market reads. Each HTTP request has a 10-second timeout and uses the
|
||||||
|
Hub's existing HTTP client (no redirects by default). On HTTP 429 the adapter respects
|
||||||
|
`Retry-After` (seconds or HTTP date; 60 seconds if missing/invalid). Further calls
|
||||||
|
fail fast during that shared cooldown, letting Jupiter and other wallet attempts
|
||||||
|
continue; the normal refresh cycle retries later. There are no automatic
|
||||||
|
retries, new API keys or authentication/signing flows. Public reads worked during
|
||||||
|
verification, despite generated API security annotations referring to bearer auth.
|
||||||
|
HTTP failures, including 401/403/404/429, are errors rather than empty portfolios.
|
||||||
|
The shared pacing follows Phoenix's [rate-limit guidance](https://docs.phoenix.trade/sdk/ws-api-best-practices);
|
||||||
|
it does not guarantee a fixed quota.
|
||||||
|
|
||||||
|
## Units, precision and identity
|
||||||
|
|
||||||
|
The official [SDK's `baseLotsToBaseUnits`](https://github.com/Ellipsis-Labs/rise-public/blob/master/ts/src/units.ts)
|
||||||
|
uses `lots / 10 ** baseLotsDecimals`; the exponent may be negative. The adapter uses:
|
||||||
|
|
||||||
|
```text
|
||||||
|
quantity = BigDecimal(basePositionLots).scaleByPowerOfTen(-baseLotsDecimals)
|
||||||
|
sizeUsd = abs(quantity) * markPrice.price
|
||||||
|
```
|
||||||
|
|
||||||
|
All lot counts use `BigInteger`; conversion and multiplication use `BigDecimal`
|
||||||
|
without a rounding context. Jackson parses JSON price numbers directly as decimal,
|
||||||
|
without a `double` intermediate. For example, 2,144 SOL lots at `baseLotsDecimals=2`
|
||||||
|
are 21.44 SOL; at a mark price of 104.45 USD, notional is 2,239.408 USD. Entry price,
|
||||||
|
collateral, virtual quote lots and margin are not substitutes for current notional.
|
||||||
|
Rounding to cents occurs only in the EMC display formatter. Margin and estimated
|
||||||
|
exit fees additionally follow the venue rounding described below.
|
||||||
|
|
||||||
|
Within each wallet's Phoenix venue, identity is `traderPdaIndex:subaccountIndex:symbol`.
|
||||||
|
Sequence numbers do not affect identity. Rows sort by PDA index, subaccount index
|
||||||
|
and symbol; labels use `SYMBOL-PERP`. Isolated rows remain directly beneath the venue.
|
||||||
|
Cross positions belong beneath their explicit `Cross` account group, which supersedes
|
||||||
|
the former no-subaccount-level restriction from issue #99. Positive
|
||||||
|
quantity means Long and negative means Short. Thus two SOL subaccounts remain two
|
||||||
|
rows even though their labels match.
|
||||||
|
|
||||||
|
Authority/PDA identity, snapshot version, arrays, symbols, duplicate subaccounts or
|
||||||
|
positions, integer lots, metadata and prices are validated. A malformed lookup fails
|
||||||
|
as a whole; it never publishes a partially decoded portfolio. The shared Jackson
|
||||||
|
mapper is copied so strict parsing does not change other integrations.
|
||||||
|
|
||||||
|
## Refresh behavior and limitations
|
||||||
|
|
||||||
|
Each Evelyn process performs an initial background refresh, then waits 60 seconds
|
||||||
|
following each completed cycle. Jupiter and Phoenix are attempted independently for
|
||||||
|
each configured wallet. Successful results replace only that wallet/venue; failures
|
||||||
|
log wallet and venue and retain its last successful result. A valid empty result
|
||||||
|
replaces that venue's positions and account balances. EMC hides the venue heading
|
||||||
|
when it has neither position rows, Cross accounts nor TWAP warnings; its read status remains in
|
||||||
|
the wallet tooltip and the scope still contributes to snapshot totals.
|
||||||
|
Funded Cross accounts and Cross accounts with eligible warnings remain visible when they no longer hold positions. Snapshot reads never fetch
|
||||||
|
external data; EMC's three-second polling remains unchanged.
|
||||||
|
|
||||||
|
Positions and prices are separate REST reads and can belong to different slots.
|
||||||
|
This is a periodic current-notional estimate, not an atomic exchange valuation.
|
||||||
|
A cycle's duration includes all sequential reads and request pacing before the
|
||||||
|
60-second pause (32 active markets and one visible account need at least about 35 seconds for their reads). Failed
|
||||||
|
reads can leave old data visible indefinitely. A visible venue shows `(retained)` after
|
||||||
|
a failed refresh; cell tooltips include the last-success time. Childless venues are hidden,
|
||||||
|
with failed or pending first-read status available in their wallet's tooltip. Such unknown
|
||||||
|
scopes cannot contribute fabricated zero totals.
|
||||||
|
Trader PDA indices other than 0, streaming and position management are out of scope.
|
||||||
|
|
||||||
|
## Venue-reported account risk
|
||||||
|
|
||||||
|
The [official address derivation](https://github.com/Ellipsis-Labs/rise-public/blob/master/ts/src/pdas.ts)
|
||||||
|
uses program `EtrnLzgbS7nMMy5fbD42kXiUzGg8XQzJ972Xtk1cjWih` and seeds
|
||||||
|
`trader`, authority bytes, a one-byte PDA index and a one-byte subaccount index.
|
||||||
|
The adapter uses the existing local Solana derivation, validates the returned
|
||||||
|
authority/trader key/PDA/subaccount, and verifies the nonzero position membership
|
||||||
|
and signed quantities against trader state. A fill during the separate reads can
|
||||||
|
therefore cause a retained failed observation rather than attach risk to mismatched
|
||||||
|
position state. Lookup works without any active TWAP.
|
||||||
|
|
||||||
|
TraderView returns `riskState`, `riskTier`, `effectiveCollateral`,
|
||||||
|
`maintenanceMargin` and position `liquidationPrice`. The actual deployed amount
|
||||||
|
objects contain integer `value` and `decimals`; the reader computes exact decimal
|
||||||
|
amounts with decimal exponents from -18 through 18 and ignores formatted `ui` strings.
|
||||||
|
Positive liquidation prices are
|
||||||
|
retained; `-1`, zero, other nonpositive values and absent prices are unavailable.
|
||||||
|
They are not zero risk or infinite safety. Optional missing fields and unsupported
|
||||||
|
future tiers cannot silently become Safe. Malformed amounts, identity mismatches
|
||||||
|
and rejected HTTP reads fail the complete wallet/venue observation.
|
||||||
|
|
||||||
|
Cross risk belongs to the shared account. The reported effective collateral can
|
||||||
|
include funding, discounted positive PnL and collateral haircuts; it is distinct
|
||||||
|
from the overview's undiscounted equity and estimated closing Value. Risk therefore
|
||||||
|
can be available even where the older capital valuation cannot value spot collateral.
|
||||||
|
The reported thresholds and tier are preserved without inventing a liquidation engine.
|
||||||
|
See [Phoenix account health](https://docs.phoenix.trade/phoenix/margin-and-risk/account-health)
|
||||||
|
and [margin math](https://docs.phoenix.trade/phoenix/margin-and-risk/margin-math).
|
||||||
|
|
||||||
|
Account risk carries its original response receipt time. Isolated risk combines
|
||||||
|
that account's reported liquidation price with its already fetched Phoenix mark;
|
||||||
|
its observation time is the older receipt. Prices and account data are separate
|
||||||
|
reads and can lag the chain. Existing failures retain original timestamps. The
|
||||||
|
[Risk presentation](Evelyn.md#risiko-i-perps) defines five-minute expiration,
|
||||||
|
neutral isolated distance, venue tier colors and group summaries.
|
||||||
|
|
||||||
|
## TWAP observations
|
||||||
|
|
||||||
|
TWAP data is matched by requested authority, PDA index, subaccount and market `assetId`
|
||||||
|
using Phoenix metadata. Only an existing nonzero position carries a progressbar, but
|
||||||
|
an eligible order that cannot be matched is preserved in its account's immutable
|
||||||
|
warning list. The order itself must have `baseLotsFilled > 0` and remaining children.
|
||||||
|
A new order without fills stays invisible everywhere, even if the market already
|
||||||
|
has a position; completed orders also create no warning. No synthetic position or
|
||||||
|
monetary group total is created. A successful snapshot without that active order
|
||||||
|
clears its progress and warning data. A failed or invalid TWAP read fails the whole wallet/venue observation,
|
||||||
|
retaining the previous complete snapshot and its original timestamps as `(retained)`.
|
||||||
|
|
||||||
|
Warnings retain the TWAP address/order sequence, asset and subaccount IDs, raw
|
||||||
|
execution/fill counters and original receipt time. Missing market metadata or no
|
||||||
|
matching open position is an observed mismatch, not proof of delisting, cancellation
|
||||||
|
or stopped execution. Missing conversion data is shown as raw lots/unknown values;
|
||||||
|
warning-only markets do not trigger mark-price requests. A matched order with no
|
||||||
|
usable future next-execution estimate keeps its progress plus a timing warning.
|
||||||
|
Account/schema/counter mismatches still fail the read; valid missing-market warnings
|
||||||
|
do not discard independent positions or account risk. See the [EMC warning presentation](Evelyn.md#twap-advarsler)
|
||||||
|
for exact placement, ancestor propagation and retained/stale qualifications.
|
||||||
|
|
||||||
|
The [SDK snapshot](https://github.com/Ellipsis-Labs/rise-public/blob/master/ts/src/api/traders/twap.ts)
|
||||||
|
reports `nChildOrders` as the total execution count including dust. With explicit
|
||||||
|
`nDustOrders > 0`, regular count is total minus dust count. Legacy zero/omitted dust
|
||||||
|
count with a positive `dustOrderSize` replaces one final regular child with dust.
|
||||||
|
Total quantity is regular count × regular lot size plus dust count × dust lot size;
|
||||||
|
the next size switches to dust when the regular children have executed. Convert all
|
||||||
|
sizes and cumulative `baseLotsFilled` with `baseLotsDecimals`, using exact arithmetic.
|
||||||
|
If size/dust metadata is unavailable, sizes remain unknown while independent counters
|
||||||
|
and fills remain available. Execution progress is executed/total children; it does
|
||||||
|
not assert that the planned quantity has filled completely.
|
||||||
|
|
||||||
|
Time estimates use the current nominal **200 ms per Solana slot**, documented by the
|
||||||
|
[Solana Foundation in October 2026](https://solana.com/fr/upgrades/reduced-slot-times).
|
||||||
|
The older 400 ms assumption no longer applies. At receipt of the TWAP response:
|
||||||
|
|
||||||
|
```text
|
||||||
|
estimated next = observation time + (dueSlot - snapshot.slot) × 200 ms
|
||||||
|
final slot = dueSlot + (childOrdersRemaining - 1) × cooldownSlots
|
||||||
|
estimated end = observation time + (final slot - snapshot.slot) × 200 ms
|
||||||
|
```
|
||||||
|
|
||||||
|
Receipt time is an approximate anchor; API lag, variable slot times and delayed
|
||||||
|
execution can shift the estimates. No separate RPC sampling or live timer is used.
|
||||||
|
Already-due schedules yield unavailable estimates, as do overflow or expiry before
|
||||||
|
the scheduled child/completion. `lastValidSlot` is only an expiry boundary.
|
||||||
|
|
||||||
|
EMC appends **TWAP** after uPnL. Its progressbar overlays a snapshot duration with
|
||||||
|
unbounded total hours, such as `≈ 28:32:10` or `≈ 128:32:10`. It updates only with new
|
||||||
|
snapshots and is explicitly as of the tooltip's `Updated` time. Due/unavailable
|
||||||
|
timing shows `Due / unknown`; a failed venue shows `Retained` instead of a fresh
|
||||||
|
countdown. Cells without relevant progress or warnings stay empty. Groups show warning
|
||||||
|
summaries when applicable, never aggregated progressbars. Tooltips show progress, `Estimated completion`, total/next/fill
|
||||||
|
quantities, different dust sizes where relevant, observation time and limitations.
|
||||||
|
|
||||||
|
Total and next-child USD values reuse that position's already fetched Phoenix mark,
|
||||||
|
independently of the Ticker. They are approximate current notionals, excluding fees,
|
||||||
|
not historical spend or a promise about future execution cost. An absent mark keeps
|
||||||
|
token quantities visible with unavailable USD values; affected exposure, margin,
|
||||||
|
PnL/equity/Value and dependent totals are unavailable rather than zero.
|
||||||
|
|
||||||
|
|
||||||
|
## Valuations, funding and margin
|
||||||
|
|
||||||
|
Monetary arithmetic uses exact `BigDecimal`/`BigInteger` values. Quote lots are
|
||||||
|
1e-6 USD. The [official SDK computation](https://github.com/Ellipsis-Labs/rise-public/blob/master/ts/src/margin/compute.ts)
|
||||||
|
and [PnL definition](https://docs.phoenix.trade/phoenix/margin-and-risk/entry-price-pnl)
|
||||||
|
separate price PnL from funding and fees:
|
||||||
|
|
||||||
|
```text
|
||||||
|
price uPnL = quantity × markPrice + virtualQuotePositionLots / 1e6
|
||||||
|
funding = unsettledFundingQuoteLots / 1e6 (signed; positive adds equity)
|
||||||
|
equity = collateral + sum(price uPnL + funding)
|
||||||
|
exit fee = ceil_to_micro_USD(abs(quantity) × markPrice × takerFee × REST takerFeeOverrideMultiplier)
|
||||||
|
Value = equity - sum(estimated exit fees)
|
||||||
|
```
|
||||||
|
|
||||||
|
The REST override is already a decimal multiplier (observed 0.9), not the raw
|
||||||
|
on-chain i8 representation. Missing override, fee, funding or virtual quote data
|
||||||
|
makes affected valuations unavailable, rather than defaulting to zero. Settled
|
||||||
|
trading fees and `accumulatedFundingQuoteLots` are not subtracted again; collateral
|
||||||
|
already reflects settlement. Equity is undiscounted: no positive-uPnL risk discount
|
||||||
|
or spot-collateral haircut is substituted for owned capital.
|
||||||
|
|
||||||
|
`collateral` is quote collateral in micro-USD. Each nonzero `spotCollaterals.balance`
|
||||||
|
currently makes collateral/equity/Value unavailable: this version does not have a
|
||||||
|
verified spot index-price valuation. It never quietly ignores SOL or another
|
||||||
|
collateral asset. Zero spot balances do not change quote-only valuations. Missing
|
||||||
|
collateral is also unavailable. Independent price PnL/exposure remain computable.
|
||||||
|
|
||||||
|
For a Cross position the Collateral column is its **initial margin requirement**,
|
||||||
|
not a share of the account balance. The implementation follows the
|
||||||
|
[SDK margin math](https://github.com/Ellipsis-Labs/rise-public/blob/master/ts/src/margin/math.ts)
|
||||||
|
and [integer price conversion](https://github.com/Ellipsis-Labs/rise-public/blob/master/ts/src/units.ts):
|
||||||
|
|
||||||
|
```text
|
||||||
|
markTicks = floor(markPrice × 10^(6-baseLotsDecimals) / tickSize)
|
||||||
|
grossQuoteLots = abs(basePositionLots) × markTicks × tickSize
|
||||||
|
L = first-tier leverage, or truncated linear interpolation of leverage between
|
||||||
|
the surrounding maxSizeBaseLots bounds; beyond the final bound use 1
|
||||||
|
initialMarginUsd = ceil(grossQuoteLots / L) / 1e6
|
||||||
|
```
|
||||||
|
|
||||||
|
Interpolation truncates the complete positive leverage, including when leverage
|
||||||
|
is decreasing. For 150 lots between (100 lots, 10×) and (200 lots, 5×), L is 7,
|
||||||
|
not 7.5 or 8. At 100 USD and exponent 2, margin is 21.428572 USD. Exposure retains
|
||||||
|
the exact original REST mark, preserving issue #99's current-notional meaning.
|
||||||
|
|
||||||
|
Cross account leverage is gross exposure / positive undiscounted equity before
|
||||||
|
estimated exit fees. Cross position leverage and ROI are unavailable because the
|
||||||
|
venue's displayed conventions have not been verified; the UI explains this and
|
||||||
|
shows dollar PnL. Isolated leverage/ROI use actual collateral. Cross position
|
||||||
|
Value is inapplicable; Cross account Value counts all shared capital once,
|
||||||
|
including free collateral when its last position closes. Other aggregate ratios
|
||||||
|
are blank. Risk separately displays reported maintenance-margin utilization
|
||||||
|
against effective collateral. It is not total initial-margin utilization, free
|
||||||
|
buying power or the ratio used for the Leverage column.
|
||||||
|
|
||||||
|
Value is an estimate at separately read reference prices, not an executable quote.
|
||||||
|
Slippage, price impact, swaps, network fees, account rent, future funding and
|
||||||
|
unrelated wallet balances are excluded. No profit cap is applied to Phoenix.
|
||||||
|
Unsupported/missing values and any higher totals requiring them show `—`.
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
schema: spec-driven
|
||||||
|
created: 2026-10-06
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# Design
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
See proposal.md for the operational need. The burner already reloads EVE after its independent buyback stage, retains known burn signatures before finalization, and prioritizes pending burns. It can reuse supported-program and mint-supply precision validation already used for input selection. Monetary values and CurrencyType are immutable.
|
||||||
|
|
||||||
|
## Goals / Non-Goals
|
||||||
|
|
||||||
|
**Goals:** Preserve a configured EVE reserve and accumulate small buys/deposits until a representable burn amount qualifies, retaining existing transaction and notification safety.
|
||||||
|
|
||||||
|
**Non-Goals:** Fixed burn batches, maximum burn size, a new configuration framework, notification batching, altered buybacks, persistent reconciliation or live deployment.
|
||||||
|
|
||||||
|
## Decisions
|
||||||
|
|
||||||
|
1. Add `minimumBurnAmount` and `eveReserve` MoneyAmount constructor arguments beside input bounds. Validate non-null amount/currency/UUID, canonical EVE UUID, positive minimum and non-negative reserve before startup, copying into canonical EVE MoneyAmount values. Stale display metadata is accepted by UUID. A separate record or ValueTag adds no needed domain safety.
|
||||||
|
2. Reload EVE after buybacks. Subtract the reserve and skip non-positive available balance, then resolve EVE mint precision using its detected token program. Round DOWN and compare that actual amount to the exact unrounded minimum. Skip non-positive/below-minimum amounts before price lookup/submission. Rounding configuration up or down would alter the owner's reserve/minimum; instead any fractional remainder stays in the wallet.
|
||||||
|
3. Submit all eligible EVE above reserve, using the same amount for price multiplication and PendingBurn. Keep pending-burn priority and awaiting unchanged so later thresholds/balances never rebuild an unresolved burn.
|
||||||
|
4. The owner chose production minimum **0.1 EVE** and reserve **0.01 EVE** on 2026-10-06. Supply both using canonical EVE in the production Hub. Issue #93's 1/0.1 values remain independent constructor acceptance examples, not production defaults.
|
||||||
|
|
||||||
|
## Risks / Trade-offs
|
||||||
|
|
||||||
|
- [EVE mint metadata unavailable] → Postpone burning without notification, using existing warning and interruption behavior; buybacks have already been evaluated independently.
|
||||||
|
- [Configuration precision exceeds mint precision] → Round only the eligible burn amount DOWN; compare to exact minimum and retain at least the exact reserve.
|
||||||
|
- [Unknown transaction outcome] → Preserve original signature and existing process-local suspension/reconciliation behavior. Restart still requires external reconciliation.
|
||||||
|
|
||||||
|
## Migration Plan
|
||||||
|
|
||||||
|
Compile the reference sources and update the production construction call together. Verify controlled threshold, precision, accumulation, direct-deposit, validation, pending-state and notification cases offline, synchronize specs and archive. Deployment is a separate task; no private deployment files or live transactions are touched. Rollback requires restoring the prior constructor and Hub call together, after reconciling uncertain transactions.
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# Proposal
|
||||||
|
|
||||||
|
## Why
|
||||||
|
|
||||||
|
Small Evelyn buybacks currently create tiny burns and excessive proof notifications. Accumulating EVE for larger burns while retaining a wallet reserve supports predictable Evelyn operation without changing its small buybacks.
|
||||||
|
|
||||||
|
## What Changes
|
||||||
|
|
||||||
|
- Add independently configured EVE `MoneyAmount` values for a strictly positive burn minimum and non-negative retained reserve.
|
||||||
|
- Burn the entire balance above reserve, rounded down to mint precision, only when that actual amount reaches the minimum.
|
||||||
|
- Keep buybacks independent and preserve pending transaction reconciliation and successful FINALIZED proof delivery using the actual burned amount.
|
||||||
|
- Configure production with the owner's chosen minimum 0.1 EVE and reserve 0.01 EVE; retain issue #93's 1/0.1 examples as acceptance cases.
|
||||||
|
- **BREAKING**: Reference implementation constructor requires the two burn policy amounts.
|
||||||
|
|
||||||
|
## Capabilities
|
||||||
|
|
||||||
|
### New Capabilities
|
||||||
|
|
||||||
|
None.
|
||||||
|
|
||||||
|
### Modified Capabilities
|
||||||
|
|
||||||
|
- `evelyn-iou-burner-service`: EVE burn policy configuration/validation, precision-aware eligibility above reserve, submitted/notification amounts and production settings.
|
||||||
|
|
||||||
|
## Impact
|
||||||
|
|
||||||
|
Reference burner implementation and its domain JavaDoc, production Hub constructor, Evelyn configuration documentation and directly affected OpenSpec requirements. No new ValueTags, administration API, buyback policy, scheduling, deployment changes or permanent tests. Verification uses controlled offline dependencies without live transactions.
|
||||||
+125
@@ -0,0 +1,125 @@
|
|||||||
|
# Spec Delta
|
||||||
|
|
||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: Independent EVE burn policy configuration
|
||||||
|
The constructor SHALL accept minimumBurnAmount and eveReserve as EVE-denominated MoneyAmount values identified by canonical EVE UUID regardless of supplied display metadata. Both values, their amounts, currencies and UUIDs SHALL be non-null; minimum SHALL be strictly positive and reserve non-negative. Zero reserve SHALL be valid. Invalid burn configuration SHALL fail before worker startup without changing existing input validation.
|
||||||
|
|
||||||
|
#### Scenario: Invalid burn configuration
|
||||||
|
- **WHEN** either value, amount, currency or UUID is null, a UUID is not EVE, the minimum is zero/negative, or the reserve is negative
|
||||||
|
- **THEN** construction fails before creating a worker or transaction
|
||||||
|
|
||||||
|
#### Scenario: Stale EVE metadata and zero reserve
|
||||||
|
- **WHEN** both values carry the EVE UUID with stale names/symbols and the reserve is zero
|
||||||
|
- **THEN** construction succeeds and uses canonical EVE identity with the configured exact numeric amounts
|
||||||
|
|
||||||
|
## MODIFIED Requirements
|
||||||
|
|
||||||
|
### Requirement: Complete visible EVE balance is eligible
|
||||||
|
After the optional swap attempt, the service SHALL detect the fixed EVE mint's supported token program internally and reload the wallet's complete available EVE balance. A missing EVE mint account, unsupported program, absent wallet token account, zero balance, or negative/invalid balance SHALL cause no burn or notification in that iteration. The service SHALL subtract its configured EVE reserve, clamp negative available amounts to zero and round down to validated EVE mint precision using the detected token program. It SHALL submit a new burn only when the actual rounded amount is positive and at least the configured minimum burn amount; equality qualifies. It SHALL burn the entire eligible amount above reserve, without a maximum or fixed batch size, including EVE deposited before the iteration or visible after a failed or absent swap. Below threshold or with unusable mint precision it SHALL retain the EVE and send no burn notification. This threshold SHALL NOT suppress independent buybacks.
|
||||||
|
|
||||||
|
#### Scenario: Existing EVE without buyback
|
||||||
|
- **WHEN** no swap is performed but the wallet contains a positive EVE balance
|
||||||
|
- **THEN** the service evaluates that complete balance under the EVE reserve, precision and burn-minimum rules
|
||||||
|
|
||||||
|
#### Scenario: EVE account is absent or empty
|
||||||
|
- **WHEN** the wallet has no EVE token account or its complete balance is zero
|
||||||
|
- **THEN** the iteration submits no burn and sends no notification
|
||||||
|
|
||||||
|
#### Scenario: Issue example threshold and reserve
|
||||||
|
- **WHEN** minimum is 1 EVE and reserve is 0.1 EVE
|
||||||
|
- **THEN** balances 0, 0.05, 0.1, 1.05 and 1.099999 produce no burn; balance 1.1 burns 1 and balance 1.35 burns 1.25, retaining 0.1 after success
|
||||||
|
|
||||||
|
#### Scenario: Accumulation across small buybacks
|
||||||
|
- **WHEN** individually eligible small buybacks accumulate EVE over multiple iterations
|
||||||
|
- **THEN** each below-threshold iteration still permits its buyback and sends no burn message; the first qualifying iteration burns all eligible EVE and subsequent accumulation starts with the retained reserve
|
||||||
|
|
||||||
|
#### Scenario: Exact configuration exceeds mint precision
|
||||||
|
- **WHEN** six-decimal mint precision rounds available 1.0000009 to 1.000000 while minimum is 1.0000001
|
||||||
|
- **THEN** no burn occurs; no amount is rounded upward and the reserve is never consumed to reach the minimum
|
||||||
|
|
||||||
|
### Requirement: Fresh EVE value is required before submission
|
||||||
|
Immediately before a new burn, the service SHALL obtain the latest canonical EVE/USDT observation and require a positive price, non-null observation timestamp, and age no greater than `maximumPriceAge` milliseconds. USDT SHALL be treated as USD for notification value, calculated as the actual rounded eligible EVE amount above reserve multiplied by that fresh price. If the pair is unsupported or the observation is missing, invalid, or stale, the service SHALL warn, postpone the burn, send no notification, and retry only by re-evaluating state in a later scheduled iteration.
|
||||||
|
|
||||||
|
#### Scenario: Fresh EVE price exists
|
||||||
|
- **WHEN** a positive rounded EVE amount at or above the burn minimum and sufficiently fresh positive EVE/USDT observation are available
|
||||||
|
- **THEN** the service calculates the notification's USD value from that actual eligible burn amount, excluding reserve before submitting the burn
|
||||||
|
|
||||||
|
#### Scenario: EVE price is stale
|
||||||
|
- **WHEN** EVE is present but its observation is older than the configured age limit
|
||||||
|
- **THEN** the service leaves EVE unburned and sends no notification in that iteration
|
||||||
|
|
||||||
|
### Requirement: Single complete-balance burn submission and pending state
|
||||||
|
For a positive rounded EVE amount at or above the minimum with a usable price, the service SHALL call the wallet burn operation exactly once with the fixed EVE mint and entire eligible rounded amount above reserve. It SHALL NOT automatically retry, rebuild, re-sign, or resubmit within or across iterations. Immediately after a non-blank signature is returned, it SHALL retain process-local pending data containing that signature, actual submitted EVE amount excluding reserve, and its calculated notification value or complete notification text before awaiting the signature.
|
||||||
|
|
||||||
|
If burn submission fails without a usable signature and its outcome may be unknown, including an I/O or interruption path whose contract permits post-submission uncertainty, the service SHALL conservatively suspend further burn submissions for the current process. It MAY continue ordinary iteration work that does not submit another burn. Recovery SHALL require external reconciliation followed by a complete process restart, not a Burner service stop/start operation.
|
||||||
|
|
||||||
|
#### Scenario: Burn returns a signature
|
||||||
|
- **WHEN** the wallet submits the entire eligible rounded amount above reserve and returns a non-blank signature
|
||||||
|
- **THEN** pending state is retained before the service begins finalization waiting
|
||||||
|
|
||||||
|
#### Scenario: Submission outcome may be unknown without a signature
|
||||||
|
- **WHEN** burn submission fails or is interrupted without providing a usable signature and may already have reached Solana
|
||||||
|
- **THEN** the service performs no further burn submission until external reconciliation and a complete process restart
|
||||||
|
|
||||||
|
#### Scenario: Pending burn bypasses new threshold evaluation
|
||||||
|
- **WHEN** a retained burn signature remains pending while balance is now below the minimum or at the reserve
|
||||||
|
- **THEN** only the original signature is awaited, its original submitted amount/value are preserved and it is never re-evaluated, rebuilt or resubmitted
|
||||||
|
|
||||||
|
### Requirement: Exact token-capped amount and per-currency reserve
|
||||||
|
The service SHALL select the lesser of positive `balance - minimumReserve` and the input UUID's maximum token amount, then round downward to mint precision. It SHALL skip non-positive results and actual rounded amounts below that UUID's minimum, continuing to later inputs in the same iteration without counting a swap attempt. Equality SHALL qualify. It SHALL never round upward, top up from reserves or exceed either existing bound. Input selection SHALL require no input/USDC Ticker observation. Minima and maxima SHALL constrain buyback inputs only; EVE burns SHALL remain uncapped and governed independently by their own EVE minimum and reserve. Native SOL reserves SHALL use observed balance without estimating future fees.
|
||||||
|
|
||||||
|
#### Scenario: SyrupUSDC maximum is lower than spendable balance
|
||||||
|
- **WHEN** SyrupUSDC balance is 0.5, its reserve is 0.095, its minimum is 0.045 and its maximum is 0.2
|
||||||
|
- **THEN** 0.2 SyrupUSDC is selected without requesting a SyrupUSDC/USDC observation
|
||||||
|
|
||||||
|
#### Scenario: SyrupUSDC spendable balance is lower than the maximum
|
||||||
|
- **WHEN** SyrupUSDC balance is 0.245, its reserve is 0.095, its minimum is 0.045 and its maximum is 0.2
|
||||||
|
- **THEN** 0.150 SyrupUSDC is selected before downward mint-precision rounding
|
||||||
|
|
||||||
|
#### Scenario: Reserve consumes the balance
|
||||||
|
- **WHEN** the current balance is equal to or below that entry's minimum reserve
|
||||||
|
- **THEN** no amount of that currency is intentionally swapped
|
||||||
|
|
||||||
|
#### Scenario: USDT maximum preserves the production bound
|
||||||
|
- **WHEN** USDT balance is 0.35, its reserve is 0.1, its minimum is 0.05 and its maximum is 0.25
|
||||||
|
- **THEN** 0.25 USDT is selected before downward mint-precision rounding
|
||||||
|
|
||||||
|
#### Scenario: Precision rounding never increases the spend
|
||||||
|
- **WHEN** the lesser of spendable balance and maximum is 0.1509999, the mint supports six decimals and its minimum is no greater than 0.150999
|
||||||
|
- **THEN** the service selects 0.150999, and if downward rounding instead produces zero the input is skipped
|
||||||
|
|
||||||
|
#### Scenario: Native transaction fees
|
||||||
|
- **WHEN** native SOL is selected with a configured reserve
|
||||||
|
- **THEN** the service enforces the reserve against the observed balance only and does not estimate fees, even though later fees may reduce the final balance or cause execution to fail
|
||||||
|
|
||||||
|
#### Scenario: USDT below and at its minimum
|
||||||
|
- **WHEN** USDT reserve is 0.1 and minimum is 0.05
|
||||||
|
- **THEN** balances 0.14 and 0.149999 are skipped, while balance 0.15 selects exactly 0.05
|
||||||
|
|
||||||
|
#### Scenario: Tiny first input permits useful later input
|
||||||
|
- **WHEN** USDT balance is 0.100001 with reserve 0.1/minimum 0.05, and SyrupUSDC balance is 0.502048 with reserve 0.095/minimum 0.045/maximum 0.2
|
||||||
|
- **THEN** the USDT remainder is skipped and 0.2 SyrupUSDC is the only attempted swap
|
||||||
|
|
||||||
|
#### Scenario: SyrupUSDC minimum boundary
|
||||||
|
- **WHEN** SyrupUSDC reserve is 0.095 and minimum is 0.045
|
||||||
|
- **THEN** balance 0.14 selects exactly 0.045 while 0.139999 is skipped
|
||||||
|
|
||||||
|
#### Scenario: Actual rounded candidate falls below a fractional minimum
|
||||||
|
- **WHEN** six-decimal input precision rounds candidate 0.0500009 down to 0.050000 and its minimum is 0.0500001
|
||||||
|
- **THEN** the candidate is skipped without rounding up or using reserves
|
||||||
|
|
||||||
|
#### Scenario: Input minima do not suppress direct EVE burn
|
||||||
|
- **WHEN** every configured input is below its minimum but the wallet contains EVE with a usable EVE/USDT price
|
||||||
|
- **THEN** the complete EVE balance is still evaluated and its rounded eligible amount above the EVE reserve is burned if it meets the EVE burn minimum
|
||||||
|
|
||||||
|
### Requirement: Production burner accepts USDT and SyrupUSDC
|
||||||
|
The production Hub SHALL configure USDT first with reserve 0.1, minimum input 0.05 and maximum input 0.25 USDT, and SyrupUSDC second with reserve 0.095, minimum input 0.045 and maximum input 0.2 SyrupUSDC. These SHALL be token-denominated limits without a USD-value guarantee. The production EVE burn minimum SHALL be 0.1 EVE and its retained reserve SHALL be 0.01 EVE, as selected by the owner on 2026-10-06, configured as MoneyAmount values using the canonical EVE UUID. The existing burner wallet, Jupiter route selection, registration, hourly interval, slippage, EVE/USDT freshness, confirmation settings and burn recovery behavior SHALL be preserved without new Ticker sources or history storage.
|
||||||
|
|
||||||
|
#### Scenario: Production inputs and settings
|
||||||
|
- **WHEN** the Hub constructs the production burner
|
||||||
|
- **THEN** it supplies the exact reserves, minima and maxima as MoneyAmount values in USDT-then-SyrupUSDC priority, supplies minimum burn 0.1 EVE/reserve 0.01 EVE and retains the other existing production arguments
|
||||||
|
|
||||||
|
#### Scenario: Production burn threshold
|
||||||
|
- **WHEN** production EVE balance is below 0.11, exactly 0.11, or 0.135 with usable mint precision and fresh price
|
||||||
|
- **THEN** below 0.11 produces no burn, 0.11 burns 0.1 and 0.135 burns 0.125; successful burns retain 0.01 EVE
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
# Tasks
|
||||||
|
|
||||||
|
## 1. EVE burn policy
|
||||||
|
|
||||||
|
- [x] 1.1 Add canonical EVE MoneyAmount minimum/reserve constructor validation and constructor JavaDoc; verify invalid/null/wrong UUID values fail before worker/transaction activity, stale metadata works and zero reserve is valid in controlled offline checks.
|
||||||
|
- [x] 1.2 Replace full-balance submission with reserve-aware downward precision rounding and actual-minimum qualification; update domain JavaDoc and verify issue boundaries, rounding, direct deposits, accumulation, independent buybacks and actual pending/notification amounts with controlled dependencies.
|
||||||
|
|
||||||
|
## 2. Production configuration
|
||||||
|
|
||||||
|
- [x] 2.1 Supply owner's selected minimum 0.1 EVE and reserve 0.01 EVE in the production Hub, update Evelyn docs and verify source arguments plus production 0.11 threshold/0.135 balance examples offline.
|
||||||
|
|
||||||
|
## 3. Integration and completion
|
||||||
|
|
||||||
|
- [x] 3.1 Compile relevant sources, verify pending burn/swap reconciliation and suspension regressions without live transactions, and record results in verification.md; run strict OpenSpec validation and git diff --check.
|
||||||
|
- [x] 3.2 Synchronize directly affected main specification, verify all delta blocks match while unrelated content survives, run strict validation and archive completed artifacts/tasks.
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
# Verification
|
||||||
|
|
||||||
|
Verified on 2026-10-06 with `/usr/local/software/java/jfx-25`.
|
||||||
|
|
||||||
|
## Compilation and controlled acceptance checks
|
||||||
|
|
||||||
|
- `JAVA_HOME=/usr/local/software/java/jfx-25 ./gradlew compileJava compileTestJava --offline --no-daemon` → **BUILD SUCCESSFUL in 6s**; normal Detag and both source compilations executed. The initial sandbox attempt could not create Gradle's existing cache lock outside the workspace; the same compilation passed with approved cache access.
|
||||||
|
- Temporary harness sources in `/tmp/evelyn-issue93` compiled with `javac --enable-preview --release 25 -cp 'build/classes/java/main:build/libs/*' -d /tmp/evelyn-issue93`.
|
||||||
|
- `java --enable-preview -cp '/tmp/evelyn-issue93:build/classes/java/main:build/libs/*' Issue93Verification` → **PASS: 235 focused burn-policy assertions**.
|
||||||
|
- Same invocation for the copied `Issue92Verification` → **PASS: 89 buyback-minimum regression assertions**.
|
||||||
|
- Same invocation for the copied `Issue91Verification`, restricted to its burner checks → **PASS: 63 pending-swap/recovery regression assertions**. Unchanged RPC/Jupiter library checks were not rerun.
|
||||||
|
- Production source check confirms canonical EVE minimum **0.1** and reserve **0.01** in the correct constructor order and preserved input bounds, hourly interval, freshness and finalization arguments.
|
||||||
|
|
||||||
|
All wallet, transaction, chain, pricing and notification dependencies are controlled. No real RPC, signing or live financial operation occurs. No permanent automated tests were added. The older temporary harness copies add only a positive EVE burn minimum of 0.000001 and zero reserve to their constructor calls, retaining their original buyback/recovery expectations; original temporary files are preserved.
|
||||||
|
|
||||||
|
The #93 checks cover every issue #93 1/0.1 example and production 0.1/0.01 boundaries, numerical equality across decimal scales, zero reserve, fractional minimum above the rounded candidate, fractional reserve preservation, zero after rounding, both SPL Token and Token-2022 precision, missing/unsupported mint data, supply/mint/program mismatch, invalid decimals, I/O and interruption, missing/negative balance, stale price, repeated small buyback accumulation and subsequent reserve-based accumulation, direct deposits with absent/below-minimum inputs, no burn maximum, failed/unknown buybacks, original pending signature and amount/value across timeout/I/O/interruption, failed finalization without notification, once-only notification after delayed finalization, delivery failure and signature-less burn suspension with continued buybacks. Null/invalid numeric configuration, wrong/unknown UUIDs and stale EVE metadata are checked before worker/transaction activity; CurrencyType itself rejects null UUIDs before burner configuration.
|
||||||
|
|
||||||
|
An initial temporary harness compile failed because its notification-error fixture used Throwable rather than the transport's IOException contract; the fixture was corrected. Its default successful swap output was also corrected from zero to a positive amount, preventing fixture-only result validation failures. The final checks above pass against the unchanged production implementation.
|
||||||
|
|
||||||
|
## Review and workflow
|
||||||
|
|
||||||
|
Parent independent source/design/documentation and harness review found no material findings. Two delta scenario wording fixes were applied to state the actual eligible amount and grammatically separate the new production burn settings. Scope is limited to constructor burn policy, EVE eligibility/actual amount use, JavaDoc, production arguments, Evelyn docs and their specification. Buyback limits, priority, pending recovery, lifecycle, Jupiter, Ticker, ValueTags and private deployment files are preserved.
|
||||||
|
|
||||||
|
Strict active-change validation and `git diff --check` passed before synchronization. Final strict validation of the active change and synchronized main specification passed. Six delta requirement blocks match the main specification exactly; 12 untouched requirements remain byte-for-byte intact. The purpose now describes accumulated, threshold-qualified burns above reserve. Existing long-requirement informational hints remain because modified requirement blocks stay whole.
|
||||||
|
|
||||||
|
An initial temporary synchronization matcher allowed a requirement title to span lines and copied an invalid delta header into the main specification. Strict validation caught this before archive. The matcher was corrected and the main-spec edit rebuilt from the clean starting version plus the six intended blocks, then compared for exact delta matching and untouched-content preservation. Final strict validation passes.
|
||||||
|
|
||||||
|
All four planning artifacts and all five tasks are complete. The completed change is archived at `openspec/changes/archive/2026-10-06-93-add-minimum-eve-burn-and-reserve`. After archival, `openspec list --json` reports no active changes, and `openspec validate --archived --json --report findings` passed for all **31** archived changes with **0 failures**. Final whitespace validation also passes.
|
||||||
|
|
||||||
|
No deployment, push, Gitea write or live integration was performed.
|
||||||
+2
@@ -0,0 +1,2 @@
|
|||||||
|
schema: spec-driven
|
||||||
|
created: 2026-10-06
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
# Design
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
See proposal.md for motivation and the two delta specifications for required behavior. The burner already selects one rounded, reserve/minimum/maximum-qualified input, invokes Jupiter at most once, and then independently reloads EVE for burn evaluation. Its fresh canonical EVE/USDT reader is also used for burn valuation. The Hub constructs processes in the manually configured order and rejects unavailable process dependencies.
|
||||||
|
|
||||||
|
## Goals / Non-Goals
|
||||||
|
|
||||||
|
**Goals:** Insert the purchase condition at the last point before a new Jupiter attempt, use the corresponding live reference API, and retain independent burn and pending-transaction behavior.
|
||||||
|
|
||||||
|
**Non-Goals:** A general price-policy framework, another clock/configuration/freshness option, duplicated curve calculations, stored-history decisions, or changes to transaction execution, Ticker sources, lifecycle and deployment selection.
|
||||||
|
|
||||||
|
## Decisions
|
||||||
|
|
||||||
|
1. Add a required `EvelynStatusService` constructor dependency beside Ticker. Production composition uses the existing `requireProcessDependency` guard for `evelyn.status.prod`. The Hub does not infer or start dependencies; operators must place Ticker and Production status before the burner. Constructing a private reference calculator in the burner would duplicate ownership and risk policy drift.
|
||||||
|
2. After the first eligible rounded input is selected, reuse `findFreshPrice(eveUsdtTradingPair)`, obtain the reference through `getReferencePrice(Instant.now())`, validate its canonical pair and positive amount, and compare with `BigDecimal.compareTo`. This avoids division/index rounding and keeps the check close to submission. Checking before input selection would age the decision during balance/mint work; history/chart reads would use stale sample time.
|
||||||
|
3. A rejected condition returns only from `attemptAtMostOneSwap`, preserving the unconditional later `processCompleteEveBalance`. No other input can bypass a globally rejected EVE price. Missing/invalid reference or runtime lookup failure produces a safe warning; an at/above comparison logs the values and decision instant. No suspension state is set. Burn valuation independently reads its existing fresh price and never depends on reference availability.
|
||||||
|
4. Pending burn and swap paths stay ahead of input selection. Reconciliation is read-only regardless of price, and only a newly eligible purchase after reconciliation invokes the gate. Amount limits, precision, unknown-outcome handling and notifications remain as existing contracts define.
|
||||||
|
|
||||||
|
## Risks / Trade-offs
|
||||||
|
|
||||||
|
- [Market movement after observation] → Use the existing maximum price age and the last purchase-selection point; Jupiter execution and slippage rules remain unchanged.
|
||||||
|
- [Reference service failure] → Skip the purchase for this iteration, keep burn processing and re-evaluate next time; log exception type without arbitrary provider text.
|
||||||
|
- [Missing deployment predecessor] → Fail clearly through existing Hub dependency validation; document order without editing private deployment configuration.
|
||||||
|
- [Unknown transaction after restart] → Retain current pending-state/restart contracts; the price gate cannot replace reconciliation.
|
||||||
|
|
||||||
|
## Migration Plan
|
||||||
|
|
||||||
|
Compile and verify offline, then review the change. A later separately authorized deployment must have `assetaz.ticker.default` and `evelyn.status.prod` before a selected `evelyn.iou-burner.prod`. This implementation request changes no live configuration, performs no transactions and deploys nothing. Rollback restores the preceding application version and its constructor composition; uncertain transactions must first be reconciled under the existing restart rules.
|
||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
# Proposal
|
||||||
|
|
||||||
|
## Why
|
||||||
|
|
||||||
|
Issue #94 requires Evelyn IOU buybacks to reduce supply specifically while the Evelyn Price Index is below zero. The burner currently buys whenever an input is eligible, so it needs the existing reference-price policy before submitting a new purchase while preserving independent EVE burning.
|
||||||
|
|
||||||
|
## What Changes
|
||||||
|
|
||||||
|
- Permit new buybacks only with a fresh canonical EVE/USDT price strictly below the corresponding Evelyn status service's reference price at the current decision instant.
|
||||||
|
- Skip only the purchase when either price is unusable or the market price is at/above reference; continue existing EVE burn processing and re-evaluate next iteration.
|
||||||
|
- Preserve pending-burn priority, read-only pending-swap reconciliation, at most one new swap, and all existing amount, reserve, precision and transaction rules.
|
||||||
|
- Inject the Production Evelyn status service through the existing Hub composition, documenting its explicit startup dependency and the purchase-only policy.
|
||||||
|
- Update constructor/domain JavaDoc and operating documentation and verify the boundaries and recovery paths offline.
|
||||||
|
|
||||||
|
## Capabilities
|
||||||
|
|
||||||
|
### New Capabilities
|
||||||
|
|
||||||
|
None.
|
||||||
|
|
||||||
|
### Modified Capabilities
|
||||||
|
|
||||||
|
- `evelyn-iou-burner-service`: Gate new purchases on the existing reference-price API while keeping reconciliation and burns independent.
|
||||||
|
- `nenjim-hub-configuration`: Supply the corresponding Production status service to the burner using existing explicit dependency ordering.
|
||||||
|
|
||||||
|
## Impact
|
||||||
|
|
||||||
|
The burner constructor gains an `EvelynStatusService` dependency, supplied by `NenjimHubImpl`. Runtime input configuration, schedule, Ticker sources, history, ValueTags and live deployment configuration remain outside this change. Verification uses controlled offline collaborators without financial transactions or permanent test additions.
|
||||||
+71
@@ -0,0 +1,71 @@
|
|||||||
|
# Spec Delta
|
||||||
|
|
||||||
|
## MODIFIED Requirements
|
||||||
|
|
||||||
|
### Requirement: At most one confirmed swap attempt per iteration
|
||||||
|
After selecting a positive exact amount and satisfying the current below-reference price condition, the service SHALL invoke the existing Jupiter exact-input operation once with the selected CIS-resolved input mint, fixed EVE output mint, selected amount, and configured slippage. Normal return SHALL be treated as already `CONFIRMED` and SHALL NOT trigger a second confirmation wait. Once invoked, no later input currency SHALL be attempted in that iteration regardless of success or failure, and the service SHALL proceed to reload the complete EVE balance whenever processing was not interrupted.
|
||||||
|
|
||||||
|
A structured definitive on-chain swap failure SHALL be logged without suspending later-process swaps. A timed-out outcome or known-signature confirmation communication error SHALL retain process-local pending swap signature, canonical input, attempted amount and original validity metadata for later read-only reconciliation. Proven pre-submission errors SHALL permit a fresh attempt in a later scheduled iteration. An ambiguous post-submission error without a trustworthy signature SHALL retain manual suspension. Interruption SHALL preserve available pending context and propagate normally. No unknown outcome SHALL cause automatic rebuilding, re-signing or resubmission; pending swaps SHALL still permit existing EVE burn evaluation.
|
||||||
|
|
||||||
|
#### Scenario: First swap fails definitively
|
||||||
|
- **WHEN** the first attempted input returns a definitive on-chain failure
|
||||||
|
- **THEN** no second input is attempted in that iteration, the EVE balance is still reloaded, and a later iteration may attempt a new swap
|
||||||
|
|
||||||
|
#### Scenario: Swap outcome is unknown
|
||||||
|
- **WHEN** the attempted swap times out, throws an I/O failure that permits unknown post-submission status, or is interrupted
|
||||||
|
- **THEN** no equivalent or alternative swap is attempted while its outcome is unresolved; a known signature is checked in later iterations and a signature-less ambiguous submission requires manual reconciliation
|
||||||
|
|
||||||
|
#### Scenario: Swap returns normally
|
||||||
|
- **WHEN** Jupiter returns a successful result whose transaction already reached `CONFIRMED`
|
||||||
|
- **THEN** the burner performs no additional confirmation wait and reloads the wallet instead of deriving its burn amount only from the returned output amount
|
||||||
|
|
||||||
|
### Requirement: Production burner accepts USDT and SyrupUSDC
|
||||||
|
The production Hub SHALL configure USDT first with reserve 0.1, minimum input 0.05 and maximum input 0.25 USDT, and SyrupUSDC second with reserve 0.095, minimum input 0.045 and maximum input 0.2 SyrupUSDC. These SHALL be token-denominated limits without a USD-value guarantee. The production EVE burn minimum SHALL be 0.1 EVE and its retained reserve SHALL be 0.01 EVE, as selected by the owner on 2026-10-06, configured as MoneyAmount values using the canonical EVE UUID. The production Hub SHALL supply its corresponding Production Evelyn status service for current reference-price decisions. The existing burner wallet, Jupiter route selection, registration, hourly interval, slippage, EVE/USDT freshness, confirmation settings and burn recovery behavior SHALL be preserved without new Ticker sources or history storage.
|
||||||
|
|
||||||
|
#### Scenario: Production inputs and settings
|
||||||
|
- **WHEN** the Hub constructs the production burner
|
||||||
|
- **THEN** it supplies the exact reserves, minima and maxima as MoneyAmount values in USDT-then-SyrupUSDC priority, supplies minimum burn 0.1 EVE/reserve 0.01 EVE and the Production status service, and retains the other existing production arguments
|
||||||
|
|
||||||
|
#### Scenario: Production burn threshold
|
||||||
|
- **WHEN** production EVE balance is below 0.11, exactly 0.11, or 0.135 with usable mint precision and fresh price
|
||||||
|
- **THEN** below 0.11 produces no burn, 0.11 burns 0.1 and 0.135 burns 0.125; successful burns retain 0.01 EVE
|
||||||
|
|
||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: New buybacks require a below-reference market price
|
||||||
|
An otherwise eligible new buyback SHALL require a fresh canonical EVE/USDT observation within the existing maximum price age and a usable canonical EVE/USDT reference price from the injected corresponding Evelyn status service at the current decision instant. Both prices SHALL be positive. Only actual price strictly below reference SHALL permit submission; equality SHALL not qualify. The existing public reference operation SHALL be reused without copying curve calculations or reading stored history.
|
||||||
|
|
||||||
|
#### Scenario: Strict boundary below reference
|
||||||
|
- **WHEN** an input is otherwise eligible and reference price is 15 USDT/EVE
|
||||||
|
- **THEN** market price 14.99 permits the existing buyback, while 15.00 and 15.01 submit no new swap from any input in that iteration
|
||||||
|
|
||||||
|
#### Scenario: Required prices are unusable
|
||||||
|
- **WHEN** market observation is missing, stale, incomplete, unavailable or has an invalid pair/non-positive price, or reference lookup throws, is missing or has an invalid pair/non-positive price
|
||||||
|
- **THEN** no new swap is submitted and a safe diagnostic explains why the purchase was skipped
|
||||||
|
|
||||||
|
#### Scenario: Reference uses the current decision instant
|
||||||
|
- **WHEN** the first usable input is ready for a new swap
|
||||||
|
- **THEN** the corresponding status service is asked for its reference price at the current instant rather than using a sampled history point or the observation timestamp
|
||||||
|
|
||||||
|
### Requirement: Price skips preserve independent burn processing and recovery
|
||||||
|
A rejected buyback price condition SHALL skip purchases only, continue complete EVE-balance processing under the existing independent burn requirements, and SHALL NOT create a suspension. Each subsequent otherwise eligible iteration SHALL re-evaluate prices. Pending-burn priority and read-only pending-swap reconciliation SHALL remain independent of the new-purchase condition; any newly permitted purchase after reconciliation SHALL pass the current price check.
|
||||||
|
|
||||||
|
#### Scenario: Existing EVE burns at or above reference
|
||||||
|
- **WHEN** market price is equal to or above reference and existing EVE satisfies its own reserve, minimum, precision, fresh valuation and transaction requirements
|
||||||
|
- **THEN** no new swap occurs and the existing eligible EVE is burned normally
|
||||||
|
|
||||||
|
#### Scenario: Reference unavailable but burn valuation is usable
|
||||||
|
- **WHEN** reference lookup is unavailable or invalid but existing EVE and its fresh canonical valuation satisfy the burn requirements
|
||||||
|
- **THEN** the purchase is skipped and the independent eligible burn still occurs
|
||||||
|
|
||||||
|
#### Scenario: Next scheduled iteration recovers
|
||||||
|
- **WHEN** one iteration skips a purchase because its prices do not qualify and a later otherwise eligible iteration has usable prices below reference
|
||||||
|
- **THEN** the later iteration can attempt its one new swap without a restart or manual reset
|
||||||
|
|
||||||
|
#### Scenario: Pending swap is resolved above reference
|
||||||
|
- **WHEN** a pending swap resolves read-only at or above reference
|
||||||
|
- **THEN** it is cleared normally, any otherwise eligible new purchase is price-checked and skipped, and existing EVE burn evaluation continues
|
||||||
|
|
||||||
|
#### Scenario: Unresolved swap and pending burn retain priority
|
||||||
|
- **WHEN** a swap remains unresolved or the iteration starts with a pending burn
|
||||||
|
- **THEN** existing transaction-priority rules apply without evaluating a new-purchase price condition or submitting a new swap
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
# Spec Delta
|
||||||
|
|
||||||
|
## MODIFIED Requirements
|
||||||
|
|
||||||
|
### Requirement: Selected processes are constructed and started lazily in exact order
|
||||||
|
The Hub SHALL support the modern process IDs `nenjim.registry.service`, `nenjim.journal.service`, `assetaz.ticker.default`, `jupiter-perps-alarm.default`, `evelyn.status.prod`, `evelyn.status.test`, `evelyn.iou-burner.prod`, `evelyn.liquidity.prod`, `nenjim.javafx-runtime.service`, `evelyn.mission-control.default`, and `nenjim.process-manager.default` through implementation-only construction capability that creates no process instance during validation. After passive composition, the Hub SHALL visit every configured entry after Registry in declared order, beginning with Journal. At each entry it SHALL construct only that process and any explicitly composed process-specific passive dependencies or administration view, register the process under the exact configured ID, resolve it through the canonical `NenjimProcess` contract where applicable, call `start()`, and record the ID only after successful return.
|
||||||
|
|
||||||
|
The production IOU burner SHALL receive the already constructed Production Evelyn status service, and SHALL require both Ticker and `evelyn.status.prod` to precede `evelyn.iou-burner.prod`. The configuration SHALL be the complete manually ordered process dependency closure. The Hub SHALL not discover, insert, recursively construct, reorder, or automatically start dependencies. A process omitted from `[AUTO_START]` SHALL not be constructed, registered, started, or recorded, even when its implementation is available. Legacy process prototypes outside the modern Hub graph SHALL not be selectable.
|
||||||
|
|
||||||
|
#### Scenario: Configured process starts successfully
|
||||||
|
- **WHEN** a configured process entry is reached and its dependencies are already available
|
||||||
|
- **THEN** that process is constructed, registered, started, and recorded before the next entry is reached
|
||||||
|
|
||||||
|
#### Scenario: Process is omitted
|
||||||
|
- **WHEN** a known process ID does not appear in `[AUTO_START]`
|
||||||
|
- **THEN** no instance of that process is constructed or registered and its lifecycle is never started or recorded
|
||||||
|
|
||||||
|
#### Scenario: Jupiter Perps Alarm is selected
|
||||||
|
- **WHEN** `jupiter-perps-alarm.default` is explicitly listed after its required predecessors
|
||||||
|
- **THEN** the otherwise inactive modern alarm process is constructed, registered, started and recorded at that exact position
|
||||||
|
|
||||||
|
#### Scenario: Dependency order is invalid
|
||||||
|
- **WHEN** a selected process is reached before a process dependency required by its current constructor or startup behavior
|
||||||
|
- **THEN** construction or startup fails clearly without implicit dependency construction, repair, or reordering
|
||||||
|
|
||||||
|
#### Scenario: JavaFX processes are omitted
|
||||||
|
- **WHEN** a valid headless plan omits `nenjim.javafx-runtime.service`, `evelyn.mission-control.default`, and `nenjim.process-manager.default`
|
||||||
|
- **THEN** none of those objects is constructed or registered and the JavaFX toolkit remains uninitialized by the Hub
|
||||||
|
|
||||||
|
#### Scenario: Renamed status services retain dependency wiring
|
||||||
|
- **WHEN** `evelyn.status.prod` and `evelyn.status.test` are listed after Ticker and before Mission Control
|
||||||
|
- **THEN** the Hub constructs separate Production/Test status services, registers the renamed IDs, and injects the corresponding typed service views into Mission Control in the configured order
|
||||||
|
|
||||||
|
#### Scenario: Deployment still contains an old status ID
|
||||||
|
- **WHEN** a private deployment configuration contains `evelyn.service.prod` or `evelyn.service.test`
|
||||||
|
- **THEN** validation rejects the unknown ID before startup; deployment migration replaces only that ID with `evelyn.status.prod` or `evelyn.status.test`, preserving selection and order
|
||||||
|
|
||||||
|
#### Scenario: Liquidity is explicitly selected
|
||||||
|
- **WHEN** `evelyn.liquidity.prod` is reached in a valid startup plan
|
||||||
|
- **THEN** the Hub loads its runtime configuration, binds the dedicated signer/wallet and Jupiter service to each other, constructs direct Raydium access and registers/starts the liquidity process without inserting a Ticker or status dependency or reordering the plan
|
||||||
|
|
||||||
|
#### Scenario: Production burner has its reference-price dependency
|
||||||
|
- **WHEN** `evelyn.iou-burner.prod` is reached after Ticker and `evelyn.status.prod`
|
||||||
|
- **THEN** the Hub injects the corresponding Production status service for buyback price decisions
|
||||||
|
|
||||||
|
#### Scenario: Production burner precedes its status dependency
|
||||||
|
- **WHEN** the burner is reached without a previously constructed Production status service
|
||||||
|
- **THEN** construction fails clearly without creating or starting the omitted dependency or the burner
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
# Tasks
|
||||||
|
|
||||||
|
## 1. Purchase policy and independent burns
|
||||||
|
|
||||||
|
- [x] 1.1 Require the corresponding Evelyn status service and gate a fully eligible new purchase on fresh canonical actual price strictly below its current reference; verify constructor validation, below/equal/above and unavailable/invalid prices with controlled offline checks.
|
||||||
|
- [x] 1.2 Update constructor/domain JavaDoc and Evelyn operating documentation; verify price skips still burn eligible existing EVE, re-evaluate next iteration and preserve pending burn/swap priority in the same offline checks.
|
||||||
|
|
||||||
|
## 2. Hub composition
|
||||||
|
|
||||||
|
- [x] 2.1 Inject the Production status dependency through the existing Hub guard and document the explicit Ticker/status/burner order; verify production arguments and fail-fast dependency behavior without starting the Hub or editing deployment configuration.
|
||||||
|
|
||||||
|
## 3. Integrated verification
|
||||||
|
|
||||||
|
- [x] 3.1 Compile main and existing test sources through normal Gradle/Detag, execute focused offline purchase/burn/recovery checks and relevant prior burner harnesses, and record their final results without adding permanent tests or running live transactions.
|
||||||
|
- [x] 3.2 Validate the scoped OpenSpec change strictly and check whitespace and changed-file scope; deliver completed planning artifacts and verification evidence for review.
|
||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
# Verification
|
||||||
|
|
||||||
|
Verified on 2026-10-06 with `/usr/local/software/java/jfx-25`.
|
||||||
|
|
||||||
|
## Compilation and controlled acceptance checks
|
||||||
|
|
||||||
|
- `JAVA_HOME=/usr/local/software/java/jfx-25 ./gradlew compileJava compileTestJava --offline --no-daemon` → **BUILD SUCCESSFUL in 8s**. Normal Gradle-integrated Detag and both source compilations executed. The initial sandbox attempt could not create the existing Gradle cache lock outside the workspace; the same compilation passed with approved cache access.
|
||||||
|
- Temporary sources in `/tmp/evelyn-issue94` compiled with `javac --enable-preview --release 25 -cp 'build/classes/java/main:build/libs/*' -d /tmp/evelyn-issue94`.
|
||||||
|
- `java --enable-preview -cp '/tmp/evelyn-issue94:build/classes/java/main:build/libs/*' Issue94Verification` → **PASS: 459 focused issue #94 assertions**.
|
||||||
|
- The copied `Issue93Verification` → **PASS: 246 burn-policy regression assertions**.
|
||||||
|
- The copied `Issue92Verification` → **PASS: 115 input-minimum regression assertions**.
|
||||||
|
- The copied `Issue91Verification`, restricted to burner checks → **PASS: 80 pending-swap/recovery regression assertions**. Unchanged Solana/Jupiter library checks were not rerun.
|
||||||
|
- Production composition source check verifies the corresponding Production status dependency and preserved input reserves/minima/maxima, EVE burn minimum/reserve, hourly interval, slippage, freshness and finalization arguments.
|
||||||
|
- An INFO/WARN-enabled controlled run verifies purchase-skip reasons for at/above reference, unavailable/invalid reference and unusable market price, without arbitrary reference/Ticker exception messages.
|
||||||
|
|
||||||
|
The **900** controlled assertions invoke the actual burner iteration with offline wallet, chain, swap, Ticker, reference and notification collaborators. No RPC, signing, live financial operation, application/Hub startup or deployment configuration read occurs. The Hub dependency guard is invoked directly without constructing a Hub. No permanent automated tests were added. Prior harness copies only add an always-below reference stub to constructor calls and update #92's expected canonical EVE/USDT lookup count for eligible purchases; original temporary files remain intact.
|
||||||
|
|
||||||
|
The #94 harness covers 14.99/15.00/15.01 against reference 15, numeric equality across scales, a close below-reference decimal, the current reference decision instant instead of observation/history time, complete independent burns and reserve preservation, missing/unavailable/non-positive/null-amount/wrong-pair market and reference data, stale market observations, fresh independent burn valuation after a purchase-price failure, stale burn valuation postponement, next-iteration recovery from every rejection category, input priority and amount limits, below-minimum input fallback, no-eligible-input/direct-EVE-only paths, manual swap suspension and null status validation. It verifies original-signature read-only reconciliation while price is above reference, unresolved retention, definitive success/failure/proven expiry followed by a newly rejected purchase, later below-reference recovery, and simultaneous pending burn/swap priority with no reference lookup or new transaction. PriceObservation itself rejects null observation timestamp/price before they can reach the burner.
|
||||||
|
|
||||||
|
An initial temporary harness compilation used the wrong fully qualified component-ID type; it was corrected to the repository's existing `com.r35157.nenjim.service.registry.valuetypes.NenjimComponentId`. Final compiled checks pass. Concurrent initial Java invocations reported harmless performance-data lock warnings; the controlled diagnostic run disabled performance data. No production fix was needed for either verification-only issue.
|
||||||
|
|
||||||
|
## Review and workflow
|
||||||
|
|
||||||
|
Parent independent review of the proposal/specs/design, implementation, operating documentation and controlled harness found no actionable findings. The existing malformed burn JavaDoc line was corrected while updating that same list item. Scope is limited to the required reference dependency, new-purchase condition, Hub composition, related documentation and two OpenSpec capability deltas. Transaction execution, reconciliation, burn valuation/policy, ValueTags, scheduling and private deployment files retain their existing behavior.
|
||||||
|
|
||||||
|
Strict validation of the active change and `git diff --check` pass. All four planning artifact types and all five implementation tasks are complete. The active change is ready for review; main specifications have not been synchronized and the change has not been archived.
|
||||||
|
|
||||||
|
No deployment, commit, push, Gitea write or live integration was performed.
|
||||||
|
|
||||||
|
## Follow-up: clearer purchase-skip logs
|
||||||
|
|
||||||
|
The owner's requested logging refinement keeps price eligibility unchanged. INFO price skips now show a direct labelled comparison, for example `Skipping buyback as EVE price is at or above reference (market=15.01 >= reference=15 USDT/EVE)`, followed by the trading pair, decision instant and next action. Prices use plain decimals without redundant trailing zeros. WARN skips retain their reason; unavailable or invalid reference-price diagnostics also include the known market price.
|
||||||
|
|
||||||
|
`JAVA_HOME=/usr/local/software/java/jfx-25 ./gradlew compileJava --offline --no-daemon` passed. The existing temporary #94 harness passed its 459 assertions again with INFO/WARN enabled. Its captured output in `/tmp/evelyn-issue94/issue94-logging-refinement.log` confirms the numeric comparison for both equality and higher prices, reference failure details, missing/stale market diagnostics and exclusion of arbitrary exception messages. No new tests were added.
|
||||||
|
|
||||||
|
## Finalization
|
||||||
|
|
||||||
|
On 2026-10-06, the owner requested synchronization and archiving. Both capability deltas were merged into the main specifications for `evelyn-iou-burner-service` and `nenjim-hub-configuration`, preserving unrelated requirements and existing purpose sections. Every delta requirement was compared against the resulting main spec, and strict validation of both main specs and the completed change passed; only informational hints about long requirement descriptions were reported.
|
||||||
|
|
||||||
|
The seven completed change files, including `.openspec.yaml` and all five completed tasks, were archived intact to `openspec/changes/archive/2026-10-06-94-gate-buybacks-on-evelyn-reference-price`. OpenSpec reports no remaining active changes. This finalization records the subsequent sync/archive after the initial implementation and logging-refinement verification above.
|
||||||
+2
@@ -0,0 +1,2 @@
|
|||||||
|
schema: spec-driven
|
||||||
|
created: 2026-10-06
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
# Design
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
See proposal.md for motivation and the two delta specifications for required behavior. The burner already selects one rounded, reserve/minimum/maximum-qualified input, invokes Jupiter at most once, and then independently reloads EVE for burn evaluation. Its fresh canonical EVE/USDT reader is also used for burn valuation. The Hub constructs processes in the manually configured order and rejects unavailable process dependencies.
|
||||||
|
|
||||||
|
## Goals / Non-Goals
|
||||||
|
|
||||||
|
**Goals:** Insert the purchase condition at the last point before a new Jupiter attempt, use the corresponding live reference API, and retain independent burn and pending-transaction behavior.
|
||||||
|
|
||||||
|
**Non-Goals:** A general price-policy framework, another clock/configuration/freshness option, duplicated curve calculations, stored-history decisions, or changes to transaction execution, Ticker sources, lifecycle and deployment selection.
|
||||||
|
|
||||||
|
## Decisions
|
||||||
|
|
||||||
|
1. Add a required `EvelynStatusService` constructor dependency beside Ticker. Production composition uses the existing `requireProcessDependency` guard for `evelyn.status.prod`. The Hub does not infer or start dependencies; operators must place Ticker and Production status before the burner. Constructing a private reference calculator in the burner would duplicate ownership and risk policy drift.
|
||||||
|
2. After the first eligible rounded input is selected, reuse `findFreshPrice(eveUsdtTradingPair)`, obtain the reference through `getReferencePrice(Instant.now())`, validate its canonical pair and positive amount, and compare with `BigDecimal.compareTo`. This avoids division/index rounding and keeps the check close to submission. Checking before input selection would age the decision during balance/mint work; history/chart reads would use stale sample time.
|
||||||
|
3. A rejected condition returns only from `attemptAtMostOneSwap`, preserving the unconditional later `processCompleteEveBalance`. No other input can bypass a globally rejected EVE price. Missing/invalid reference or runtime lookup failure produces a safe warning; an at/above comparison logs the values and decision instant. No suspension state is set. Burn valuation independently reads its existing fresh price and never depends on reference availability.
|
||||||
|
4. Pending burn and swap paths stay ahead of input selection. Reconciliation is read-only regardless of price, and only a newly eligible purchase after reconciliation invokes the gate. Amount limits, precision, unknown-outcome handling and notifications remain as existing contracts define.
|
||||||
|
|
||||||
|
## Risks / Trade-offs
|
||||||
|
|
||||||
|
- [Market movement after observation] → Use the existing maximum price age and the last purchase-selection point; Jupiter execution and slippage rules remain unchanged.
|
||||||
|
- [Reference service failure] → Skip the purchase for this iteration, keep burn processing and re-evaluate next time; log exception type without arbitrary provider text.
|
||||||
|
- [Missing deployment predecessor] → Fail clearly through existing Hub dependency validation; document order without editing private deployment configuration.
|
||||||
|
- [Unknown transaction after restart] → Retain current pending-state/restart contracts; the price gate cannot replace reconciliation.
|
||||||
|
|
||||||
|
## Migration Plan
|
||||||
|
|
||||||
|
Compile and verify offline, then review the change. A later separately authorized deployment must have `assetaz.ticker.default` and `evelyn.status.prod` before a selected `evelyn.iou-burner.prod`. This implementation request changes no live configuration, performs no transactions and deploys nothing. Rollback restores the preceding application version and its constructor composition; uncertain transactions must first be reconciled under the existing restart rules.
|
||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
# Proposal
|
||||||
|
|
||||||
|
## Why
|
||||||
|
|
||||||
|
Issue #94 requires Evelyn IOU buybacks to reduce supply specifically while the Evelyn Price Index is below zero. The burner currently buys whenever an input is eligible, so it needs the existing reference-price policy before submitting a new purchase while preserving independent EVE burning.
|
||||||
|
|
||||||
|
## What Changes
|
||||||
|
|
||||||
|
- Permit new buybacks only with a fresh canonical EVE/USDT price strictly below the corresponding Evelyn status service's reference price at the current decision instant.
|
||||||
|
- Skip only the purchase when either price is unusable or the market price is at/above reference; continue existing EVE burn processing and re-evaluate next iteration.
|
||||||
|
- Preserve pending-burn priority, read-only pending-swap reconciliation, at most one new swap, and all existing amount, reserve, precision and transaction rules.
|
||||||
|
- Inject the Production Evelyn status service through the existing Hub composition, documenting its explicit startup dependency and the purchase-only policy.
|
||||||
|
- Update constructor/domain JavaDoc and operating documentation and verify the boundaries and recovery paths offline.
|
||||||
|
|
||||||
|
## Capabilities
|
||||||
|
|
||||||
|
### New Capabilities
|
||||||
|
|
||||||
|
None.
|
||||||
|
|
||||||
|
### Modified Capabilities
|
||||||
|
|
||||||
|
- `evelyn-iou-burner-service`: Gate new purchases on the existing reference-price API while keeping reconciliation and burns independent.
|
||||||
|
- `nenjim-hub-configuration`: Supply the corresponding Production status service to the burner using existing explicit dependency ordering.
|
||||||
|
|
||||||
|
## Impact
|
||||||
|
|
||||||
|
The burner constructor gains an `EvelynStatusService` dependency, supplied by `NenjimHubImpl`. Runtime input configuration, schedule, Ticker sources, history, ValueTags and live deployment configuration remain outside this change. Verification uses controlled offline collaborators without financial transactions or permanent test additions.
|
||||||
+71
@@ -0,0 +1,71 @@
|
|||||||
|
# Spec Delta
|
||||||
|
|
||||||
|
## MODIFIED Requirements
|
||||||
|
|
||||||
|
### Requirement: At most one confirmed swap attempt per iteration
|
||||||
|
After selecting a positive exact amount and satisfying the current below-reference price condition, the service SHALL invoke the existing Jupiter exact-input operation once with the selected CIS-resolved input mint, fixed EVE output mint, selected amount, and configured slippage. Normal return SHALL be treated as already `CONFIRMED` and SHALL NOT trigger a second confirmation wait. Once invoked, no later input currency SHALL be attempted in that iteration regardless of success or failure, and the service SHALL proceed to reload the complete EVE balance whenever processing was not interrupted.
|
||||||
|
|
||||||
|
A structured definitive on-chain swap failure SHALL be logged without suspending later-process swaps. A timed-out outcome or known-signature confirmation communication error SHALL retain process-local pending swap signature, canonical input, attempted amount and original validity metadata for later read-only reconciliation. Proven pre-submission errors SHALL permit a fresh attempt in a later scheduled iteration. An ambiguous post-submission error without a trustworthy signature SHALL retain manual suspension. Interruption SHALL preserve available pending context and propagate normally. No unknown outcome SHALL cause automatic rebuilding, re-signing or resubmission; pending swaps SHALL still permit existing EVE burn evaluation.
|
||||||
|
|
||||||
|
#### Scenario: First swap fails definitively
|
||||||
|
- **WHEN** the first attempted input returns a definitive on-chain failure
|
||||||
|
- **THEN** no second input is attempted in that iteration, the EVE balance is still reloaded, and a later iteration may attempt a new swap
|
||||||
|
|
||||||
|
#### Scenario: Swap outcome is unknown
|
||||||
|
- **WHEN** the attempted swap times out, throws an I/O failure that permits unknown post-submission status, or is interrupted
|
||||||
|
- **THEN** no equivalent or alternative swap is attempted while its outcome is unresolved; a known signature is checked in later iterations and a signature-less ambiguous submission requires manual reconciliation
|
||||||
|
|
||||||
|
#### Scenario: Swap returns normally
|
||||||
|
- **WHEN** Jupiter returns a successful result whose transaction already reached `CONFIRMED`
|
||||||
|
- **THEN** the burner performs no additional confirmation wait and reloads the wallet instead of deriving its burn amount only from the returned output amount
|
||||||
|
|
||||||
|
### Requirement: Production burner accepts USDT and SyrupUSDC
|
||||||
|
The production Hub SHALL configure USDT first with reserve 0.1, minimum input 0.05 and maximum input 0.25 USDT, and SyrupUSDC second with reserve 0.095, minimum input 0.045 and maximum input 0.2 SyrupUSDC. These SHALL be token-denominated limits without a USD-value guarantee. The production EVE burn minimum SHALL be 0.1 EVE and its retained reserve SHALL be 0.01 EVE, as selected by the owner on 2026-10-06, configured as MoneyAmount values using the canonical EVE UUID. The production Hub SHALL supply its corresponding Production Evelyn status service for current reference-price decisions. The existing burner wallet, Jupiter route selection, registration, hourly interval, slippage, EVE/USDT freshness, confirmation settings and burn recovery behavior SHALL be preserved without new Ticker sources or history storage.
|
||||||
|
|
||||||
|
#### Scenario: Production inputs and settings
|
||||||
|
- **WHEN** the Hub constructs the production burner
|
||||||
|
- **THEN** it supplies the exact reserves, minima and maxima as MoneyAmount values in USDT-then-SyrupUSDC priority, supplies minimum burn 0.1 EVE/reserve 0.01 EVE and the Production status service, and retains the other existing production arguments
|
||||||
|
|
||||||
|
#### Scenario: Production burn threshold
|
||||||
|
- **WHEN** production EVE balance is below 0.11, exactly 0.11, or 0.135 with usable mint precision and fresh price
|
||||||
|
- **THEN** below 0.11 produces no burn, 0.11 burns 0.1 and 0.135 burns 0.125; successful burns retain 0.01 EVE
|
||||||
|
|
||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: New buybacks require a below-reference market price
|
||||||
|
An otherwise eligible new buyback SHALL require a fresh canonical EVE/USDT observation within the existing maximum price age and a usable canonical EVE/USDT reference price from the injected corresponding Evelyn status service at the current decision instant. Both prices SHALL be positive. Only actual price strictly below reference SHALL permit submission; equality SHALL not qualify. The existing public reference operation SHALL be reused without copying curve calculations or reading stored history.
|
||||||
|
|
||||||
|
#### Scenario: Strict boundary below reference
|
||||||
|
- **WHEN** an input is otherwise eligible and reference price is 15 USDT/EVE
|
||||||
|
- **THEN** market price 14.99 permits the existing buyback, while 15.00 and 15.01 submit no new swap from any input in that iteration
|
||||||
|
|
||||||
|
#### Scenario: Required prices are unusable
|
||||||
|
- **WHEN** market observation is missing, stale, incomplete, unavailable or has an invalid pair/non-positive price, or reference lookup throws, is missing or has an invalid pair/non-positive price
|
||||||
|
- **THEN** no new swap is submitted and a safe diagnostic explains why the purchase was skipped
|
||||||
|
|
||||||
|
#### Scenario: Reference uses the current decision instant
|
||||||
|
- **WHEN** the first usable input is ready for a new swap
|
||||||
|
- **THEN** the corresponding status service is asked for its reference price at the current instant rather than using a sampled history point or the observation timestamp
|
||||||
|
|
||||||
|
### Requirement: Price skips preserve independent burn processing and recovery
|
||||||
|
A rejected buyback price condition SHALL skip purchases only, continue complete EVE-balance processing under the existing independent burn requirements, and SHALL NOT create a suspension. Each subsequent otherwise eligible iteration SHALL re-evaluate prices. Pending-burn priority and read-only pending-swap reconciliation SHALL remain independent of the new-purchase condition; any newly permitted purchase after reconciliation SHALL pass the current price check.
|
||||||
|
|
||||||
|
#### Scenario: Existing EVE burns at or above reference
|
||||||
|
- **WHEN** market price is equal to or above reference and existing EVE satisfies its own reserve, minimum, precision, fresh valuation and transaction requirements
|
||||||
|
- **THEN** no new swap occurs and the existing eligible EVE is burned normally
|
||||||
|
|
||||||
|
#### Scenario: Reference unavailable but burn valuation is usable
|
||||||
|
- **WHEN** reference lookup is unavailable or invalid but existing EVE and its fresh canonical valuation satisfy the burn requirements
|
||||||
|
- **THEN** the purchase is skipped and the independent eligible burn still occurs
|
||||||
|
|
||||||
|
#### Scenario: Next scheduled iteration recovers
|
||||||
|
- **WHEN** one iteration skips a purchase because its prices do not qualify and a later otherwise eligible iteration has usable prices below reference
|
||||||
|
- **THEN** the later iteration can attempt its one new swap without a restart or manual reset
|
||||||
|
|
||||||
|
#### Scenario: Pending swap is resolved above reference
|
||||||
|
- **WHEN** a pending swap resolves read-only at or above reference
|
||||||
|
- **THEN** it is cleared normally, any otherwise eligible new purchase is price-checked and skipped, and existing EVE burn evaluation continues
|
||||||
|
|
||||||
|
#### Scenario: Unresolved swap and pending burn retain priority
|
||||||
|
- **WHEN** a swap remains unresolved or the iteration starts with a pending burn
|
||||||
|
- **THEN** existing transaction-priority rules apply without evaluating a new-purchase price condition or submitting a new swap
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
# Spec Delta
|
||||||
|
|
||||||
|
## MODIFIED Requirements
|
||||||
|
|
||||||
|
### Requirement: Selected processes are constructed and started lazily in exact order
|
||||||
|
The Hub SHALL support the modern process IDs `nenjim.registry.service`, `nenjim.journal.service`, `assetaz.ticker.default`, `jupiter-perps-alarm.default`, `evelyn.status.prod`, `evelyn.status.test`, `evelyn.iou-burner.prod`, `evelyn.liquidity.prod`, `nenjim.javafx-runtime.service`, `evelyn.mission-control.default`, and `nenjim.process-manager.default` through implementation-only construction capability that creates no process instance during validation. After passive composition, the Hub SHALL visit every configured entry after Registry in declared order, beginning with Journal. At each entry it SHALL construct only that process and any explicitly composed process-specific passive dependencies or administration view, register the process under the exact configured ID, resolve it through the canonical `NenjimProcess` contract where applicable, call `start()`, and record the ID only after successful return.
|
||||||
|
|
||||||
|
The production IOU burner SHALL receive the already constructed Production Evelyn status service, and SHALL require both Ticker and `evelyn.status.prod` to precede `evelyn.iou-burner.prod`. The configuration SHALL be the complete manually ordered process dependency closure. The Hub SHALL not discover, insert, recursively construct, reorder, or automatically start dependencies. A process omitted from `[AUTO_START]` SHALL not be constructed, registered, started, or recorded, even when its implementation is available. Legacy process prototypes outside the modern Hub graph SHALL not be selectable.
|
||||||
|
|
||||||
|
#### Scenario: Configured process starts successfully
|
||||||
|
- **WHEN** a configured process entry is reached and its dependencies are already available
|
||||||
|
- **THEN** that process is constructed, registered, started, and recorded before the next entry is reached
|
||||||
|
|
||||||
|
#### Scenario: Process is omitted
|
||||||
|
- **WHEN** a known process ID does not appear in `[AUTO_START]`
|
||||||
|
- **THEN** no instance of that process is constructed or registered and its lifecycle is never started or recorded
|
||||||
|
|
||||||
|
#### Scenario: Jupiter Perps Alarm is selected
|
||||||
|
- **WHEN** `jupiter-perps-alarm.default` is explicitly listed after its required predecessors
|
||||||
|
- **THEN** the otherwise inactive modern alarm process is constructed, registered, started and recorded at that exact position
|
||||||
|
|
||||||
|
#### Scenario: Dependency order is invalid
|
||||||
|
- **WHEN** a selected process is reached before a process dependency required by its current constructor or startup behavior
|
||||||
|
- **THEN** construction or startup fails clearly without implicit dependency construction, repair, or reordering
|
||||||
|
|
||||||
|
#### Scenario: JavaFX processes are omitted
|
||||||
|
- **WHEN** a valid headless plan omits `nenjim.javafx-runtime.service`, `evelyn.mission-control.default`, and `nenjim.process-manager.default`
|
||||||
|
- **THEN** none of those objects is constructed or registered and the JavaFX toolkit remains uninitialized by the Hub
|
||||||
|
|
||||||
|
#### Scenario: Renamed status services retain dependency wiring
|
||||||
|
- **WHEN** `evelyn.status.prod` and `evelyn.status.test` are listed after Ticker and before Mission Control
|
||||||
|
- **THEN** the Hub constructs separate Production/Test status services, registers the renamed IDs, and injects the corresponding typed service views into Mission Control in the configured order
|
||||||
|
|
||||||
|
#### Scenario: Deployment still contains an old status ID
|
||||||
|
- **WHEN** a private deployment configuration contains `evelyn.service.prod` or `evelyn.service.test`
|
||||||
|
- **THEN** validation rejects the unknown ID before startup; deployment migration replaces only that ID with `evelyn.status.prod` or `evelyn.status.test`, preserving selection and order
|
||||||
|
|
||||||
|
#### Scenario: Liquidity is explicitly selected
|
||||||
|
- **WHEN** `evelyn.liquidity.prod` is reached in a valid startup plan
|
||||||
|
- **THEN** the Hub loads its runtime configuration, binds the dedicated signer/wallet and Jupiter service to each other, constructs direct Raydium access and registers/starts the liquidity process without inserting a Ticker or status dependency or reordering the plan
|
||||||
|
|
||||||
|
#### Scenario: Production burner has its reference-price dependency
|
||||||
|
- **WHEN** `evelyn.iou-burner.prod` is reached after Ticker and `evelyn.status.prod`
|
||||||
|
- **THEN** the Hub injects the corresponding Production status service for buyback price decisions
|
||||||
|
|
||||||
|
#### Scenario: Production burner precedes its status dependency
|
||||||
|
- **WHEN** the burner is reached without a previously constructed Production status service
|
||||||
|
- **THEN** construction fails clearly without creating or starting the omitted dependency or the burner
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
# Tasks
|
||||||
|
|
||||||
|
## 1. Purchase policy and independent burns
|
||||||
|
|
||||||
|
- [x] 1.1 Require the corresponding Evelyn status service and gate a fully eligible new purchase on fresh canonical actual price strictly below its current reference; verify constructor validation, below/equal/above and unavailable/invalid prices with controlled offline checks.
|
||||||
|
- [x] 1.2 Update constructor/domain JavaDoc and Evelyn operating documentation; verify price skips still burn eligible existing EVE, re-evaluate next iteration and preserve pending burn/swap priority in the same offline checks.
|
||||||
|
|
||||||
|
## 2. Hub composition
|
||||||
|
|
||||||
|
- [x] 2.1 Inject the Production status dependency through the existing Hub guard and document the explicit Ticker/status/burner order; verify production arguments and fail-fast dependency behavior without starting the Hub or editing deployment configuration.
|
||||||
|
|
||||||
|
## 3. Integrated verification
|
||||||
|
|
||||||
|
- [x] 3.1 Compile main and existing test sources through normal Gradle/Detag, execute focused offline purchase/burn/recovery checks and relevant prior burner harnesses, and record their final results without adding permanent tests or running live transactions.
|
||||||
|
- [x] 3.2 Validate the scoped OpenSpec change strictly and check whitespace and changed-file scope; deliver completed planning artifacts and verification evidence for review.
|
||||||
+34
@@ -0,0 +1,34 @@
|
|||||||
|
# Verification
|
||||||
|
|
||||||
|
Verified on 2026-10-06 with `/usr/local/software/java/jfx-25`.
|
||||||
|
|
||||||
|
## Compilation and controlled acceptance checks
|
||||||
|
|
||||||
|
- `JAVA_HOME=/usr/local/software/java/jfx-25 ./gradlew compileJava compileTestJava --offline --no-daemon` → **BUILD SUCCESSFUL in 8s**. Normal Gradle-integrated Detag and both source compilations executed. The initial sandbox attempt could not create the existing Gradle cache lock outside the workspace; the same compilation passed with approved cache access.
|
||||||
|
- Temporary sources in `/tmp/evelyn-issue94` compiled with `javac --enable-preview --release 25 -cp 'build/classes/java/main:build/libs/*' -d /tmp/evelyn-issue94`.
|
||||||
|
- `java --enable-preview -cp '/tmp/evelyn-issue94:build/classes/java/main:build/libs/*' Issue94Verification` → **PASS: 459 focused issue #94 assertions**.
|
||||||
|
- The copied `Issue93Verification` → **PASS: 246 burn-policy regression assertions**.
|
||||||
|
- The copied `Issue92Verification` → **PASS: 115 input-minimum regression assertions**.
|
||||||
|
- The copied `Issue91Verification`, restricted to burner checks → **PASS: 80 pending-swap/recovery regression assertions**. Unchanged Solana/Jupiter library checks were not rerun.
|
||||||
|
- Production composition source check verifies the corresponding Production status dependency and preserved input reserves/minima/maxima, EVE burn minimum/reserve, hourly interval, slippage, freshness and finalization arguments.
|
||||||
|
- An INFO/WARN-enabled controlled run verifies purchase-skip reasons for at/above reference, unavailable/invalid reference and unusable market price, without arbitrary reference/Ticker exception messages.
|
||||||
|
|
||||||
|
The **900** controlled assertions invoke the actual burner iteration with offline wallet, chain, swap, Ticker, reference and notification collaborators. No RPC, signing, live financial operation, application/Hub startup or deployment configuration read occurs. The Hub dependency guard is invoked directly without constructing a Hub. No permanent automated tests were added. Prior harness copies only add an always-below reference stub to constructor calls and update #92's expected canonical EVE/USDT lookup count for eligible purchases; original temporary files remain intact.
|
||||||
|
|
||||||
|
The #94 harness covers 14.99/15.00/15.01 against reference 15, numeric equality across scales, a close below-reference decimal, the current reference decision instant instead of observation/history time, complete independent burns and reserve preservation, missing/unavailable/non-positive/null-amount/wrong-pair market and reference data, stale market observations, fresh independent burn valuation after a purchase-price failure, stale burn valuation postponement, next-iteration recovery from every rejection category, input priority and amount limits, below-minimum input fallback, no-eligible-input/direct-EVE-only paths, manual swap suspension and null status validation. It verifies original-signature read-only reconciliation while price is above reference, unresolved retention, definitive success/failure/proven expiry followed by a newly rejected purchase, later below-reference recovery, and simultaneous pending burn/swap priority with no reference lookup or new transaction. PriceObservation itself rejects null observation timestamp/price before they can reach the burner.
|
||||||
|
|
||||||
|
An initial temporary harness compilation used the wrong fully qualified component-ID type; it was corrected to the repository's existing `com.r35157.nenjim.service.registry.valuetypes.NenjimComponentId`. Final compiled checks pass. Concurrent initial Java invocations reported harmless performance-data lock warnings; the controlled diagnostic run disabled performance data. No production fix was needed for either verification-only issue.
|
||||||
|
|
||||||
|
## Review and workflow
|
||||||
|
|
||||||
|
Parent independent review of the proposal/specs/design, implementation, operating documentation and controlled harness found no actionable findings. The existing malformed burn JavaDoc line was corrected while updating that same list item. Scope is limited to the required reference dependency, new-purchase condition, Hub composition, related documentation and two OpenSpec capability deltas. Transaction execution, reconciliation, burn valuation/policy, ValueTags, scheduling and private deployment files retain their existing behavior.
|
||||||
|
|
||||||
|
Strict validation of the active change and `git diff --check` pass. All four planning artifact types and all five implementation tasks are complete. The active change is ready for review; main specifications have not been synchronized and the change has not been archived.
|
||||||
|
|
||||||
|
No deployment, commit, push, Gitea write or live integration was performed.
|
||||||
|
|
||||||
|
## Follow-up: clearer purchase-skip logs
|
||||||
|
|
||||||
|
The owner's requested logging refinement keeps price eligibility unchanged. INFO price skips now show a direct labelled comparison, for example `Skipping buyback as EVE price is at or above reference (market=15.01 >= reference=15 USDT/EVE)`, followed by the trading pair, decision instant and next action. Prices use plain decimals without redundant trailing zeros. WARN skips retain their reason; unavailable or invalid reference-price diagnostics also include the known market price.
|
||||||
|
|
||||||
|
`JAVA_HOME=/usr/local/software/java/jfx-25 ./gradlew compileJava --offline --no-daemon` passed. The existing temporary #94 harness passed its 459 assertions again with INFO/WARN enabled. Its captured output in `/tmp/evelyn-issue94/issue94-logging-refinement.log` confirms the numeric comparison for both equality and higher prices, reference failure details, missing/stale market diagnostics and exclusion of arbitrary exception messages. No new tests were added.
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
schema: spec-driven
|
||||||
|
created: 2026-10-19
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
# Design
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
See proposal.md for motivation. EMC currently constructs matching placeholder tabs under Production and Test in `EvelynMissionControlImpl`. JavaFX and its shared runtime are already available.
|
||||||
|
|
||||||
|
## Goals / Non-Goals
|
||||||
|
|
||||||
|
**Goals:** Isolate the prototype's construction and sample data inside the EMC implementation package; keep the existing environment tab colors.
|
||||||
|
|
||||||
|
**Non-Goals:** Public position APIs, domain/service models, runtime changes, refresh scheduling, or configuration.
|
||||||
|
|
||||||
|
## Decisions
|
||||||
|
|
||||||
|
- Add a package-private Perps tab builder instead of growing the chart-oriented main implementation or introducing service abstractions. Each environment gets a fresh tree, so collapsing one view does not affect the other.
|
||||||
|
- Use JavaFX's native tree table and an invisible expanded root. A private display record holds a label, optional direction, and optional USD size using the existing `ΩAmountΩ` ValueTag (`BigDecimal`). This is a monetary amount expressed in USD by the display contract, not a USDC token amount or Jupiter-specific raw integer. No Detag configuration changes are needed.
|
||||||
|
- Format the numeric size as US currency in the display layer, align it right, and leave group values blank. Disable column sorting to preserve the sample grouping and position order.
|
||||||
|
- Apply faint background colors through the row factory and reset the style when a virtualized row becomes empty or represents a group. Keep native selection styling and readable text by setting the control's inner background colors rather than overriding selected-row backgrounds.
|
||||||
|
- Put a wrapping sample-data notice above the table, including the illustrative status of Phoenix. Use a growing layout so the table follows the available window size.
|
||||||
|
|
||||||
|
## Risks / Trade-offs
|
||||||
|
|
||||||
|
- [Reused rows could retain a position color after collapse or scrolling] → Clear inline styling for groups and empty rows and check it in UI validation.
|
||||||
|
- [Samples could be mistaken for real positions in Production] → Show the explicit sample notice in both environments.
|
||||||
|
- [No graphical display may be available in the execution environment] → Attempt isolated UI verification without starting the operational Hub; record any remaining visual checks honestly.
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# Proposal
|
||||||
|
|
||||||
|
## Why
|
||||||
|
|
||||||
|
Evelyn Mission Control's Perps tab is empty. Issue #95 requests a small local prototype so the owner can evaluate the active-position hierarchy before connecting Evelyn services.
|
||||||
|
|
||||||
|
## What Changes
|
||||||
|
|
||||||
|
- Display an expanded, collapsible wallet → venue → position tree table in the Perps tab.
|
||||||
|
- Include hardcoded Jupiter and Phoenix examples with Long/Short text, readable USD sizes, and faint direction colors on position rows only.
|
||||||
|
- Clearly identify sample data and illustrative Phoenix rows; add no Refresh button, service communication, or simulated updates.
|
||||||
|
|
||||||
|
## Capabilities
|
||||||
|
|
||||||
|
### New Capabilities
|
||||||
|
|
||||||
|
- `evelyn-perps-overview`: EMC's hierarchical overview of perpetual positions, initially displayed with explicitly labeled sample data.
|
||||||
|
|
||||||
|
### Modified Capabilities
|
||||||
|
|
||||||
|
None.
|
||||||
|
|
||||||
|
## Impact
|
||||||
|
|
||||||
|
Only EMC implementation code under `com.r35157.evelyn.emc.impl.ref` and this OpenSpec change. Both Production and Test Perps tabs receive independent sample views. No public API, service, configuration, dependency, or network changes.
|
||||||
|
|
||||||
|
Source: https://git.r35157.com/r35157/com_r35157_nenjim-hubd-impl_ref/issues/95
|
||||||
+37
@@ -0,0 +1,37 @@
|
|||||||
|
# Spec Delta
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Let the owner inspect Evelyn perpetual positions grouped by wallet and venue in Mission Control, beginning with a clearly labeled sample-data prototype.
|
||||||
|
|
||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: Perps overview displays a collapsible position hierarchy
|
||||||
|
The Perps tab SHALL display a tree table with columns `Position`, `Direction`, and `Position size, USD`. The hierarchy SHALL be wallet → venue → position, expanded by default, with collapsible wallet and venue groups. Wallet and venue rows SHALL leave direction and size empty.
|
||||||
|
|
||||||
|
#### Scenario: Open either environment's Perps tab
|
||||||
|
- **WHEN** the owner opens Perps in Production or Test
|
||||||
|
- **THEN** the three named columns and the fully expanded hierarchy are visible
|
||||||
|
- **AND** direction and size appear only on position rows
|
||||||
|
|
||||||
|
#### Scenario: Collapse and expand groups
|
||||||
|
- **WHEN** the owner collapses and then expands a wallet or venue
|
||||||
|
- **THEN** its descendants are hidden and then shown again
|
||||||
|
|
||||||
|
### Requirement: Sample positions communicate direction and USD size
|
||||||
|
The overview SHALL include hardcoded Jupiter and Phoenix venues with multiple sample positions, including both Long and Short directions. Position rows SHALL show `Long` or `Short` text and USD sizes with grouping separators and two decimal places. Long rows SHALL have faint green backgrounds and Short rows faint red backgrounds with readable text; wallet and venue rows SHALL have no direction-based color.
|
||||||
|
|
||||||
|
#### Scenario: Inspect sample positions
|
||||||
|
- **WHEN** the owner inspects Jupiter and Phoenix examples
|
||||||
|
- **THEN** both venues have multiple positions and both directions are demonstrated
|
||||||
|
- **AND** position sizes use readable USD formatting
|
||||||
|
- **AND** only position rows use direction-based backgrounds, with direction also conveyed by text
|
||||||
|
|
||||||
|
### Requirement: Prototype is clearly labeled and entirely local
|
||||||
|
The Perps view SHALL explicitly label its data as sample data and explain that Phoenix rows are illustrative without an existing integration. It SHALL remain entirely within EMC, using no network calls, service changes, service communication, Refresh button, or simulated live updates for the prototype.
|
||||||
|
|
||||||
|
#### Scenario: Inspect the prototype boundary
|
||||||
|
- **WHEN** the Perps tab is displayed
|
||||||
|
- **THEN** sample-data and illustrative-Phoenix labels are visible
|
||||||
|
- **AND** there is no Refresh button or automated update of sample positions
|
||||||
|
- **AND** displaying and interacting with the prototype uses only local EMC data
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# Tasks
|
||||||
|
|
||||||
|
## 1. Local Perps prototype
|
||||||
|
|
||||||
|
- [x] 1.1 Implement the EMC-local expanded wallet/venue/position table, sample notice, USD formatting, and direction colors; verify source against issue #95 including blank group cells and row-style reset.
|
||||||
|
- [x] 1.2 Replace both environments' Perps placeholders with independent sample views; verify integration and compile with the normal Gradle build without starting the operational Hub.
|
||||||
|
|
||||||
|
## 2. Integration verification
|
||||||
|
|
||||||
|
- [x] 2.1 Validate this OpenSpec change strictly and run existing Gradle checks; record results and a visual expand/collapse, selection, and row-reuse check or any unavailable graphical verification in verification.md.
|
||||||
|
- [x] 2.2 Review the final diff for EMC-only implementation scope, absence of network/service changes and Refresh controls, and clean whitespace; leave all changes uncommitted for owner review.
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# Verification
|
||||||
|
|
||||||
|
Verified 2026-10-19 against Gitea issue #95.
|
||||||
|
|
||||||
|
## Build and existing checks
|
||||||
|
|
||||||
|
- `JAVA_HOME=/usr/local/software/java/jfx-25 ./gradlew build` — passed, including normal Detag generation, compilation, packaging, and existing tests.
|
||||||
|
- Existing suite: 42 tests across six suites, zero failures, errors, or skips. No new automated tests were added.
|
||||||
|
- Repeated build after switching the display amount to the existing `ΩAmountΩ` tag — passed; generated Java remained identical and compilation/tests were up-to-date.
|
||||||
|
- `openspec validate 95-display-perps-sample-positions --strict` — passed.
|
||||||
|
- `git diff --check` — passed.
|
||||||
|
|
||||||
|
## Isolated JavaFX inspection
|
||||||
|
|
||||||
|
A temporary preview outside the repository constructed the actual `PerpsTab` in nested Production/Test tabs in a 600 × 400 JavaFX scene, rendered snapshots, changed expansion/selection, and scrolled the table. The operational Hub and its services were not started.
|
||||||
|
|
||||||
|
- Inspected expanded, selected, scrolled, collapsed-group, and Test snapshots. Column headings and USD values are readable; the notice wraps without clipping and identifies Phoenix as illustrative.
|
||||||
|
- Eight positions appear under two wallets and four venue groups (14 visible tree rows when expanded). Both venues have multiple Long/Short samples.
|
||||||
|
- Position rows are faint green/red. Selected rows retain native selection styling and readable direction text.
|
||||||
|
- Collapsing wallet A reduces visible rows from 14 to eight; Test remains at 14 rows, confirming independent trees.
|
||||||
|
- Collapsing all venues leaves six group rows with blank direction/size cells and no residual direction colors after row reuse.
|
||||||
|
- Scrolling exposes the remaining positions with correct colors, directions, and sizes.
|
||||||
|
|
||||||
|
This verifies rendered scenes and programmatic control behavior. Mouse/keyboard operation in the running EMC application was not exercised.
|
||||||
|
|
||||||
|
## Scope review
|
||||||
|
|
||||||
|
Implementation consists of the new package-private EMC `PerpsTab` and one placeholder replacement in `EvelynMissionControlImpl`. Source review confirms no Refresh control, network calls, service changes, sample refresh timeline, public API changes, configuration changes, or dependency changes. Changes remain local and uncommitted for owner review; the OpenSpec change remains active.
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
schema: spec-driven
|
||||||
|
created: 2026-10-19
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# Design
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
See proposal.md for motivation. `PerpsTab` currently builds all sample rows. EMC uses a JavaFX Timeline for chart refresh and stops it on hide. The Hub explicitly constructs, registers, starts and records selected processes in configuration order.
|
||||||
|
|
||||||
|
## Goals / Non-Goals
|
||||||
|
|
||||||
|
**Goals:** Separate domain data from presentation; preserve UI identity; make polling independent of any future integration cadence.
|
||||||
|
|
||||||
|
**Non-Goals:** Exchange dependencies, a data-update worker, trading, changing samples, additional UI controls or a new general lifecycle framework.
|
||||||
|
|
||||||
|
## Decisions
|
||||||
|
|
||||||
|
- Put `EvelynPerpsService`, snapshot records and direction in `com.r35157.evelyn.service.perps`, with implementation in `impl.ref`. Nested wallet/venue/position records use IDs separate from display labels and defensive list copies. Use the existing Amount ValueTag for decimal USD size. UI labels/colors remain in EMC. A UI-shaped response would couple the service to JavaFX; a flat response would duplicate grouping responsibility.
|
||||||
|
- Use one `evelyn.perps.default` process for the unchanged examples shown in both environments. Its start publishes the immutable snapshot, reads before start fail, and a second start fails. No worker is needed for static in-memory data. Separate prod/test instances would add configuration without a current difference in data.
|
||||||
|
- Use a single daemon scheduled worker per visible EMC session, requesting immediately and every three seconds. Reads execute sequentially off JavaFX; completion is dispatched through the injected JavaFX runtime. Hide cancels the worker, and a session token rejects queued obsolete results after reopening. Exceptions are logged and isolated so polling continues. Reuse the existing chart lifecycle unchanged.
|
||||||
|
- Retain a Perps view per environment. Skip equal snapshots completely. Reconcile wallet/venue/position tree items by IDs within their parent, updating only changed row values and child order, preserving retained expansion and selection. Do not rebuild or call table refresh for unchanged data.
|
||||||
|
- Add the service to the Hub catalogue and require it in EMC construction. Update the ignored deployment configuration before EMC; retain the minimal tracked bootstrap example and document the migration.
|
||||||
|
|
||||||
|
## Risks / Trade-offs
|
||||||
|
|
||||||
|
- Late results after hide → cancel the session worker and check its identity on the JavaFX thread before applying.
|
||||||
|
- A slow read → sequential worker prevents overlapping polling calls; current snapshot reads have no I/O.
|
||||||
|
- Runtime configuration is ignored → document the required ID and order for other deployments.
|
||||||
|
- Static samples cannot prove real integration correctness → verify only the requested boundary, appearance and polling behavior.
|
||||||
|
|
||||||
|
## Migration Plan
|
||||||
|
|
||||||
|
Build and validate offline, then run an isolated JavaFX verification without starting the live Hub or financial services. On the next ordinary Hub startup, the modified local autostart list starts Perps before EMC. Other deployments must add the same entry explicitly. Rollback restores the previous code and removes that entry together. Leave the change ready for review, without commit, push, deployment or automatic archive.
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# Proposal
|
||||||
|
|
||||||
|
## Why
|
||||||
|
|
||||||
|
Issue #96 moves ownership of Evelyn's existing Perps sample positions out of EMC into a view-only service. This establishes the data boundary for Evelyn's position overview while preserving its current appearance and avoiding exchange integration work.
|
||||||
|
|
||||||
|
## What Changes
|
||||||
|
|
||||||
|
- Introduce `EvelynPerpsService` with an immutable, consistently identified wallet → venue → position snapshot and a separate reference implementation using the Hub process lifecycle.
|
||||||
|
- Move all existing hardcoded positions into that service, without network access or position-management operations.
|
||||||
|
- Poll the snapshot off the JavaFX thread every three seconds while EMC is visible; retain tree items, expansion and selection when data is unchanged.
|
||||||
|
- Register `evelyn.perps.default`, require it before EMC, and add it to this deployment's ordered autostart configuration.
|
||||||
|
|
||||||
|
## Capabilities
|
||||||
|
|
||||||
|
### New Capabilities
|
||||||
|
|
||||||
|
- `evelyn-perps-service`: View-only, in-memory Perps snapshots with stable domain identifiers and Hub-owned startup.
|
||||||
|
|
||||||
|
### Modified Capabilities
|
||||||
|
|
||||||
|
- `evelyn-perps-overview`: Replace EMC-owned samples with asynchronous lifecycle-bound polling while retaining presentation and view state.
|
||||||
|
- `nenjim-hub-configuration`: Support the Perps process and explicitly ordered injection into EMC.
|
||||||
|
|
||||||
|
## Impact
|
||||||
|
|
||||||
|
Changes the Evelyn service API, EMC constructor and Perps presentation, Hub composition, local ignored runtime configuration, and documentation. No external dependencies, trades, real exchange reads, simulated changes, new controls, commits or pushes.
|
||||||
+33
@@ -0,0 +1,33 @@
|
|||||||
|
# Spec Delta
|
||||||
|
|
||||||
|
## MODIFIED Requirements
|
||||||
|
|
||||||
|
### Requirement: Prototype is clearly labeled and entirely local
|
||||||
|
The Perps view SHALL explicitly label its data as sample data and explain that Phoenix rows are illustrative without an existing integration. It SHALL obtain positions only from the Perps service's current in-memory snapshot, with no external network calls, Refresh button, or simulated position changes. EMC SHALL own all UI components, row colors and formatting.
|
||||||
|
|
||||||
|
#### Scenario: Inspect the prototype boundary
|
||||||
|
- **WHEN** the Perps tab is displayed
|
||||||
|
- **THEN** sample-data and illustrative-Phoenix labels are visible
|
||||||
|
- **AND** there is no Refresh button or simulated update of sample positions
|
||||||
|
- **AND** displaying and interacting with the prototype uses only the service's local sample snapshot
|
||||||
|
|
||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: Asynchronous polling follows window visibility
|
||||||
|
EMC SHALL request the current Perps snapshot immediately on opening and every three seconds while visible, without blocking the UI thread. UI updates SHALL occur only on the JavaFX thread. Hiding the window SHALL stop polling; reopening SHALL create only one active polling loop and ignore results belonging to an earlier window session. Poll failures SHALL preserve the current view and allow subsequent attempts.
|
||||||
|
|
||||||
|
#### Scenario: Read takes time or fails
|
||||||
|
- **WHEN** a snapshot read is delayed or fails
|
||||||
|
- **THEN** the UI remains responsive and the existing view is retained
|
||||||
|
- **AND** later polling remains possible without overlapping scheduled reads
|
||||||
|
|
||||||
|
#### Scenario: Hide and reopen
|
||||||
|
- **WHEN** the owner hides and reopens EMC repeatedly
|
||||||
|
- **THEN** each visible session has only one polling loop and obsolete results cannot update its views
|
||||||
|
|
||||||
|
### Requirement: Polling preserves Perps view state
|
||||||
|
The Perps table SHALL retain matching wallet, venue and position identities across snapshots. Repeated unchanged snapshots SHALL leave existing rows, expansion, selection and formatting untouched without flicker. New groups SHALL be expanded by default, and future added or removed positions SHALL be reconciled by identity without resetting retained groups.
|
||||||
|
|
||||||
|
#### Scenario: Poll unchanged positions
|
||||||
|
- **WHEN** a user collapses a group or selects a position and repeated snapshots are unchanged
|
||||||
|
- **THEN** the same rows remain, with the same expansion and selection and no rebuild or refresh
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
# Spec Delta
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Provide a view-only source of current Evelyn perpetual positions independently of Mission Control presentation and refresh timing.
|
||||||
|
|
||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: Read-only current positions snapshot
|
||||||
|
The Perps service SHALL expose only a read operation returning its complete current in-memory snapshot. Wallets, venues within wallets, and positions within venues SHALL have stable identifiers across reads. Responses SHALL be immutable domain data without UI components, colors or formatting, and SHALL make no Jupiter or Phoenix requests.
|
||||||
|
|
||||||
|
#### Scenario: Repeated reads
|
||||||
|
- **WHEN** clients repeatedly retrieve the started service's snapshot
|
||||||
|
- **THEN** they receive the same complete positions with stable identifiers and cannot mutate service-owned collections
|
||||||
|
- **AND** no exchange requests or position-management operations occur
|
||||||
|
|
||||||
|
### Requirement: Service owns the unchanged sample positions
|
||||||
|
The service SHALL own the existing eight sample positions grouped under Sample wallet A and Sample wallet B and their Jupiter and Phoenix venues, retaining every existing instrument, direction and decimal USD size. The snapshot SHALL remain unchanged for this task.
|
||||||
|
|
||||||
|
#### Scenario: Existing samples are retrieved
|
||||||
|
- **WHEN** EMC retrieves the service snapshot
|
||||||
|
- **THEN** wallet A contains Jupiter SOL Long 12500.00 and BTC Short 8250.50, Phoenix SOL Short 3200.75 and ETH Long 5400.00
|
||||||
|
- **AND** wallet B contains Jupiter ETH Long 1875.25 and SOL Short 950.00, Phoenix BTC Long 21000.00 and SOL Short 640.50
|
||||||
|
|
||||||
|
### Requirement: Hub owns Perps startup
|
||||||
|
The service SHALL run as a separate Hub-managed process registered as `evelyn.perps.default`. The service API SHALL expose no lifecycle operations. Snapshot reads before process startup SHALL fail clearly.
|
||||||
|
|
||||||
|
#### Scenario: Service starts
|
||||||
|
- **WHEN** the Hub starts the registered Perps process
|
||||||
|
- **THEN** its service view makes the complete sample snapshot available without starting a polling or integration worker
|
||||||
+56
@@ -0,0 +1,56 @@
|
|||||||
|
# Spec Delta
|
||||||
|
|
||||||
|
## MODIFIED Requirements
|
||||||
|
|
||||||
|
### Requirement: Selected processes are constructed and started lazily in exact order
|
||||||
|
The Hub SHALL support the modern process IDs `nenjim.registry.service`, `nenjim.journal.service`, `assetaz.ticker.default`, `jupiter-perps-alarm.default`, `evelyn.status.prod`, `evelyn.status.test`, `evelyn.iou-burner.prod`, `evelyn.liquidity.prod`, `evelyn.perps.default`, `nenjim.javafx-runtime.service`, `evelyn.mission-control.default`, and `nenjim.process-manager.default` through implementation-only construction capability that creates no process instance during validation. After passive composition, the Hub SHALL visit every configured entry after Registry in declared order, beginning with Journal. At each entry it SHALL construct only that process and any explicitly composed process-specific passive dependencies or administration view, register the process under the exact configured ID, resolve it through the canonical `NenjimProcess` contract where applicable, call `start()`, and record the ID only after successful return.
|
||||||
|
|
||||||
|
The production IOU burner SHALL receive the already constructed Production Evelyn status service, and SHALL require both Ticker and `evelyn.status.prod` to precede `evelyn.iou-burner.prod`. EMC SHALL receive the already started `evelyn.perps.default` service, which SHALL precede EMC in the deployment autostart list. The configuration SHALL be the complete manually ordered process dependency closure. The Hub SHALL not discover, insert, recursively construct, reorder, or automatically start dependencies. A process omitted from `[AUTO_START]` SHALL not be constructed, registered, started, or recorded, even when its implementation is available. Legacy process prototypes outside the modern Hub graph SHALL not be selectable.
|
||||||
|
|
||||||
|
#### Scenario: Configured process starts successfully
|
||||||
|
- **WHEN** a configured process entry is reached and its dependencies are already available
|
||||||
|
- **THEN** that process is constructed, registered, started, and recorded before the next entry is reached
|
||||||
|
|
||||||
|
#### Scenario: Process is omitted
|
||||||
|
- **WHEN** a known process ID does not appear in `[AUTO_START]`
|
||||||
|
- **THEN** no instance of that process is constructed or registered and its lifecycle is never started or recorded
|
||||||
|
|
||||||
|
#### Scenario: Jupiter Perps Alarm is selected
|
||||||
|
- **WHEN** `jupiter-perps-alarm.default` is explicitly listed after its required predecessors
|
||||||
|
- **THEN** the otherwise inactive modern alarm process is constructed, registered, started and recorded at that exact position
|
||||||
|
|
||||||
|
#### Scenario: Dependency order is invalid
|
||||||
|
- **WHEN** a selected process is reached before a process dependency required by its current constructor or startup behavior
|
||||||
|
- **THEN** construction or startup fails clearly without implicit dependency construction, repair, or reordering
|
||||||
|
|
||||||
|
#### Scenario: JavaFX processes are omitted
|
||||||
|
- **WHEN** a valid headless plan omits `nenjim.javafx-runtime.service`, `evelyn.mission-control.default`, and `nenjim.process-manager.default`
|
||||||
|
- **THEN** none of those objects is constructed or registered and the JavaFX toolkit remains uninitialized by the Hub
|
||||||
|
|
||||||
|
#### Scenario: Renamed status services retain dependency wiring
|
||||||
|
- **WHEN** `evelyn.status.prod` and `evelyn.status.test` are listed after Ticker and before Mission Control
|
||||||
|
- **THEN** the Hub constructs separate Production/Test status services, registers the renamed IDs, and injects the corresponding typed service views into Mission Control in the configured order
|
||||||
|
|
||||||
|
#### Scenario: Deployment still contains an old status ID
|
||||||
|
- **WHEN** a private deployment configuration contains `evelyn.service.prod` or `evelyn.service.test`
|
||||||
|
- **THEN** validation rejects the unknown ID before startup; deployment migration replaces only that ID with `evelyn.status.prod` or `evelyn.status.test`, preserving selection and order
|
||||||
|
|
||||||
|
#### Scenario: Liquidity is explicitly selected
|
||||||
|
- **WHEN** `evelyn.liquidity.prod` is reached in a valid startup plan
|
||||||
|
- **THEN** the Hub loads its runtime configuration, binds the dedicated signer/wallet and Jupiter service to each other, constructs direct Raydium access and registers/starts the liquidity process without inserting a Ticker or status dependency or reordering the plan
|
||||||
|
|
||||||
|
#### Scenario: Production burner has its reference-price dependency
|
||||||
|
- **WHEN** `evelyn.iou-burner.prod` is reached after Ticker and `evelyn.status.prod`
|
||||||
|
- **THEN** the Hub injects the corresponding Production status service for buyback price decisions
|
||||||
|
|
||||||
|
#### Scenario: Production burner precedes its status dependency
|
||||||
|
- **WHEN** the burner is reached without a previously constructed Production status service
|
||||||
|
- **THEN** construction fails clearly without creating or starting the omitted dependency or the burner
|
||||||
|
|
||||||
|
#### Scenario: EMC has its Perps dependency
|
||||||
|
- **WHEN** `evelyn.perps.default` appears before EMC in the deployment's ordered autostart list
|
||||||
|
- **THEN** the Hub registers and starts that service and injects its service view into EMC
|
||||||
|
|
||||||
|
#### Scenario: EMC precedes Perps
|
||||||
|
- **WHEN** EMC is selected without an earlier Perps process
|
||||||
|
- **THEN** construction fails clearly without inserting or starting that dependency
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# Tasks
|
||||||
|
|
||||||
|
## 1. Perps service and data boundary
|
||||||
|
|
||||||
|
- [x] 1.1 Add the domain-only immutable snapshot API and Hub-started reference service; verify all eight existing values, stable IDs, read-only API and defensive list copies by source inspection and compilation.
|
||||||
|
- [x] 1.2 Document ownership and lifecycle in docs/Evelyn.md; verify documentation matches the public API and no exchange dependencies are introduced.
|
||||||
|
|
||||||
|
## 2. EMC polling and presentation
|
||||||
|
|
||||||
|
- [x] 2.1 Replace EMC-owned samples with identity-based snapshot rendering; verify unchanged snapshots leave tree items, expansion and selection untouched, and formatting/colors remain unchanged.
|
||||||
|
- [x] 2.2 Inject the service and add sequential off-thread three-second polling tied to window visibility; verify initial retrieval, error recovery, hide/reopen cancellation and obsolete-result rejection in an isolated JavaFX session.
|
||||||
|
|
||||||
|
## 3. Hub startup wiring
|
||||||
|
|
||||||
|
- [x] 3.1 Add the process catalogue entry and required EMC dependency, and update local autostart before EMC; verify actual configuration parses and the same service object has both service/process views.
|
||||||
|
- [x] 3.2 Document the new process ID and explicit ordering in docs/Nenjim.md; verify the tracked minimal bootstrap example remains unchanged.
|
||||||
|
|
||||||
|
## 4. Integration verification
|
||||||
|
|
||||||
|
- [x] 4.1 Run the normal Gradle build, strict OpenSpec validation and diff review; record results and leave all work uncommitted and unpushed with the active change ready for review.
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
# Verification
|
||||||
|
|
||||||
|
Verified 2026-10-19 against Gitea issue #96 and the owner's explicit autostart requirement.
|
||||||
|
|
||||||
|
## Build and specification checks
|
||||||
|
|
||||||
|
- `JAVA_HOME=/usr/local/software/java/jfx-25 ./gradlew build` passed, including normal Detag processing, compilation, packaging and the existing 42 tests across six suites, with zero failures, errors or skips.
|
||||||
|
- `openspec validate 96-extract-evelyn-perps-service --strict` passed.
|
||||||
|
- `git diff --check` passed.
|
||||||
|
- No automated tests were added to the repository. The runtime checks below used a temporary standalone preview under `/tmp/emc96-preview`.
|
||||||
|
|
||||||
|
## Service and Hub verification
|
||||||
|
|
||||||
|
- Snapshot reads before startup and duplicate startup fail clearly. The started service returns its complete fixed immutable snapshot; wallet, venue and position collections reject mutation.
|
||||||
|
- Inspected all eight original instruments, directions and decimal USD values and stable sibling IDs. The public service API has only one snapshot read operation. Its response types contain no JavaFX, presentation formatting or exchange dependencies.
|
||||||
|
- Parsed the actual ignored `conf/NenjimHub.conf` against the Hub's constructor catalogue. It includes `evelyn.perps.default` before EMC.
|
||||||
|
- Exercised the Hub's normal `constructRegisterAndStartProcess` path for only Perps with an isolated Registry. The same object resolved as both `EvelynPerpsService` and `NenjimProcess`, returned a populated snapshot and appeared in the Hub's successful-start snapshot.
|
||||||
|
- The tracked `conf/NenjimHub.conf.example` is unchanged. No operational Hub, wallet signer, financial process or exchange integration was started.
|
||||||
|
|
||||||
|
## Isolated JavaFX runtime inspection
|
||||||
|
|
||||||
|
Constructed the actual EMC with empty status-service views, the real Perps sample service behind an instrumented read-only view, and the real JavaFX runtime. All instrumented snapshot calls ran outside the JavaFX thread.
|
||||||
|
|
||||||
|
- Initial retrieval populated both environments with two wallets, four venues and eight positions: 14 expanded rows.
|
||||||
|
- Measured the first two scheduled gaps as 2.999846105 and 2.999953679 seconds.
|
||||||
|
- Collapsed wallet A and selected a position in wallet B. Repeated polls preserved the same selection and expansion. List/property listeners observed zero tree mutations for unchanged data. Test remained independently expanded.
|
||||||
|
- Inspected rendered images for the original column headings, sample/Phoenix notice, USD formatting, faint Long/Short colors and selection styling.
|
||||||
|
- Injected one read failure; the following scheduled read recovered and polling continued.
|
||||||
|
- Blocked one read and successfully dispatched UI work while it waited; hiding the window interrupted the blocked read and stopped subsequent polling.
|
||||||
|
- Reopened EMC and confirmed retained selection. Held an old session's queued completion across hide, emptied the preview's view, then delivered that obsolete completion; it did not repopulate the view.
|
||||||
|
- Reopened again and issued repeated start requests while visible. Exactly two additional reads occurred over 6.4 seconds, demonstrating one three-second loop rather than accumulated loops.
|
||||||
|
|
||||||
|
Images are temporary local evidence in `/tmp/emc96-preview/`: `expanded.png`, `collapsed-selected.png`, `after-polling.png` and `reopened.png`. Inspection exercised actual JavaFX scenes and controls; manual mouse/keyboard use in the operational deployment was not exercised.
|
||||||
|
|
||||||
|
## Review state
|
||||||
|
|
||||||
|
All work remains local, uncommitted and unpushed. The OpenSpec change remains active for owner review; its delta specs have not been archived into the main specs. The new service will autostart on the next normal Hub startup using this deployment's updated local configuration.
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
schema: spec-driven
|
||||||
|
created: 2026-10-22
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# Design
|
||||||
|
|
||||||
|
## Context
|
||||||
|
See proposal.md. The service currently creates two named sample wallets internally; EMC uses one shared service and snapshot for both tabs. Hub startup is explicitly ordered.
|
||||||
|
|
||||||
|
## Goals / Non-Goals
|
||||||
|
Keep wallet configuration out of EMC and snapshots instance-owned. No real addresses, integrations, trading, new configuration framework or UI redesign are introduced.
|
||||||
|
|
||||||
|
## Decisions
|
||||||
|
- Add an immutable wallet configuration record (stable ID and display name), supplied as a defensively copied list to the implementation constructor. The sample IDs are not Solana addresses. This avoids passing UI data or moving sample position definitions into Hub.
|
||||||
|
- Hub owns one immutable list of the two existing sample wallet identities, supplied to both independently constructed processes.
|
||||||
|
- Keep the existing two sample position sets in the service and assign them by configured wallet order, alternating for additional sample wallets. This is explicitly sample-only behavior, not an integration policy; empty configuration gives an empty snapshot. It avoids coupling sample data to wallet IDs or requiring exactly two wallets.
|
||||||
|
- Inject PROD and TEST service views into EMC. Retain one off-thread scheduled loop, with separate reads and error handling for each tab. Preserve existing rendering and session guards.
|
||||||
|
- Retire the default ID rather than aliasing it, following explicit process selection and fail-fast dependencies.
|
||||||
|
|
||||||
|
## Risks / Trade-offs
|
||||||
|
- Identical fixtures conceal crossed wiring → verify routing by source inspection and an isolated session with distinct snapshots.
|
||||||
|
- Private startup configuration is ignored by Git → update the local file and document the required deployment migration.
|
||||||
|
- Sample assignment is ordinal → document this temporary fixture behavior; real integration remains a later task.
|
||||||
|
|
||||||
|
## Migration Plan
|
||||||
|
Replace evelyn.perps.default with evelyn.perps.prod and evelyn.perps.test before EMC. Both must precede EMC. The minimal tracked bootstrap template stays unchanged. Rollback requires restoring both code and the old configuration entry. Do not start production processes during verification.
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# Proposal
|
||||||
|
|
||||||
|
## Why
|
||||||
|
EMC currently shares one Perps service between TEST and PROD. Issue #97 requires separate service ownership and Hub-supplied wallet configuration before real integrations are introduced.
|
||||||
|
|
||||||
|
## What Changes
|
||||||
|
- **BREAKING**: Replace `evelyn.perps.default` with `evelyn.perps.test` and `evelyn.perps.prod` in the process catalogue and startup configuration.
|
||||||
|
- Pass the same hardcoded wallet configuration from NenjimHub to two independent service instances.
|
||||||
|
- Poll each instance for its corresponding EMC tab every three seconds, preserving presentation and the parameterless snapshot API.
|
||||||
|
|
||||||
|
## Capabilities
|
||||||
|
|
||||||
|
### New Capabilities
|
||||||
|
None.
|
||||||
|
|
||||||
|
### Modified Capabilities
|
||||||
|
- `evelyn-perps-service`: Hub-configured wallets and separate TEST/PROD lifecycles and snapshots.
|
||||||
|
- `nenjim-hub-configuration`: Separate Perps process IDs and explicit EMC dependencies.
|
||||||
|
- `evelyn-perps-overview`: Each environment tab polls its own service.
|
||||||
|
|
||||||
|
## Impact
|
||||||
|
Perps service construction, EMC injection and polling, Hub process catalogue, local startup configuration and project documentation. No exchange integration, trading, advanced configuration or UI changes.
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
## MODIFIED Requirements
|
||||||
|
|
||||||
|
### Requirement: Asynchronous polling follows window visibility
|
||||||
|
EMC SHALL request the current Perps snapshot from each environment's own service immediately on opening and every three seconds while visible, without blocking the UI thread. UI updates SHALL occur only on the JavaFX thread. Hiding the window SHALL stop polling; reopening SHALL create only one active polling loop and ignore results belonging to an earlier window session. Poll failures SHALL preserve the current view and allow subsequent attempts.
|
||||||
|
|
||||||
|
#### Scenario: Read takes time or fails
|
||||||
|
- **WHEN** a snapshot read is delayed or fails
|
||||||
|
- **THEN** the UI remains responsive and the existing view is retained
|
||||||
|
- **AND** later polling remains possible without overlapping scheduled reads
|
||||||
|
|
||||||
|
#### Scenario: Hide and reopen
|
||||||
|
- **WHEN** the owner hides and reopens EMC repeatedly
|
||||||
|
- **THEN** each visible session has only one polling loop and obsolete results cannot update its views
|
||||||
|
|
||||||
|
#### Scenario: Environment data stays separate
|
||||||
|
- **WHEN** TEST and PROD return different snapshots
|
||||||
|
- **THEN** each tab displays only its own service's data
|
||||||
|
- **AND** a failure reading one service does not prevent attempting the other service
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
## MODIFIED Requirements
|
||||||
|
|
||||||
|
### Requirement: Hub owns Perps startup
|
||||||
|
The service SHALL run as two separate Hub-managed processes registered as `evelyn.perps.test` and `evelyn.perps.prod`. The service API SHALL expose no lifecycle operations. Snapshot reads before process startup SHALL fail clearly.
|
||||||
|
|
||||||
|
#### Scenario: Service starts
|
||||||
|
- **WHEN** the Hub starts either registered Perps process
|
||||||
|
- **THEN** its service view makes the complete sample snapshot available without starting a polling or integration worker
|
||||||
|
|
||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: Wallet configuration belongs to service construction
|
||||||
|
The Hub SHALL supply the same hardcoded wallet configuration to TEST and PROD at construction. Each service SHALL retain an immutable copy and own a separate snapshot. EMC SHALL neither own nor pass wallet configuration; snapshot reads SHALL remain parameterless.
|
||||||
|
|
||||||
|
#### Scenario: Separate configured instances
|
||||||
|
- **WHEN** the Hub constructs and starts both instances using the shared sample wallet configuration
|
||||||
|
- **THEN** each snapshot contains the configured wallet identities and existing sample positions
|
||||||
|
- **AND** the two instances own separate snapshots without defining the wallet list internally
|
||||||
+59
@@ -0,0 +1,59 @@
|
|||||||
|
## MODIFIED Requirements
|
||||||
|
|
||||||
|
### Requirement: Selected processes are constructed and started lazily in exact order
|
||||||
|
The Hub SHALL support the modern process IDs `nenjim.registry.service`, `nenjim.journal.service`, `assetaz.ticker.default`, `jupiter-perps-alarm.default`, `evelyn.status.prod`, `evelyn.status.test`, `evelyn.iou-burner.prod`, `evelyn.liquidity.prod`, `evelyn.perps.prod` and `evelyn.perps.test`, `nenjim.javafx-runtime.service`, `evelyn.mission-control.default`, and `nenjim.process-manager.default` through implementation-only construction capability that creates no process instance during validation. After passive composition, the Hub SHALL visit every configured entry after Registry in declared order, beginning with Journal. At each entry it SHALL construct only that process and any explicitly composed process-specific passive dependencies or administration view, register the process under the exact configured ID, resolve it through the canonical `NenjimProcess` contract where applicable, call `start()`, and record the ID only after successful return.
|
||||||
|
|
||||||
|
The production IOU burner SHALL receive the already constructed Production Evelyn status service, and SHALL require both Ticker and `evelyn.status.prod` to precede `evelyn.iou-burner.prod`. EMC SHALL receive the already started `evelyn.perps.prod` and `evelyn.perps.test` services, both of which SHALL precede EMC in the deployment autostart list. The configuration SHALL be the complete manually ordered process dependency closure. The Hub SHALL not discover, insert, recursively construct, reorder, or automatically start dependencies. A process omitted from `[AUTO_START]` SHALL not be constructed, registered, started, or recorded, even when its implementation is available. Legacy process prototypes outside the modern Hub graph SHALL not be selectable.
|
||||||
|
|
||||||
|
#### Scenario: Configured process starts successfully
|
||||||
|
- **WHEN** a configured process entry is reached and its dependencies are already available
|
||||||
|
- **THEN** that process is constructed, registered, started, and recorded before the next entry is reached
|
||||||
|
|
||||||
|
#### Scenario: Process is omitted
|
||||||
|
- **WHEN** a known process ID does not appear in `[AUTO_START]`
|
||||||
|
- **THEN** no instance of that process is constructed or registered and its lifecycle is never started or recorded
|
||||||
|
|
||||||
|
#### Scenario: Jupiter Perps Alarm is selected
|
||||||
|
- **WHEN** `jupiter-perps-alarm.default` is explicitly listed after its required predecessors
|
||||||
|
- **THEN** the otherwise inactive modern alarm process is constructed, registered, started and recorded at that exact position
|
||||||
|
|
||||||
|
#### Scenario: Dependency order is invalid
|
||||||
|
- **WHEN** a selected process is reached before a process dependency required by its current constructor or startup behavior
|
||||||
|
- **THEN** construction or startup fails clearly without implicit dependency construction, repair, or reordering
|
||||||
|
|
||||||
|
#### Scenario: JavaFX processes are omitted
|
||||||
|
- **WHEN** a valid headless plan omits `nenjim.javafx-runtime.service`, `evelyn.mission-control.default`, and `nenjim.process-manager.default`
|
||||||
|
- **THEN** none of those objects is constructed or registered and the JavaFX toolkit remains uninitialized by the Hub
|
||||||
|
|
||||||
|
#### Scenario: Renamed status services retain dependency wiring
|
||||||
|
- **WHEN** `evelyn.status.prod` and `evelyn.status.test` are listed after Ticker and before Mission Control
|
||||||
|
- **THEN** the Hub constructs separate Production/Test status services, registers the renamed IDs, and injects the corresponding typed service views into Mission Control in the configured order
|
||||||
|
|
||||||
|
#### Scenario: Deployment still contains an old status ID
|
||||||
|
- **WHEN** a private deployment configuration contains `evelyn.service.prod` or `evelyn.service.test`
|
||||||
|
- **THEN** validation rejects the unknown ID before startup; deployment migration replaces only that ID with `evelyn.status.prod` or `evelyn.status.test`, preserving selection and order
|
||||||
|
|
||||||
|
#### Scenario: Liquidity is explicitly selected
|
||||||
|
- **WHEN** `evelyn.liquidity.prod` is reached in a valid startup plan
|
||||||
|
- **THEN** the Hub loads its runtime configuration, binds the dedicated signer/wallet and Jupiter service to each other, constructs direct Raydium access and registers/starts the liquidity process without inserting a Ticker or status dependency or reordering the plan
|
||||||
|
|
||||||
|
#### Scenario: Production burner has its reference-price dependency
|
||||||
|
- **WHEN** `evelyn.iou-burner.prod` is reached after Ticker and `evelyn.status.prod`
|
||||||
|
- **THEN** the Hub injects the corresponding Production status service for buyback price decisions
|
||||||
|
|
||||||
|
#### Scenario: Production burner precedes its status dependency
|
||||||
|
- **WHEN** the burner is reached without a previously constructed Production status service
|
||||||
|
- **THEN** construction fails clearly without creating or starting the omitted dependency or the burner
|
||||||
|
|
||||||
|
#### Scenario: EMC has its Perps dependency
|
||||||
|
- **WHEN** `evelyn.perps.prod` and `evelyn.perps.test` appear before EMC in the deployment's ordered autostart list
|
||||||
|
- **THEN** the Hub registers and starts separate service instances and injects their corresponding service views into EMC
|
||||||
|
|
||||||
|
#### Scenario: EMC precedes Perps
|
||||||
|
- **WHEN** EMC is selected without both earlier Perps processes
|
||||||
|
- **THEN** construction fails clearly without inserting or starting that dependency
|
||||||
|
|
||||||
|
#### Scenario: Deployment contains the retired shared Perps ID
|
||||||
|
- **WHEN** a deployment still selects `evelyn.perps.default`
|
||||||
|
- **THEN** startup validation rejects the unknown ID before starting processes
|
||||||
|
- **AND** migration replaces it with both new IDs before EMC
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# Tasks
|
||||||
|
|
||||||
|
## 1. Service configuration and Hub ownership
|
||||||
|
- [x] 1.1 Add immutable wallet configuration and separate TEST/PROD construction; inspect retained sample values, defensive copying and lifecycle wiring.
|
||||||
|
- [x] 1.2 Update local startup configuration and service/startup documentation; verify both IDs precede EMC and the minimal tracked template is unchanged.
|
||||||
|
|
||||||
|
## 2. EMC routing
|
||||||
|
- [x] 2.1 Inject both services and route independent reads to their respective tabs; verify three-second polling, error isolation and unchanged state-preserving renderer.
|
||||||
|
|
||||||
|
## 3. Integration verification
|
||||||
|
- [x] 3.1 Run Gradle build, strict OpenSpec validation and isolated verification of service snapshots and distinct environment routing; review the diff and leave changes uncommitted.
|
||||||
|
|
||||||
|
## Verification results
|
||||||
|
- Gradle build passed, including all 42 existing tests. No automated tests added to the repository.
|
||||||
|
- Strict OpenSpec validation and `git diff --check HEAD` passed.
|
||||||
|
- Isolated JavaFX verification used distinct PROD/TEST fixtures to verify routing, independent error handling, approximately 3.000-second poll gaps, unchanged row identities/selection/expansion, hide/reopen cancellation and obsolete-result rejection.
|
||||||
|
- The actual local startup configuration parsed successfully; both Perps instances were constructed, registered and started through the Hub lifecycle in isolation, with separate but equal snapshots. No production Hub startup or exchange requests.
|
||||||
|
- Local ignored `conf/NenjimHub.conf` now selects both IDs before EMC; tracked minimal bootstrap template is unchanged.
|
||||||
|
- Changes left uncommitted for owner review; OpenSpec change remains active.
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
schema: spec-driven
|
||||||
|
created: 2026-10-22
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# Design
|
||||||
|
|
||||||
|
## Context
|
||||||
|
See proposal.md and issue #98. Hub already has distinct Jupiter readers backed by a shared Solana cache (60-second TTL). The current Perps service creates samples; EMC already reconciles immutable snapshots by identity.
|
||||||
|
|
||||||
|
## Goals / Non-Goals
|
||||||
|
Keep existing public snapshot reads and read-only behavior. No changes to Jupiter trading, its caching, new columns, Phoenix, or stale-data UI. System documentation in the sibling repository is explicitly within the user-authorized scope.
|
||||||
|
|
||||||
|
## Decisions
|
||||||
|
- Replace sample wallet ID/name configuration with a SolanaWalletId address; use it for both row ID and label. Hub supplies separate fixed lists and the existing matching Jupiter dependency.
|
||||||
|
- One scheduled daemon worker per instance starts immediately, then uses a 60-second fixed delay after completion. This avoids overlap and catch-up bursts on slow RPCs. Window visibility does not affect it. Implementation-only close interrupts/shuts down the worker; the consumer interface remains read-only.
|
||||||
|
- Maintain last successful wallets in worker-owned state and publish a new immutable snapshot after each successful wallet, in configured order. Initial snapshot is empty; successful empty reads retain the wallet/Jupiter grouping with no positions. Errors preserve that wallet only; interruption ends the cycle.
|
||||||
|
- Reuse the three known Jupiter mint mappings already present in the alarm code (SOL/BTC/ETH); unknown mints display verbatim. No new currency registry entries are needed. Sort positions by account for deterministic results from unordered sets.
|
||||||
|
- Remove sample labels in PerpsTab; retain its existing identity reconciliation and polling. No new UI states.
|
||||||
|
|
||||||
|
## Risks / Trade-offs
|
||||||
|
- Errors can leave old data visible → log per wallet and document this deliberate limitation; stale indicators remain out of scope.
|
||||||
|
- Existing Jupiter model has dummy PnL and prices → use only required mint, direction, account and actual positionSize.
|
||||||
|
- Live RPC availability may limit end-to-end verification → record actual outcomes without claiming live success from compilation alone.
|
||||||
|
|
||||||
|
## Migration Plan
|
||||||
|
No process ID or autostart changes. Rebuild and restart through normal deployment to load the new wallet lists. Verification must not start the full Hub or any trading process. Leave all edits uncommitted for review.
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# Proposal
|
||||||
|
|
||||||
|
## Why
|
||||||
|
Issue #98 replaces sample portfolios with the owner's actual Jupiter positions so Mission Control can provide a useful read-only overview. Existing Jupiter position reads and the separate TEST/PROD services can be reused.
|
||||||
|
|
||||||
|
## What Changes
|
||||||
|
- Supply six PROD wallet addresses and one TEST address from Hub construction, with the corresponding existing Jupiter integrations.
|
||||||
|
- Refresh asynchronously at startup and every 60 seconds; keep immutable snapshot reads independent of integration calls.
|
||||||
|
- Preserve failed wallets' last successful data, clear successful empty results, and order positions deterministically.
|
||||||
|
- Remove sample/Phoenix positions and sample notices while retaining the existing table and polling.
|
||||||
|
- Update project documentation and the canonical system documentation in the sibling `r35157_docs` repository as explicitly requested.
|
||||||
|
|
||||||
|
## Capabilities
|
||||||
|
### New Capabilities
|
||||||
|
None.
|
||||||
|
### Modified Capabilities
|
||||||
|
- `evelyn-perps-service`: Live Jupiter refresh, real environment-specific wallet configuration and per-wallet failure behavior.
|
||||||
|
- `evelyn-perps-overview`: Real Jupiter data without prototype labels, preserving the existing presentation and row reconciliation.
|
||||||
|
|
||||||
|
## Impact
|
||||||
|
Perps configuration and implementation, Hub construction, EMC labels, and documentation in both repositories. No changes to trading operations, Phoenix integration, cache TTLs, or the parameterless public read API.
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
## REMOVED Requirements
|
||||||
|
|
||||||
|
### Requirement: Sample positions communicate direction and USD size
|
||||||
|
**Reason**: Sample data is replaced by real Jupiter positions.
|
||||||
|
**Migration**: Keep existing formatting and colors for actual positions.
|
||||||
|
|
||||||
|
### Requirement: Prototype is clearly labeled and entirely local
|
||||||
|
**Reason**: Data now comes from real Jupiter reads performed by the service.
|
||||||
|
**Migration**: Remove sample and illustrative-Phoenix notices; preserve EMC's snapshot-only boundary.
|
||||||
|
|
||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: Jupiter positions communicate direction and USD size
|
||||||
|
Position rows SHALL show Long or Short and USD sizes with grouping separators and two decimal places. Long rows SHALL have faint green backgrounds and Short rows faint red backgrounds; wallet and venue rows SHALL have no direction-based color. Existing columns and hierarchy SHALL be preserved.
|
||||||
|
|
||||||
|
#### Scenario: Inspect real positions
|
||||||
|
- **WHEN** real Jupiter positions are displayed
|
||||||
|
- **THEN** direction appears as readable text and background color, with formatted USD size
|
||||||
|
|
||||||
|
### Requirement: EMC presents service data without sample notices
|
||||||
|
EMC SHALL obtain positions only from its environment's in-memory service snapshot and SHALL contain no sample positions, sample notices, illustrative-Phoenix notices, or Refresh button. EMC SHALL own UI components, colors and formatting; integration reads SHALL belong to the service.
|
||||||
|
|
||||||
|
#### Scenario: Snapshot changes
|
||||||
|
- **WHEN** positions are added or removed after refresh
|
||||||
|
- **THEN** EMC reconciles rows by identity while preserving retained selections and expansion state
|
||||||
|
- **AND** no sample labels or Phoenix examples appear
|
||||||
+57
@@ -0,0 +1,57 @@
|
|||||||
|
## MODIFIED Requirements
|
||||||
|
|
||||||
|
### Requirement: Read-only current positions snapshot
|
||||||
|
The Perps service SHALL expose only a read operation returning its complete current in-memory snapshot. Wallets, venues within wallets, and positions within venues SHALL have stable identifiers across reads. Responses SHALL be immutable domain data without UI components, colors or formatting. Reading the snapshot SHALL make no Jupiter or Phoenix requests.
|
||||||
|
|
||||||
|
#### Scenario: Repeated reads
|
||||||
|
- **WHEN** clients repeatedly retrieve the started service's snapshot
|
||||||
|
- **THEN** they receive the latest published positions with stable identifiers and cannot mutate service-owned collections
|
||||||
|
- **AND** the reads trigger no exchange requests or position-management operations
|
||||||
|
|
||||||
|
|
||||||
|
### Requirement: Hub owns Perps startup
|
||||||
|
The service SHALL run as two separate Hub-managed processes registered as `evelyn.perps.test` and `evelyn.perps.prod`. The service API SHALL expose no lifecycle operations. Snapshot reads before process startup SHALL fail clearly. Startup SHALL schedule asynchronous retrieval without waiting for integration I/O.
|
||||||
|
|
||||||
|
#### Scenario: Service starts
|
||||||
|
- **WHEN** the Hub starts either registered Perps process
|
||||||
|
- **THEN** its service view is available without waiting for retrieval, initially empty until data arrives
|
||||||
|
- **AND** no placeholder positions are generated before successful retrieval
|
||||||
|
|
||||||
|
### Requirement: Wallet configuration belongs to service construction
|
||||||
|
The Hub SHALL supply the six PROD wallet addresses and one TEST wallet address specified in issue #98 and the corresponding existing Jupiter integration at construction. Each service SHALL retain immutable configuration and own its snapshot. Wallet addresses SHALL be used as stable identities and display labels. EMC SHALL neither own nor pass wallet configuration; reads SHALL remain parameterless.
|
||||||
|
|
||||||
|
#### Scenario: Separate configured instances
|
||||||
|
- **WHEN** the Hub constructs and starts both instances
|
||||||
|
- **THEN** each instance queries only its configured wallets through its corresponding integration
|
||||||
|
- **AND** the implementation does not define the wallet list internally
|
||||||
|
|
||||||
|
## REMOVED Requirements
|
||||||
|
|
||||||
|
### Requirement: Service owns the unchanged sample positions
|
||||||
|
**Reason**: Real Jupiter positions replace all sample data.
|
||||||
|
**Migration**: Remove sample portfolios and Phoenix examples; configure real addresses in Hub.
|
||||||
|
|
||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: Background Jupiter refresh
|
||||||
|
The service SHALL refresh immediately after startup and wait 60 seconds between completed cycles, without overlapping cycles or depending on EMC visibility. It SHALL use only existing Jupiter position reads and respect their cache. Snapshot reads SHALL make no integration calls. No transaction signing or position-management operation SHALL occur.
|
||||||
|
|
||||||
|
#### Scenario: Background update while EMC is hidden
|
||||||
|
- **WHEN** EMC is hidden and the next refresh is due
|
||||||
|
- **THEN** the service still refreshes configured wallets and publishes immutable snapshots
|
||||||
|
- **AND** no freshness guarantee bypasses the existing 60-second integration cache
|
||||||
|
|
||||||
|
### Requirement: Position mapping is stable and complete
|
||||||
|
The service SHALL map position account to stable ID, mint to known instrument label or raw mint fallback, direction to Long/Short, and positionSize to USD size. Wallet order SHALL follow configuration, and positions SHALL be ordered deterministically by position account. Only Jupiter venues and actual positions SHALL be published.
|
||||||
|
|
||||||
|
#### Scenario: Changed and unknown positions
|
||||||
|
- **WHEN** a successful read returns newly opened positions including an unknown token mint
|
||||||
|
- **THEN** all returned positions appear with stable IDs and exact USD sizes, and the unknown mint is displayed rather than dropped
|
||||||
|
|
||||||
|
### Requirement: Refresh failures are isolated per wallet
|
||||||
|
A failed wallet read or mapping SHALL retain its last successful data, log the failure and allow other wallets to refresh. Successful empty results SHALL remove previous positions. Interruption SHALL stop the current cycle. Failed first reads SHALL not invent positions.
|
||||||
|
|
||||||
|
#### Scenario: Failure followed by recovery
|
||||||
|
- **WHEN** one wallet fails while another succeeds
|
||||||
|
- **THEN** only the successful wallet's data is replaced
|
||||||
|
- **AND** a later successful empty read clears the recovered wallet's previous positions
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# Tasks
|
||||||
|
|
||||||
|
## 1. Live service and construction
|
||||||
|
- [x] 1.1 Replace samples with background Jupiter reads, address configuration and Hub injection; verify mapping, wallet isolation, empty results, deterministic ordering and cache-only snapshot reads in isolated execution.
|
||||||
|
- [x] 1.2 Document refresh lifecycle and configuration in project docs; verify exact issue wallet lists and removal of obsolete sample descriptions.
|
||||||
|
|
||||||
|
## 2. Presentation and system documentation
|
||||||
|
- [x] 2.1 Remove sample notices and retain identity-based rendering; verify additions/removals and retained selection/expansion in isolated JavaFX execution.
|
||||||
|
- [x] 2.2 Update canonical r35157_docs service overview and relevant links; verify documentation against code, distinguish code verification from deployment and check relative links.
|
||||||
|
|
||||||
|
## 3. Integration verification
|
||||||
|
- [x] 3.1 Run Gradle build, strict OpenSpec validation, diff review and a read-only live lookup if available; record evidence and leave all work uncommitted.
|
||||||
|
|
||||||
|
## Verification results
|
||||||
|
- Gradle build passed, including all 42 existing tests. No new repository tests were added, following project instructions.
|
||||||
|
- Isolated execution verified asynchronous initial retrieval, immutable/cached snapshot reads, configured address validation, mapping and unknown mint fallback, exact sizes, deterministic order, per-wallet failure retention, successful empty clearing and interruption.
|
||||||
|
- Isolated JavaFX execution verified removal of notices, stable selection/expansion/row identities and additions/removals; screenshot inspected. Service refresh remains independent of EMC polling.
|
||||||
|
- Read-only live calls through the existing Jupiter adapter completed for all seven configured addresses on 2026-10-22: each returned zero open positions. This verifies live reads/empty results, not actual open-position decoding; nonempty behavior was checked with isolated data. No wallet/signing operation or full Hub startup was invoked.
|
||||||
|
- Strict OpenSpec validation and diff checks passed. System documentation and its links were checked in r35157_docs; no deployment is claimed.
|
||||||
|
- Existing #97 index/worktree changes were left untouched. No commits or pushes; change remains active for review.
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
schema: spec-driven
|
||||||
|
created: 2026-10-22
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# Design
|
||||||
|
|
||||||
|
## Context
|
||||||
|
See proposal.md and issue #99. The existing worker handles Jupiter wallets and publishes immutable snapshots; EMC already supports multiple venues and stable row reconciliation. Phoenix trader state contains all subaccounts but represents positions in signed base lots rather than USD.
|
||||||
|
|
||||||
|
## Goals / Non-Goals
|
||||||
|
Add only read-only REST access and venue-level failure isolation. Keep wallet lists, process names, UI, polling and Jupiter behavior. System documentation in r35157_docs is explicitly in scope. No SDK runtime, trading, streaming or generalized integration framework.
|
||||||
|
|
||||||
|
## Decisions
|
||||||
|
- Add PhoenixPerpsService and PhoenixPerpsPosition in com.r35157.libs.phoenix.perps; implementation in impl.ref. Return subaccount identity, symbol, signed base quantity and current USD size as domain data, without referencing Evelyn or UI types.
|
||||||
|
- GET trader/state/{authority}?traderPdaIndex=0, exchange/markets for baseLotsDecimals, and market/{symbol}/mark-price for active symbols. Empty portfolios need only trader state. Reuse each symbol's mark price within a lookup. Use the existing HTTP/Jackson stack with 10-second request timeout and no redirects/retries. All numeric conversion uses BigInteger/BigDecimal; JSON floating numbers must be parsed directly as decimal.
|
||||||
|
- Formula verified against official Rise SDK ts/src/units.ts: baseLotsToBaseUnits(lots, decimals) = lots / 10**decimals. USD notional = abs(base quantity) * markPrice.price. This avoids additional Solana PDA derivation needed for trader-view notional. Negative decimal exponents are supported. No use of entryPriceUsd or virtualQuotePositionLots.
|
||||||
|
- Validate authority, PDA index, required arrays/numeric fields, duplicate subaccount/position identities and matching market symbols. Missing positions arrays are permitted only as the documented optional field; null/wrong types fail. A malformed nonempty portfolio fails atomically, not partially.
|
||||||
|
- Hub supplies one shared read-only Phoenix adapter with no portfolio cache; each Evelyn instance owns independent last-successful state keyed by wallet and venue. Existing single scheduled worker reads Jupiter and Phoenix separately with independent catches; publish ordered Jupiter/Phoenix venues after each success. Interruption ends the cycle.
|
||||||
|
- Stable Phoenix IDs are PDA:subaccount:symbol; sequence numbers are not identity. Labels add -PERP only if absent. No new tree level.
|
||||||
|
|
||||||
|
## Risks / Trade-offs
|
||||||
|
- REST prices and position snapshots may come from different slots → periodic current estimates, not an atomic exchange valuation; document this limitation.
|
||||||
|
- Public reads currently work without credentials although generated OpenAPI security annotations suggest auth → use observed public access; reject 401/403 and retain data, never attempt signing/login automatically.
|
||||||
|
- Up to one mark request per active symbol per wallet → live verification exposed HTTP 429 during bursts. Space request starts at least one second apart in the shared adapter, and honor Retry-After (seconds/HTTP date, 60 seconds fallback). During cooldown fail fast without external calls, preserving venue failure isolation. No automatic retries or generic cache framework. This increases cycle duration before the existing one-minute delay.
|
||||||
|
- Previous data may persist on failure → log wallet/venue and document the existing lack of stale indicators.
|
||||||
|
|
||||||
|
## Migration Plan
|
||||||
|
No new configuration entries or dependencies. Build and restart through normal deployment after review. Do not start full Hub/trading during verification. Leave changes uncommitted and the OpenSpec change active.
|
||||||
|
|
||||||
|
## Sources
|
||||||
|
- https://docs.phoenix.trade/api/trader/get-trader-state
|
||||||
|
- https://docs.phoenix.trade/api/exchange/list-markets
|
||||||
|
- https://docs.phoenix.trade/api/exchange/get-mark-price
|
||||||
|
- https://docs.phoenix.trade/sdk/accounts
|
||||||
|
- https://github.com/Ellipsis-Labs/rise-public/blob/master/ts/src/units.ts
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# Proposal
|
||||||
|
|
||||||
|
## Why
|
||||||
|
The owner has Phoenix cross-margin positions that are missing from EMC. Issue #99 extends the existing view-only Perps overview to Phoenix using the same wallet configuration and service boundaries.
|
||||||
|
|
||||||
|
## What Changes
|
||||||
|
- Add a read-only Phoenix REST integration covering cross-margin and isolated subaccounts, with exact current USD notional conversion.
|
||||||
|
- Inject it through Hub and retain successful snapshots independently per wallet and venue.
|
||||||
|
- Keep the 60-second background refresh delay, 3-second EMC polling, and existing table presentation.
|
||||||
|
- Update project and canonical r35157_docs documentation, including API limitations and live verification evidence.
|
||||||
|
|
||||||
|
## Capabilities
|
||||||
|
### New Capabilities
|
||||||
|
- `phoenix-perps-service`: Read-only position retrieval and precise USD mapping from Phoenix REST data.
|
||||||
|
### Modified Capabilities
|
||||||
|
- `evelyn-perps-service`: Multi-venue refresh, construction and failure isolation.
|
||||||
|
|
||||||
|
## Impact
|
||||||
|
New Phoenix API/reference implementation, EvelynPerpsService implementation, Hub composition and documentation. No UI redesign, transaction signing, trading, new configuration framework or external library dependencies.
|
||||||
+37
@@ -0,0 +1,37 @@
|
|||||||
|
## MODIFIED Requirements
|
||||||
|
|
||||||
|
### Requirement: Wallet configuration belongs to service construction
|
||||||
|
The Hub SHALL supply the six PROD wallet addresses and one TEST wallet address specified in issue #98 and the corresponding existing Jupiter integration plus a read-only Phoenix integration at construction. Each service SHALL retain immutable configuration and own its snapshot. Wallet addresses SHALL be used as stable identities and display labels. EMC SHALL neither own nor pass wallet configuration; reads SHALL remain parameterless.
|
||||||
|
|
||||||
|
#### Scenario: Separate configured instances
|
||||||
|
- **WHEN** the Hub constructs and starts both instances
|
||||||
|
- **THEN** each instance queries only its configured wallets through its injected integrations
|
||||||
|
- **AND** the implementation does not define the wallet list internally
|
||||||
|
|
||||||
|
### Requirement: Background Jupiter refresh
|
||||||
|
The service SHALL refresh immediately after startup and wait 60 seconds between completed cycles, without overlapping cycles or depending on EMC visibility. It SHALL use Jupiter and Phoenix read operations and respect the existing Jupiter cache. Snapshot reads SHALL make no integration calls. No transaction signing or position-management operation SHALL occur.
|
||||||
|
|
||||||
|
#### Scenario: Background update while EMC is hidden
|
||||||
|
- **WHEN** EMC is hidden and the next refresh is due
|
||||||
|
- **THEN** the service still refreshes configured wallets and publishes immutable snapshots
|
||||||
|
- **AND** no freshness guarantee bypasses the existing 60-second integration cache
|
||||||
|
|
||||||
|
### Requirement: Position mapping is stable and complete
|
||||||
|
The service SHALL map position account to stable ID, mint to known instrument label or raw mint fallback, direction to Long/Short, and positionSize to USD size. Wallet order SHALL follow configuration, and positions SHALL be ordered deterministically by position account. Phoenix SHALL use PDA index, subaccount index and symbol as stable identity, signed quantity for direction and absolute current USD notional for size. Jupiter and Phoenix SHALL appear as separate venues, with no subaccount tree level.
|
||||||
|
|
||||||
|
#### Scenario: Changed and unknown positions
|
||||||
|
- **WHEN** a successful read returns newly opened positions including an unknown token mint
|
||||||
|
- **THEN** all returned positions appear with stable IDs and exact USD sizes, and the unknown mint is displayed rather than dropped
|
||||||
|
|
||||||
|
### Requirement: Refresh failures are isolated per wallet
|
||||||
|
A failed read or mapping SHALL retain the last successful data for that wallet and venue, log the failure and allow other wallets and venues to refresh. Successful empty results SHALL remove only that wallet/venue's previous positions. Interruption SHALL stop the current cycle. Failed first reads SHALL not invent positions.
|
||||||
|
|
||||||
|
#### Scenario: Failure followed by recovery
|
||||||
|
- **WHEN** one wallet fails while another succeeds
|
||||||
|
- **THEN** only the successful wallet/venue's data is replaced
|
||||||
|
- **AND** a later successful empty read clears the recovered wallet/venue's previous positions
|
||||||
|
|
||||||
|
#### Scenario: One venue fails
|
||||||
|
- **WHEN** Phoenix fails for a wallet while Jupiter succeeds, or vice versa
|
||||||
|
- **THEN** only the successful venue is updated and the failed venue retains its previous data
|
||||||
|
- **AND** subsequent wallets are still attempted
|
||||||
+36
@@ -0,0 +1,36 @@
|
|||||||
|
## Purpose
|
||||||
|
Read actual Phoenix perpetual positions for a Solana authority without signing or modifying accounts, preserving precise current USD notional and subaccount identities.
|
||||||
|
|
||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: Read every active position under the authority
|
||||||
|
The integration SHALL read official Phoenix REST trader state for the requested wallet authority at PDA index 0. It SHALL include nonzero positions from cross-margin and isolated subaccounts, preserve subaccount/symbol identity independently of sequence numbers, and return an immutable deterministically ordered result. No signing, trading or account activation SHALL occur.
|
||||||
|
|
||||||
|
#### Scenario: Cross and isolated positions share a symbol
|
||||||
|
- **WHEN** subaccounts 0 and 2 both have a nonzero SOL position
|
||||||
|
- **THEN** both positions are returned with distinct identities
|
||||||
|
- **AND** zero-sized positions are excluded
|
||||||
|
|
||||||
|
### Requirement: Current USD notional uses documented market units
|
||||||
|
Position direction SHALL follow signed quantity. USD size SHALL be the absolute current notional, calculated using exact decimal arithmetic from base lots divided by 10 to the market's base-lot decimal exponent and multiplied by its current mark price. Collateral, entry value and raw lot counts SHALL NOT be used as USD size. Missing conversion data SHALL fail the read.
|
||||||
|
|
||||||
|
#### Scenario: Convert a short position
|
||||||
|
- **WHEN** a position has -125 base lots, base-lot exponent 2 and mark price 104.45 USD
|
||||||
|
- **THEN** it represents -1.25 base units, Short direction and 130.5625 USD notional
|
||||||
|
|
||||||
|
### Requirement: Read failures cannot masquerade as empty portfolios
|
||||||
|
HTTP, authentication, malformed schema, identity mismatch and numeric conversion errors SHALL fail the read. Requests SHALL have bounded timeouts. Only a valid response with no nonzero positions SHALL produce an empty result. Required mark prices SHALL be positive and finite.
|
||||||
|
|
||||||
|
#### Scenario: Missing mark price or rejected access
|
||||||
|
- **WHEN** a required price is missing or an HTTP request is rejected
|
||||||
|
- **THEN** the read fails instead of returning an empty or partial portfolio
|
||||||
|
|
||||||
|
#### Scenario: Empty account
|
||||||
|
- **WHEN** a valid trader snapshot has no subaccounts or no nonzero positions
|
||||||
|
- **THEN** an empty result is returned without market-price requests
|
||||||
|
|
||||||
|
#### Scenario: Public API rate limit
|
||||||
|
- **WHEN** the shared adapter issues requests for PROD and TEST
|
||||||
|
- **THEN** request starts SHALL be spaced at least one second apart
|
||||||
|
- **AND** HTTP 429 SHALL fail the lookup and establish a shared cooldown from Retry-After (seconds or HTTP date, 60 seconds fallback)
|
||||||
|
- **AND** lookups during cooldown SHALL fail without external calls, with no immediate retry
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
# Tasks
|
||||||
|
|
||||||
|
## 1. Phoenix integration
|
||||||
|
- [x] 1.1 Implement read-only REST API/domain types and strict exact-decimal conversion; verify documented units, cross/isolated identities, empty results, malformed data, errors and timeouts in isolated execution and live nonempty responses.
|
||||||
|
- [x] 1.2 Document REST sources, unit conversion and limitations in project documentation; check statements against official docs and observed responses.
|
||||||
|
|
||||||
|
## 2. Evelyn integration and system documentation
|
||||||
|
- [x] 2.1 Inject Phoenix through Hub and isolate last-successful data per wallet/venue; verify failure combinations, empty clearing, unchanged Jupiter mapping, polling and cached snapshot reads in isolated execution.
|
||||||
|
- [x] 2.2 Update r35157_docs and related project documentation; verify removal of obsolete Jupiter-only statements and relative links.
|
||||||
|
|
||||||
|
## 3. Integration verification
|
||||||
|
- [x] 3.1 Run build/existing tests, strict OpenSpec validation, live/UI checks and final diff review; record evidence and leave all edits uncommitted.
|
||||||
|
|
||||||
|
Verification evidence: [verification.md](verification.md).
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
# Verification — 2026-10-22
|
||||||
|
|
||||||
|
## Build and specifications
|
||||||
|
|
||||||
|
- `JAVA_HOME=/usr/local/software/java/jfx-25 ./gradlew build`: successful; 42 existing tests, no failures/errors.
|
||||||
|
- `openspec validate 99-phoenix-perps-positions --strict`: valid.
|
||||||
|
- Relative file links in the changed project/system documentation resolve; whitespace checks pass.
|
||||||
|
- No repository test suite was added, in accordance with OpenSpec project context. Focused temporary execution harnesses are under `/tmp/emc99`.
|
||||||
|
|
||||||
|
## Phoenix reader
|
||||||
|
|
||||||
|
Temporary local HTTP responses exercised exact quantities beyond double's integer precision,
|
||||||
|
negative lot exponents, precise decimal prices, nonzero filtering, both cross and isolated
|
||||||
|
subaccounts holding the same symbol, deterministic sorting, immutable results and sequence-independent identity.
|
||||||
|
|
||||||
|
Verified valid empty states (including omitted optional positions), rejection of malformed/null
|
||||||
|
state, metadata and prices, wrong authority/PDA/version, duplicate identities, missing markets,
|
||||||
|
conversion overflow, HTTP 401/403/404/429/500, header and body timeouts, and interruption.
|
||||||
|
The shared Jackson mapper is unchanged. The harness accepts GET only. Pacing, fail-fast cooldown
|
||||||
|
and Retry-After seconds/HTTP-date parsing (including invalid/overflow fallback) were exercised.
|
||||||
|
|
||||||
|
## Evelyn and EMC
|
||||||
|
|
||||||
|
Temporary readers exercised immediate asynchronous startup, defensive wallet configuration,
|
||||||
|
1,000 cached snapshot reads during a blocked integration request, immutable snapshots,
|
||||||
|
unchanged Jupiter mapping, Phoenix subaccount identity/direction and deterministic ordering.
|
||||||
|
Independent combinations of Jupiter/Phoenix success/failure/empty for two wallets confirmed
|
||||||
|
retention and clearing per wallet/venue, including continued attempts after errors and cycle
|
||||||
|
termination on interruption. The single scheduled worker and 60-second fixed delay remain in place.
|
||||||
|
The existing six PROD/one TEST wallet lists are unchanged and validate.
|
||||||
|
|
||||||
|
A standalone JavaFX Perps view exercised same-symbol subaccounts, value updates, added/removed
|
||||||
|
positions and empty results while retaining row identity, selection and expansion. The existing
|
||||||
|
three columns and direction colors rendered the captured live data correctly. Screenshot:
|
||||||
|
`/tmp/emc99/view.png`. EMC code and its three-second snapshot polling are unchanged.
|
||||||
|
|
||||||
|
## Live reference
|
||||||
|
|
||||||
|
The adapter read all seven configured wallets through a local recording proxy forwarding
|
||||||
|
GET requests to the official Phoenix API. Captured trader state, metadata and mark-price responses
|
||||||
|
were independently checked with Python Decimal against every adapter result, including exact
|
||||||
|
quantity, notional, authority/subaccount/symbol identity and count.
|
||||||
|
|
||||||
|
| Environment / wallet prefix | Positions | Long | Short |
|
||||||
|
| --- | ---: | ---: | ---: |
|
||||||
|
| PROD H5Yrdm7B… | 32 | 30 | 2 |
|
||||||
|
| PROD 7PttBqap… | 32 | 29 | 3 |
|
||||||
|
| PROD vshTPUDa… | 31 | 10 | 21 |
|
||||||
|
| PROD 8XGrkDQC… | 32 | 3 | 29 |
|
||||||
|
| PROD 4wzxcYQ8… | 29 | 6 | 23 |
|
||||||
|
| PROD HM13QYMv… | 14 | 7 | 7 |
|
||||||
|
| TEST 6NyYEoXm… | 0 | 0 | 0 |
|
||||||
|
|
||||||
|
All 170 nonzero positions matched; H5's short positions were FET and TLT.
|
||||||
|
These are observations, not fixed expectations. Isolated positions were verified with controlled
|
||||||
|
responses, as the live positions were cross-margin.
|
||||||
|
|
||||||
|
An initial burst of price requests hit HTTP 429. The final shared one-second pacing completed
|
||||||
|
the live check without errors. At this portfolio size the sequential reads take about three
|
||||||
|
minutes before the existing 60-second pause; this is not a fresh full portfolio every 60 seconds.
|
||||||
|
Positions and mark prices are not atomic snapshots. These limitations and retained old data
|
||||||
|
on errors are documented in `docs/PhoenixPerps.md` and the canonical Evelyn system page.
|
||||||
|
|
||||||
|
No full Hub, trading/signing flow or deployment was started. Code and documentation are left
|
||||||
|
uncommitted; this OpenSpec change remains active for the owner's review/archive.
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
schema: spec-driven
|
||||||
|
created: 2026-10-24
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# Design
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
See proposal.md. The existing service holds only positions and drops first-read failures from the hierarchy. Jupiter's transaction adapter exposes dummy valuation zeros; Phoenix already validates REST trader state and paces requests. Existing local staged/deleted OpenSpec changes are outside this change.
|
||||||
|
|
||||||
|
## Goals / Non-Goals
|
||||||
|
|
||||||
|
**Goals:** Exact decimal monetary calculations, explicit incomplete/stale scopes, account-owned Cross capital, and a readable first version using existing configured environments.
|
||||||
|
|
||||||
|
**Non-Goals:** Account utilization, inferred Phoenix position ROI/leverage, wallet configuration changes, transaction/quote construction, live deployment, or a permanent test suite.
|
||||||
|
|
||||||
|
## Decisions
|
||||||
|
|
||||||
|
1. Extend immutable service snapshots with nullable metrics, isolated positions, explicit Cross accounts and last-success/read-failure metadata. Aggregate in the service domain, independently of tree expansion. Sum actual capital once per isolated position/Cross account; sum gross exposure and dollar PnL; propagate unavailable components separately per field. Never aggregate ratios. Preserve a never-read venue as unknown instead of silently excluding it.
|
||||||
|
2. Add a Jupiter overview-only REST interface and implementation, injected independently of the existing transaction services. Validate count, identities, directions, integer micro-USD fields and complete responses. Use one coherent GET per wallet. Size remains the venue's existing USD size, collateral is its recorded post-fee collateral, price PnL uses pnlBeforeFeesUsd, and leverage/percentage are derived from that collateral. Current live reads match on-chain size/collateral; API percentages are percentage points. Do not use API valueUsd until its valuation is trustworthy: observed valueUsd subtracts openFeesUsd from collateral that already matches post-fee on-chain collateral; profit-cap/collateral valuation semantics are also undocumented. Display unavailable Value with this reason rather than correct one fee while guessing the rest.
|
||||||
|
3. Extend Phoenix's own account/position result, keeping it independent of Evelyn/UI. Subaccount 0 is Cross; other indices are isolated. Read one trader state and one metadata response, then one price per unique held symbol. Convert quote lots at 1e-6 USD. Price PnL is signed quantity times mark plus virtual quote. Funding is the signed unsettled funding amount returned in trader state, added to equity; accumulated/settled funding is not added again. Closing fee estimate uses current notional times market taker fee times the account's REST fee override. Do not subtract settled trading fees again. Nonzero unsupported spot collateral makes collateral/equity/Value unavailable, while independent notional/PnL remain available.
|
||||||
|
4. Phoenix position initial margin follows SDK integer tick conversion (floor), integer tier interpolation (truncate the complete interpolated leverage), and ceil division to quote lots. Exposure/PnL keep the original precise REST mark, preserving existing notional meaning. Isolated leverage and ROI use collateral; Cross position leverage/ROI remain unavailable, Cross position Value is inapplicable. Cross group leverage is gross exposure divided by undiscounted equity before estimated exit fees.
|
||||||
|
5. JavaFX cells only format published domain metrics and notes. Reconcile stable IDs at all levels, including Cross accounts; preserve root and row instances, selection and expansion. Give numbers aligned cells and headers/rows explanatory tooltips, neutral rounded zero, and visually distinct group totals.
|
||||||
|
|
||||||
|
## Risks / Trade-offs
|
||||||
|
|
||||||
|
- Separate Phoenix state/metadata/price observations → describe reference-price estimates and lack of atomic/executable quotes; exclude slippage, swaps, rent and future funding.
|
||||||
|
- Unverified Jupiter net proceeds → explicit unavailable Value propagates to higher Value totals; do not publish a partial total.
|
||||||
|
- Unknown Phoenix spot collateral or missing valuation inputs → only affected fields unavailable; malformed position/identity/required price data still fails the whole scope.
|
||||||
|
- REST failure or cooldown → retain last success with timestamp and failure notice; no immediate retries or overlapping cycles.
|
||||||
|
- Additional metrics exceed former three-column layout → preview in the existing EMC dimensions and allow horizontal scrolling at useful minimum widths.
|
||||||
|
|
||||||
|
## Migration Plan
|
||||||
|
|
||||||
|
No runtime configuration or storage migration. Build/verify locally and leave changes uncommitted for review. A later owner-controlled deployment replaces the binary; rollback restores the previous build. Do not restart the live Hub.
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
# Proposal
|
||||||
|
|
||||||
|
## Why
|
||||||
|
|
||||||
|
The owner cannot assess Evelyn's committed capital and risk from direction and size alone. Issue #100 adds a first inspectable, read-only valuation overview for existing wallets, directly supporting safe Evelyn operation.
|
||||||
|
|
||||||
|
## What Changes
|
||||||
|
|
||||||
|
- Show Position, Direction, Margin, Collateral, Leverage, Exposure, Value and uPnL beneath a visible Perpetual Contracts root.
|
||||||
|
- **BREAKING**: Extend snapshot and Phoenix result record contracts to represent shared Cross accounts separately from isolated positions, retaining funded empty Cross accounts.
|
||||||
|
- Publish precise venue valuations and complete gross exposure, dollar uPnL and closing-value totals without counting shared capital twice. Unsupported metrics remain explicitly unavailable.
|
||||||
|
- Add an overview-only Jupiter REST reader; preserve the existing trading, signing, alarm and entry-price integration.
|
||||||
|
- Preserve background refresh timing, independent last-success retention, immutable snapshots and stable UI reconciliation; explain retained or incomplete data.
|
||||||
|
|
||||||
|
## Capabilities
|
||||||
|
|
||||||
|
### New Capabilities
|
||||||
|
|
||||||
|
- `jupiter-perps-overview`: Validated read-only Jupiter position metrics, independent of transaction operations.
|
||||||
|
|
||||||
|
### Modified Capabilities
|
||||||
|
|
||||||
|
- `evelyn-perps-service`: Account-aware immutable metrics and complete, field-specific aggregation with independent refresh status.
|
||||||
|
- `evelyn-perps-overview`: Eight columns, Cross hierarchy, totals, formatting and definitions.
|
||||||
|
- `phoenix-perps-service`: Shared balances, exact price PnL, initial margin and account closing-value estimates.
|
||||||
|
|
||||||
|
## Impact
|
||||||
|
|
||||||
|
Changes are limited to `.tjava` integration/service/presentation code, passive Hub wiring, OpenSpec and applicable project/shared documentation. No new dependencies, configuration system, trading operations, live Hub restart, commits or pushes. Verification uses the normal build and temporary focused/read-only JavaFX harnesses.
|
||||||
+33
@@ -0,0 +1,33 @@
|
|||||||
|
## MODIFIED Requirements
|
||||||
|
|
||||||
|
### Requirement: Perps overview displays a collapsible position hierarchy
|
||||||
|
The Perps tab SHALL display Position, Direction, Margin, Collateral, Leverage, Exposure, Value and uPnL in that order beneath the visible Perpetual Contracts root. Isolated hierarchy SHALL be wallet → venue → position; each Phoenix Cross account SHALL add a Cross group above its positions. Groups SHALL show applicable totals, leave direction/aggregate ratios blank, and be expanded by default.
|
||||||
|
|
||||||
|
#### Scenario: Open either environment's Perps tab
|
||||||
|
- **WHEN** the owner opens Perps in Production or Test
|
||||||
|
- **THEN** all eight columns, root totals and the account-aware hierarchy are visible
|
||||||
|
|
||||||
|
#### Scenario: Collapse and expand groups
|
||||||
|
- **WHEN** the owner collapses and expands a wallet, venue or Cross account
|
||||||
|
- **THEN** descendants hide and return without changing totals
|
||||||
|
|
||||||
|
### Requirement: Jupiter positions communicate direction and USD size
|
||||||
|
Positions SHALL show readable Long/Short direction and Isolated/Cross margin text. USD amounts SHALL use grouping and two decimals; leverage SHALL use a multiplication sign. Signed uPnL SHALL be green for positive, red for negative and neutral for rounded zero without negative zero. Existing faint direction backgrounds SHALL remain; groups SHALL have distinct weight and no direction color. Numeric columns SHALL be right-aligned with useful widths and tooltips.
|
||||||
|
|
||||||
|
#### Scenario: Small or unavailable PnL
|
||||||
|
- **WHEN** PnL rounds to zero or is unavailable
|
||||||
|
- **THEN** zero has no negative sign/color and unavailable values show an em dash with an explanation
|
||||||
|
|
||||||
|
#### Scenario: Inspect real positions
|
||||||
|
- **WHEN** real Jupiter positions are displayed
|
||||||
|
- **THEN** direction appears as readable text and background color, with formatted gross USD exposure and applicable valuation metrics
|
||||||
|
|
||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: Definitions distinguish owned collateral and margin requirements
|
||||||
|
Tooltips SHALL explain isolated collateral, exposure-based leverage, collateral-based ROI, estimated net Value and excluded slippage/swaps/rent. Cross position collateral SHALL be identified as initial margin required against shared collateral. Cross position Value SHALL be unavailable; unsupported Cross ROI/leverage SHALL be explained. Cross account leverage SHALL identify undiscounted equity before exit fees as its denominator.
|
||||||
|
|
||||||
|
#### Scenario: Inspect a Cross position
|
||||||
|
- **WHEN** the owner inspects a Cross row and its account group
|
||||||
|
- **THEN** the position shows a USD margin requirement and the account shows shared capital once
|
||||||
|
- **AND** no position-only metric is labeled account utilization
|
||||||
+53
@@ -0,0 +1,53 @@
|
|||||||
|
## MODIFIED Requirements
|
||||||
|
|
||||||
|
### Requirement: Wallet configuration belongs to service construction
|
||||||
|
The Hub SHALL supply the six PROD wallet addresses and one TEST wallet address specified in issue #98, a read-only Jupiter overview reader and a read-only Phoenix integration at construction. Each service SHALL retain immutable configuration and own its snapshot. Wallet addresses SHALL be used as stable identities and display labels. EMC SHALL neither own nor pass wallet configuration; reads SHALL remain parameterless. Existing Jupiter trading/alarm services SHALL retain their behavior.
|
||||||
|
|
||||||
|
#### Scenario: Separate configured instances
|
||||||
|
- **WHEN** the Hub constructs and starts both instances
|
||||||
|
- **THEN** each instance queries only its configured wallets through its injected integrations
|
||||||
|
- **AND** the implementation does not define the wallet list internally
|
||||||
|
|
||||||
|
### Requirement: Position mapping is stable and complete
|
||||||
|
The service SHALL preserve Jupiter position-account identities and its existing USD position-size basis. Phoenix SHALL preserve PDA/subaccount/symbol identities and absolute current notional. Isolated positions SHALL remain directly under venues; Cross accounts SHALL own their positions and shared balances separately. Wallet and position ordering SHALL be deterministic. No account membership SHALL be inferred from instrument labels.
|
||||||
|
|
||||||
|
#### Scenario: Same-market cross and isolated positions
|
||||||
|
- **WHEN** a Phoenix wallet holds SOL in Cross and an isolated subaccount
|
||||||
|
- **THEN** both positions retain distinct IDs and their actual account membership
|
||||||
|
|
||||||
|
#### Scenario: Changed and unknown positions
|
||||||
|
- **WHEN** a successful read returns newly opened positions including an unknown token mint
|
||||||
|
- **THEN** all returned positions appear with stable IDs and exact USD sizes, and the unknown mint is displayed rather than dropped
|
||||||
|
|
||||||
|
### Requirement: Background Jupiter refresh
|
||||||
|
The service SHALL refresh immediately after startup and wait 60 seconds after completed cycles without overlap or dependence on EMC visibility. It SHALL use a dedicated read-only Jupiter overview and the Phoenix account reader. Snapshot reads SHALL make no integration calls. No signing or position-management operation SHALL occur; existing transaction/alarm reads SHALL retain their behavior.
|
||||||
|
|
||||||
|
#### Scenario: Background update while EMC is hidden
|
||||||
|
- **WHEN** EMC is hidden and the next refresh is due
|
||||||
|
- **THEN** the service refreshes configured wallets and publishes immutable snapshots
|
||||||
|
- **AND** all UI snapshot reads remain in-memory
|
||||||
|
|
||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: Complete gross and account-aware totals
|
||||||
|
Exposure and dollar price PnL SHALL sum through all levels. Exposure SHALL be gross, with positive absolute notionals. Capital and closing Value SHALL count isolated positions and shared Cross accounts once. Missing required components SHALL make only the affected total unavailable. Successfully read empty scopes SHALL total zero. Leverage and PnL percentages SHALL NOT be aggregated.
|
||||||
|
|
||||||
|
#### Scenario: Opposite directions and shared collateral
|
||||||
|
- **WHEN** two Cross positions have notionals 100 USD Long and 100 USD Short, shared collateral 80 USD, combined PnL 5 USD and closing fees 1 USD
|
||||||
|
- **THEN** exposure totals 200 USD, collateral 80 USD and Value 84 USD at account, venue, wallet and root levels
|
||||||
|
- **AND** collapsing any UI group does not change totals
|
||||||
|
|
||||||
|
#### Scenario: First read fails
|
||||||
|
- **WHEN** one configured venue has never been read successfully
|
||||||
|
- **THEN** it remains unknown and cannot silently produce complete portfolio totals
|
||||||
|
|
||||||
|
### Requirement: Retained observations communicate status
|
||||||
|
Each venue SHALL carry its last-success time and whether the latest attempt failed. Failure SHALL preserve positions and account balances independently per wallet/venue. A valid funded empty Cross account SHALL remain visible. Retained data SHALL NOT be described as fresh.
|
||||||
|
|
||||||
|
#### Scenario: Last Cross position closes
|
||||||
|
- **WHEN** a successful refresh returns a Cross account with nonzero collateral and no positions
|
||||||
|
- **THEN** its capital and Value remain included without synthetic positions
|
||||||
|
|
||||||
|
#### Scenario: Retained last success
|
||||||
|
- **WHEN** a subsequent read fails
|
||||||
|
- **THEN** its previous observations remain available with their original success time and failure indication
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
## Purpose
|
||||||
|
|
||||||
|
Provide precise read-only Jupiter position metrics for capital and risk inspection without altering transaction or alarm behavior.
|
||||||
|
|
||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: Coherent read-only position overview
|
||||||
|
The overview SHALL read the official positions response for one wallet, validate completeness and unique stable identities, and return immutable results. USD integer amounts SHALL be converted from micro-USD without binary floating point. Requests SHALL be interruptible and have bounded timeouts; failed or malformed reads SHALL NOT become empty portfolios.
|
||||||
|
|
||||||
|
#### Scenario: Incomplete response
|
||||||
|
- **WHEN** count differs from the returned position count, or identities are duplicated
|
||||||
|
- **THEN** the read fails and no partial portfolio is published
|
||||||
|
|
||||||
|
#### Scenario: Valid empty response
|
||||||
|
- **WHEN** the response contains count zero and an empty position list
|
||||||
|
- **THEN** the successful result clears that wallet's Jupiter scope
|
||||||
|
|
||||||
|
### Requirement: Verified metrics and unavailable valuations
|
||||||
|
The overview SHALL preserve size basis, use post-fee USD collateral and price PnL before separate fees, and calculate leverage and ROI against positive collateral. Unverified closing valuations SHALL be unavailable with an explanation; placeholder zeros and double subtraction of settled fees SHALL NOT be used.
|
||||||
|
|
||||||
|
#### Scenario: Unverified API closing value
|
||||||
|
- **WHEN** API value subtracts opening fees already settled in collateral or does not establish profit-cap valuation
|
||||||
|
- **THEN** closing Value is unavailable, while independently verified metrics remain available
|
||||||
|
|
||||||
|
#### Scenario: Nonpositive collateral
|
||||||
|
- **WHEN** a position's collateral is zero or negative
|
||||||
|
- **THEN** its leverage and PnL percentage are unavailable
|
||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: Separate account capital and precise position metrics
|
||||||
|
The reader SHALL return immutable account balances separately from positions, including funded empty Cross accounts. Quote lots SHALL convert at 1e-6 USD. Price PnL SHALL use precise virtual quote and signed base amounts at mark. Initial position margin SHALL follow size-dependent tiers including venue interpolation and rounding. Unsupported non-USDC collateral SHALL make affected valuations unavailable instead of being ignored.
|
||||||
|
|
||||||
|
#### Scenario: Size between margin tiers
|
||||||
|
- **WHEN** a position lies between two leverage tiers
|
||||||
|
- **THEN** its initial margin follows interpolated integer leverage and upward quote-lot rounding, rather than the market's maximum leverage
|
||||||
|
|
||||||
|
#### Scenario: Unsupported spot collateral
|
||||||
|
- **WHEN** an account has nonzero spot collateral that cannot be valued reliably
|
||||||
|
- **THEN** collateral, equity and closing Value are unavailable while independent exposure and price PnL remain available
|
||||||
|
|
||||||
|
### Requirement: Estimated account and isolated closing proceeds
|
||||||
|
Equity SHALL include collateral, undiscounted price PnL and signed unsettled funding once. Estimated Value SHALL subtract estimated closing taker fees with account fee overrides. Settled fees/funding SHALL NOT be subtracted again. Isolated position leverage/ROI SHALL use actual collateral; Cross position leverage/ROI SHALL remain unavailable unless their venue convention is verified.
|
||||||
|
|
||||||
|
#### Scenario: Funding signs and fee override
|
||||||
|
- **WHEN** collateral is 100 USD, PnL 5 USD, unsettled funding -2 USD, and estimated exit fee 1 USD
|
||||||
|
- **THEN** equity is 103 USD and estimated Value 102 USD
|
||||||
|
- **AND** effective Cross leverage uses 103 USD as its denominator
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# Tasks
|
||||||
|
|
||||||
|
## 1. Snapshot and totals
|
||||||
|
|
||||||
|
- [x] 1.1 Add immutable metrics, Cross accounts and read status; verify gross totals, missing components and no shared-capital double counting with a temporary focused harness.
|
||||||
|
- [x] 1.2 Update the service contract documentation and verify it distinguishes unknown, empty and retained observations.
|
||||||
|
|
||||||
|
## 2. Venue valuations and refresh
|
||||||
|
|
||||||
|
- [x] 2.1 Add and wire an overview-only Jupiter REST reader; verify live units, completeness, unsupported Value, positive/negative PnL and zero denominators with read-only data and temporary fixtures; document valuation definitions.
|
||||||
|
- [x] 2.2 Extend Phoenix account reads and exact valuation/margin rules; verify tiers, funding signs, fee overrides, unknown collateral and funded empty accounts with temporary fixtures and live data; update Phoenix documentation.
|
||||||
|
- [x] 2.3 Publish per-wallet/venue immutable refresh results and status; verify failure retention, recovery to empty, independent attempts and parameterless no-I/O reads with a temporary harness; update shared Evelyn documentation.
|
||||||
|
|
||||||
|
## 3. Presentation
|
||||||
|
|
||||||
|
- [x] 3.1 Implement eight columns, root/Cross hierarchy, signed formatting and tooltips; verify a standalone JavaFX preview with real read-only data plus controlled edge cases, both environments and preserved tree state.
|
||||||
|
- [x] 3.2 Document UI definitions and unavailable metrics; verify documentation agrees with rendered behavior.
|
||||||
|
|
||||||
|
## 4. Integrated verification
|
||||||
|
|
||||||
|
- [x] 4.1 Run the normal Gradle build and applicable existing checks, validate OpenSpec strictly, and record observation times, preview evidence and remaining valuation limitations in verification.md.
|
||||||
|
- [x] 4.2 Inspect the final diffs for unintended changes and confirm no commits, pushes, shared Detag edits or live Hub restart occurred.
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
# Verification — issue #100
|
||||||
|
|
||||||
|
Verified locally on 24 October 2026; no live Hub was started or restarted, and no
|
||||||
|
signing, trading, activation, deposits or withdrawals were performed. Changes are
|
||||||
|
uncommitted and unpushed. Existing staged/deleted change-97/change-99 artifacts
|
||||||
|
and the pre-existing untracked NFS log file were left untouched.
|
||||||
|
|
||||||
|
## Build and focused checks
|
||||||
|
|
||||||
|
- `JAVA_HOME=/usr/local/software/java/jfx-25 ./gradlew build` passed against the
|
||||||
|
final sources: 42 existing tests, zero failures/errors. Some intermediate runs
|
||||||
|
encountered transient NFS locks while deleting binary test results; normal
|
||||||
|
reruns succeeded without changing build configuration.
|
||||||
|
- Temporary `/tmp/issue100-check/Issue100Check.java` passed 66 focused checks.
|
||||||
|
No permanent test suite was added. Fixtures exercised gross Long/Short totals,
|
||||||
|
once-only Cross capital/Value, missing components, valid empty scopes, immutable
|
||||||
|
collections, positive/negative PnL, zero/negative denominators, Jupiter response
|
||||||
|
completeness/duplicates/units, Phoenix funding signs and fee overrides,
|
||||||
|
size-dependent tiers and quote-lot rounding, negative base-lot exponents,
|
||||||
|
unsupported spot collateral, funded empty Cross accounts, failed refresh,
|
||||||
|
retained timestamps, recovery to empty, independent wallet/venue attempts,
|
||||||
|
no-I/O snapshot access and Phoenix 429 cooldown.
|
||||||
|
- Hand-checkable Cross fixture: 100 USD Long + 100 USD Short = 200 USD gross
|
||||||
|
exposure; shared collateral 80, price PnL +5, net unsettled funding 0 and
|
||||||
|
exit fees 1 give equity 85 and Value 84. Cross leverage is 200/85. Initial
|
||||||
|
margin requirements of 10 per child are not added to the 80 owned capital.
|
||||||
|
- Tier fixture: 150 base lots at exponent 2 and price 100.009, tick size 100,
|
||||||
|
between tiers (100 lots, 10×) and (200 lots, 5×), uses integer leverage 7
|
||||||
|
and initial margin 21.428572 USD. Exposure remains 150.0135 USD.
|
||||||
|
|
||||||
|
## Read-only live comparison
|
||||||
|
|
||||||
|
The new adapters were read directly, outside the Hub, from
|
||||||
|
**2026-10-24 10:18:38 UTC to 10:19:13 UTC** (12:18–12:19 CEST).
|
||||||
|
The controlled PROD wallet was `H5Yrdm7B4FHhTxhPw9SVwFzQyTEL6bAPptZuTyRxcepq`;
|
||||||
|
the existing TEST wallet was `6NyYEoXmJyBXvU4pvv9aoxZNdyqqtkmPBZFEDkRDHcqp`.
|
||||||
|
The other five PROD wallets remain configured, but were not all read for this
|
||||||
|
preview. Counts and prices are observations, not fixed expectations.
|
||||||
|
|
||||||
|
| Observed scope | Positions | Collateral USD | Gross Exposure USD | Price uPnL USD | Estimated Value USD |
|
||||||
|
| --- | ---: | ---: | ---: | ---: | ---: |
|
||||||
|
| Controlled PROD wallet, Phoenix | 32 | 938.131075 | 4527.060948 | -328.677770 | 608.027266 |
|
||||||
|
| Controlled PROD wallet, Jupiter | 6 | 54.993558 | 704.568047 | -15.752428 | unavailable |
|
||||||
|
| Controlled PROD wallet, combined | 38 | 993.124633 | 5231.628995 | -344.430198 | unavailable |
|
||||||
|
| TEST wallet, both venues | 0 | 0 | 0 | 0 | 0 |
|
||||||
|
|
||||||
|
Jupiter API units and collateral were independently checked against a read-only
|
||||||
|
Solana `getMultipleAccounts` response for all six returned position identities.
|
||||||
|
API `sizeUsd` and `collateralUsd` matched the on-chain values exactly. USD and
|
||||||
|
price fields use six decimal places; percentage fields are already percentage
|
||||||
|
points. The UI's leverage and isolated PnL percentage are derived independently
|
||||||
|
from precise collateral, rather than using rounded API ratios.
|
||||||
|
|
||||||
|
Phoenix uses exact signed quantity × REST mark plus virtual quote for price
|
||||||
|
PnL; quote lots are 1e-6 USD. Estimated equity adds signed unsettled funding,
|
||||||
|
and Value subtracts estimated closing taker fees with the REST account override.
|
||||||
|
Settled funding/fees are not charged again. Tier interpolation and integer price
|
||||||
|
rounding were cross-checked against the official SDK `margin/math.ts`,
|
||||||
|
`margin/compute.ts`, `units.ts` and Rust leverage-tier implementation.
|
||||||
|
|
||||||
|
## JavaFX verification
|
||||||
|
|
||||||
|
A standalone `/tmp/issue100-check/PerpsPreview.java` used the actual immutable
|
||||||
|
read-only PROD/TEST observations above. Controlled edge cases were a separate
|
||||||
|
preview tab and are absent from application sources. It checked eight columns,
|
||||||
|
the visible root, Cross grouping, preserved selection/expansion/row identity on
|
||||||
|
unchanged and changed snapshots, positive/negative/rounded-zero PnL and tooltip
|
||||||
|
definitions. Both 1100-pixel and 600-pixel layouts were inspected; the latter
|
||||||
|
has horizontal scrolling at useful column widths. Root labels use explicit readable text color, including after selection and tab
|
||||||
|
changes; column factories are installed before the initial root.
|
||||||
|
|
||||||
|
Screenshots produced and visually inspected:
|
||||||
|
|
||||||
|
- `/tmp/issue100-check/preview-prod.png`
|
||||||
|
- `/tmp/issue100-check/preview-test.png`
|
||||||
|
- `/tmp/issue100-check/preview-edges.png`
|
||||||
|
- `/tmp/issue100-check/preview-tooltip.png`
|
||||||
|
- `/tmp/issue100-check/preview-small-window.png`
|
||||||
|
|
||||||
|
## OpenSpec and documentation
|
||||||
|
|
||||||
|
The change and all four affected main specifications pass strict validation:
|
||||||
|
|
||||||
|
```text
|
||||||
|
openspec validate 100-perps-capital-and-risk --strict
|
||||||
|
openspec validate evelyn-perps-service --type spec --strict
|
||||||
|
openspec validate evelyn-perps-overview --type spec --strict
|
||||||
|
openspec validate phoenix-perps-service --type spec --strict
|
||||||
|
openspec validate jupiter-perps-overview --type spec --strict
|
||||||
|
```
|
||||||
|
|
||||||
|
Main specs were synchronized; the change remains active for review. Project
|
||||||
|
Evelyn/Phoenix documentation and the shared `r35157_docs/projects/evelyn/README.md`
|
||||||
|
were updated. `git diff --check` passed. Whole-project `openspec validate --all
|
||||||
|
--strict` reports 24 passed and two pre-existing failures: placeholder Purpose
|
||||||
|
sections in `javafx-runtime-service` and `nenjim-process-manager`. Those unrelated
|
||||||
|
specifications were not changed.
|
||||||
|
|
||||||
|
## Known unavailable metrics and valuation limits
|
||||||
|
|
||||||
|
- **Jupiter Value is unavailable**, including higher totals requiring it. The
|
||||||
|
observed API `valueUsd` subtracts opening fees from collateral that already
|
||||||
|
matches post-fee on-chain collateral. Its collateral-valuation/profit-cap
|
||||||
|
treatment is not verified. No fabricated zero, partial total or speculative
|
||||||
|
fee-only correction is displayed; see `docs/JupiterPerpsOverview.md`.
|
||||||
|
- Phoenix Cross position leverage/ROI conventions remain unverified. Those
|
||||||
|
ratios are unavailable; dollar PnL is shown and account effective leverage
|
||||||
|
is provided. Individual Cross position Value is inapplicable.
|
||||||
|
- Nonzero unsupported spot collateral or missing funding/fees/PnL makes the
|
||||||
|
affected collateral/equity/Value unavailable. This version does not claim full
|
||||||
|
account utilization or include open-order risk adjustments in such a metric.
|
||||||
|
- Valuations use periodic reference observations, not executable prices.
|
||||||
|
Slippage, price impact, swaps, network fees, account rent and future funding
|
||||||
|
are excluded. Phoenix state and prices are separate REST observations.
|
||||||
|
Failed refreshes can retain old data indefinitely with explicit status/time.
|
||||||
|
- No changes were made to shared `detag.conf`, wallet lists, trading/alarm
|
||||||
|
implementations, EMC polling cadence or the 60-second post-cycle delay.
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
schema: spec-driven
|
||||||
|
created: 2026-10-24
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# Design
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
See proposal.md for motivation. Phoenix account reads already fetch one mark per held market and are published atomically per wallet/venue. A failed read retains that venue's complete previous snapshot. EMC reconciles immutable snapshots and has no exchange dependencies.
|
||||||
|
|
||||||
|
## Goals / Non-Goals
|
||||||
|
|
||||||
|
**Goals:** Attach precise, optional TWAP observations to existing positions and preserve their observation time through the service/UI boundary.
|
||||||
|
|
||||||
|
**Non-Goals:** Order placement, rows for unfilled orders, group summaries, new polling loops, live countdown timers or Ticker changes.
|
||||||
|
|
||||||
|
## Decisions
|
||||||
|
|
||||||
|
1. Read one TWAP snapshot per wallet with held positions, through the existing paced HTTP adapter. Validate authority/PDA/subaccount/asset identities against market metadata and trader state. Attach only orders with positive filled base lots and remaining children; never synthesize a position. A TWAP/schema/HTTP failure fails the complete venue read rather than masquerading as order disappearance. Independent partial TWAP refresh/cache would require a larger status model.
|
||||||
|
2. Keep dedicated immutable Phoenix and Evelyn TWAP domain records, with exact integer counts, decimal quantities, a nullable mark and observation/estimated instants. Existing position constructor overloads remain convenient for consumers without TWAP data. A valid absent mark yields unknown price-dependent metrics and still exposes quantities; invalid prices and HTTP failures remain errors.
|
||||||
|
3. Anchor `dueSlot - snapshot.slot` at response receipt and use nominal 200 ms per slot. Completion is `dueSlot + (remaining - 1) × cooldownSlots`; expiry is never used as completion. Already-due schedules, expiry before completion and unrepresentable estimates yield unavailable timing. These estimates require no new RPC client, network dependency or sampling cache. The current nominal 200 ms is documented by the [Solana Foundation](https://solana.com/fr/upgrades/reduced-slot-times), updated October 2026 (400 ms is obsolete). Measured slot duration is deferred; nominal estimates and observation age are explicit in tooltips/docs.
|
||||||
|
4. Decode snapshot counts as total children including dust. Explicit dust subtracts its count from total; legacy positive dust size with zero count replaces one final regular child. Missing dust metadata makes sizes unknown, never silently assumes an equal split. The next child switches to dust once regular children have executed.
|
||||||
|
5. Append a graphic TWAP column using a ProgressBar and overlaid duration label. Counters measure execution count, with fills shown separately. Duration uses integer total hours and updates only with new snapshots. Failed venues show `Retained` instead of a fresh countdown. Tooltips show approximate USD values from the same mark already used for the position and state snapshot timing limitations.
|
||||||
|
|
||||||
|
## Risks / Trade-offs
|
||||||
|
|
||||||
|
- Nominal slot speed, API lag and delayed child execution → label estimates, show observation time, do not imply actual execution or advance counts locally.
|
||||||
|
- More sequential reads → retain shared pacing/cooldown, skip TWAP reads without held positions; preserve 60-second post-cycle pause.
|
||||||
|
- Nullable marks affect valuations → propagate unknowns into affected totals rather than publish partial totals or hide held positions.
|
||||||
|
- Completed orders can disappear from the active endpoint → no historical completion claim or synthetic completed-order cache.
|
||||||
|
|
||||||
|
## Migration Plan
|
||||||
|
|
||||||
|
No persisted data or configuration changes. Build and inspect focused temporary harnesses/JavaFX previews, update existing documentation and sync the three delta specs. Leave the running Hub untouched; rollback is reverting the source change before a normal owner-controlled deployment.
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# Proposal
|
||||||
|
|
||||||
|
## Why
|
||||||
|
|
||||||
|
The owner needs to distinguish ongoing Phoenix TWAP execution from the current position size in EMC. Issue #101 requests progress and the estimated wait until the next child without changing refresh frequency or exposing unfilled orders.
|
||||||
|
|
||||||
|
## What Changes
|
||||||
|
|
||||||
|
- Read Phoenix TWAP snapshots alongside existing account observations and attach only filled orders to matching existing positions.
|
||||||
|
- Add a ninth TWAP column with child-count progress, elapsed-hour duration text and detailed estimated timing/quantity tooltips.
|
||||||
|
- Reuse Phoenix mark prices, preserving quantities when a valid response has no mark price and propagating unknown valuations.
|
||||||
|
- Preserve whole-venue observations on TWAP errors; document periodic timing and nominal slot estimates.
|
||||||
|
|
||||||
|
## Capabilities
|
||||||
|
|
||||||
|
### New Capabilities
|
||||||
|
|
||||||
|
None.
|
||||||
|
|
||||||
|
### Modified Capabilities
|
||||||
|
|
||||||
|
- `phoenix-perps-service`: precise TWAP quantities, identities, optional mark prices and read failure behavior.
|
||||||
|
- `evelyn-perps-service`: immutable TWAP observations on existing positions, with retained venue status.
|
||||||
|
- `evelyn-perps-overview`: TWAP progress, long durations and detailed tooltips.
|
||||||
|
|
||||||
|
## Impact
|
||||||
|
|
||||||
|
Phoenix DTOs/reader, Evelyn snapshot/mapping, JavaFX Perps cells and relevant existing project/shared documentation. No trading, new dependencies, faster polling, commits or pushes.
|
||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
## MODIFIED Requirements
|
||||||
|
|
||||||
|
### Requirement: Perps overview displays a collapsible position hierarchy
|
||||||
|
The Perps tab SHALL display Position, Direction, Margin, Collateral, Leverage, Exposure, Value, uPnL and TWAP in that order beneath the visible Perpetual Contracts root. Isolated hierarchy SHALL be wallet → venue → position; each Phoenix Cross account SHALL add a Cross group above its positions. Groups SHALL show applicable totals, leave direction/aggregate ratios and TWAP blank, and be expanded by default.
|
||||||
|
|
||||||
|
#### Scenario: Open either environment's Perps tab
|
||||||
|
- **WHEN** the owner opens Perps in Production or Test
|
||||||
|
- **THEN** all nine columns, root totals and the account-aware hierarchy are available, with horizontal scrolling where required
|
||||||
|
|
||||||
|
#### Scenario: Collapse and expand groups
|
||||||
|
- **WHEN** the owner collapses and expands a wallet, venue or Cross account
|
||||||
|
- **THEN** descendants hide and return without changing totals
|
||||||
|
|
||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: TWAP cells show execution progress and snapshot duration
|
||||||
|
Matching filled Phoenix TWAPs SHALL show a progressbar using executed/total child counts and an overlaid estimated duration to the next child in unbounded hours, minutes and seconds. Other cells SHALL be empty. Updates SHALL follow existing snapshots without a live timer. Failed retained observations SHALL be labeled retained; due or unknown schedules SHALL not show negative or wrapped times or imply execution.
|
||||||
|
|
||||||
|
#### Scenario: Long interval
|
||||||
|
- **WHEN** the snapshot's estimated wait is 28 hours 32 minutes 10 seconds, or 128 hours 32 minutes 10 seconds
|
||||||
|
- **THEN** the text is approximately 28:32:10 or 128:32:10 respectively
|
||||||
|
|
||||||
|
#### Scenario: Failed or due observation
|
||||||
|
- **WHEN** the venue read fails or the latest observed next slot is already due
|
||||||
|
- **THEN** the cell communicates retained or awaiting-update status without advancing progress
|
||||||
|
|
||||||
|
### Requirement: TWAP tooltip distinguishes estimates from fills
|
||||||
|
TWAP tooltips SHALL show executed/total progress, Estimated completion, total size, next child size, filled quantity and observation time. Sizes SHALL include their symbol and approximate USD values from Phoenix mark prices where available. Distinct regular/dust sizes and periodic nominal-slot timing assumptions SHALL be explained. Unknown values SHALL remain unknown; expiry SHALL never be presented as completion.
|
||||||
|
|
||||||
|
#### Scenario: Missing price or smaller next child
|
||||||
|
- **WHEN** the mark price is unavailable or the next child is a smaller rest order
|
||||||
|
- **THEN** token amounts remain visible with unknown USD values, or the next size reflects that rest order respectively
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: TWAP observations belong to existing filled positions
|
||||||
|
The service SHALL publish immutable TWAP observations only on matching existing Phoenix positions whose order has a positive actual fill and remaining executions. It SHALL preserve counters, quantities, nullable price and observation/estimated times without UI formatting. No new position SHALL be synthesized. TWAP errors SHALL retain the complete prior venue observation with failure status. Polling frequency SHALL remain unchanged.
|
||||||
|
|
||||||
|
#### Scenario: Unfilled order or no position
|
||||||
|
- **WHEN** an active TWAP has no actual fills or has no matching open position
|
||||||
|
- **THEN** it is absent from every overview row and cannot create a visible venue
|
||||||
|
|
||||||
|
#### Scenario: TWAP read fails
|
||||||
|
- **WHEN** the TWAP read fails after a successful venue observation
|
||||||
|
- **THEN** prior positions, TWAP observations and original timestamps remain with failed venue status
|
||||||
|
- **AND** other venues and wallets continue refreshing
|
||||||
+44
@@ -0,0 +1,44 @@
|
|||||||
|
## MODIFIED Requirements
|
||||||
|
|
||||||
|
### Requirement: Read failures cannot masquerade as empty portfolios
|
||||||
|
HTTP, authentication, malformed schema, identity mismatch and numeric conversion errors SHALL fail the read. Requests SHALL have bounded timeouts. Only a valid response with no nonzero positions SHALL produce an empty result. Present mark prices SHALL be positive and finite; an explicitly absent mark SHALL preserve positions and quantities with unavailable price-dependent valuations.
|
||||||
|
|
||||||
|
#### Scenario: Missing mark price
|
||||||
|
- **WHEN** a valid mark response has no mark price for a held position
|
||||||
|
- **THEN** the position and its TWAP quantities remain present with unknown USD valuations
|
||||||
|
- **AND** affected aggregate valuations remain unknown
|
||||||
|
|
||||||
|
#### Scenario: Rejected access or invalid price
|
||||||
|
- **WHEN** an HTTP request is rejected or a present mark price is invalid
|
||||||
|
- **THEN** the read fails instead of returning an empty or partial portfolio
|
||||||
|
|
||||||
|
#### Scenario: Empty account
|
||||||
|
- **WHEN** a valid trader snapshot has no subaccounts or no nonzero positions
|
||||||
|
- **THEN** an empty result is returned without market-price or TWAP requests
|
||||||
|
|
||||||
|
#### Scenario: Public API rate limit
|
||||||
|
- **WHEN** the shared adapter issues requests for PROD and TEST
|
||||||
|
- **THEN** request starts SHALL be spaced at least one second apart
|
||||||
|
- **AND** HTTP 429 SHALL fail the lookup and establish a shared cooldown from Retry-After (seconds or HTTP date, 60 seconds fallback)
|
||||||
|
- **AND** lookups during cooldown SHALL fail without external calls, with no immediate retry
|
||||||
|
|
||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: Read precise TWAP snapshots for held markets
|
||||||
|
The reader SHALL read official TWAP snapshots through its existing paced transport and associate orders by authority, PDA, subaccount and market identity. Only orders with actual fills and remaining children on an existing position SHALL be exposed. Quantities SHALL use exact lot conversion and explicit/legacy dust semantics. Unavailable size inputs SHALL remain unknown. Existing mark prices SHALL be reused without Ticker reads.
|
||||||
|
|
||||||
|
#### Scenario: Explicit and legacy dust
|
||||||
|
- **WHEN** total children are 5, regular size 10 lots and explicit dust count/size are 2/3
|
||||||
|
- **THEN** total size is 36 lots and the fourth child is 3 lots
|
||||||
|
- **AND** legacy zero dust count with size 3 instead represents four regular children and a final 3-lot child
|
||||||
|
|
||||||
|
#### Scenario: Identity mismatch or unfilled order
|
||||||
|
- **WHEN** authority/PDA/subaccount/asset identities disagree or an order has no fills
|
||||||
|
- **THEN** disagreement fails the read, while a valid unfilled order is absent from returned positions' TWAP data
|
||||||
|
|
||||||
|
### Requirement: TWAP time estimates disclose their slot assumption
|
||||||
|
The reader SHALL estimate future execution times from snapshot slot, due slot and cooldown using a documented nominal slot duration and response observation time. Completion SHALL derive from remaining executions, never expiry. Already-due schedules, invalidated completion schedules and unrepresentable estimates SHALL remain unavailable instead of predicting fills.
|
||||||
|
|
||||||
|
#### Scenario: Expiry before planned completion
|
||||||
|
- **WHEN** the scheduled final child lies beyond the order's expiry
|
||||||
|
- **THEN** the reader does not provide that schedule as an estimated completion time
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# Tasks
|
||||||
|
|
||||||
|
## 1. Phoenix observations
|
||||||
|
|
||||||
|
- [x] 1.1 Add immutable TWAP data and paced snapshot decoding with precise sizes, identities, nullable marks and estimates; verify with focused temporary fixtures for dust, missing prices, errors and matching.
|
||||||
|
- [x] 1.2 Update Phoenix integration documentation for the implemented endpoint, units and timing/error behavior; verify against code and source contracts.
|
||||||
|
|
||||||
|
## 2. Evelyn and EMC
|
||||||
|
|
||||||
|
- [x] 2.1 Map TWAP data into immutable Evelyn position snapshots and render progressbar cells/tooltips; verify long durations, hidden orders, blank groups and retained failures in temporary service/UI checks.
|
||||||
|
- [x] 2.2 Update existing Evelyn and shared r35157_docs documentation to match the ninth column, visibility and estimate limitations; verify links and remove conflicting claims.
|
||||||
|
|
||||||
|
## 3. Integration verification
|
||||||
|
|
||||||
|
- [x] 3.1 Run the normal build, focused read-only live/fixture checks and JavaFX visual checks; record results and material limitations.
|
||||||
|
- [x] 3.2 Sync delta specs, validate the change and affected main specs, and inspect final diffs without committing or pushing.
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
# Verification — issue #101
|
||||||
|
|
||||||
|
Verified locally on 2026-10-24. No Hub restart, deployment, signing, trading,
|
||||||
|
commit or push was performed. Existing unrelated staged/deleted OpenSpec files
|
||||||
|
were preserved.
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
`JAVA_HOME=/usr/local/software/java/jfx-25 ./gradlew build` succeeded.
|
||||||
|
All 42 existing tests passed, with zero failures/errors. No permanent tests were
|
||||||
|
added; focused harnesses and screenshots are temporary files under `/tmp`.
|
||||||
|
|
||||||
|
## Focused integration checks
|
||||||
|
|
||||||
|
`Issue101Check` passed 48 checks covering:
|
||||||
|
|
||||||
|
- Explicit, legacy, absent and invalid dust metadata; exact total/next/fill quantities.
|
||||||
|
- Positive fills required even on existing positions; no order-only synthetic rows.
|
||||||
|
- Same-symbol Cross/isolated matching, authority/PDA/asset mismatch and duplicate rejection.
|
||||||
|
- Invalid counters, completed observations, already-due timing, expiry before completion and overflow.
|
||||||
|
- Missing marks preserving token amounts and propagating unknown price-dependent totals.
|
||||||
|
- One TWAP and one mark request for two same-market positions; no extra reads for empty wallets.
|
||||||
|
- HTTP/TWAP failures retaining the complete prior venue observation, original timestamp and independent Jupiter refresh.
|
||||||
|
- Snapshot reads without I/O, recovery clearing absent/unfilled TWAPs, and shared 429 cooldown without immediate retry.
|
||||||
|
- Decoding captured real Phoenix data, including the BTC order with 236 smaller children out of 237 total executions.
|
||||||
|
|
||||||
|
## JavaFX and visual inspection
|
||||||
|
|
||||||
|
`Issue101Preview` passed 33 checks. Inspected the actual rendered table and tooltip:
|
||||||
|
nine columns; visible progress and overlaid `≈ 28:32:10` / `≈ 128:32:10`; blank
|
||||||
|
groups/Jupiter/non-TWAP cells; missing USD values; due/retained labels; tooltip fields;
|
||||||
|
clearing recycled cell graphics/tooltips; preserved selection/expansion; horizontal
|
||||||
|
scrolling at small widths. Temporary captures are under `/tmp/issue101-check/`.
|
||||||
|
|
||||||
|
## Read-only live check
|
||||||
|
|
||||||
|
At 2026-10-24 17:07:27–17:08:03 UTC, the real adapter returned 32 positions and 32
|
||||||
|
active filled TWAPs for the checked PROD wallet and no positions for TEST. Only GET
|
||||||
|
requests were used, with existing pacing/timeouts. Observed SOL order: 890/1779,
|
||||||
|
17.79 SOL total and 0.01 SOL next. Observed BTC order: 119/237, 0.0238 BTC total and
|
||||||
|
0.0001 BTC next. Marks were taken directly from Phoenix, with no Ticker dependency.
|
||||||
|
|
||||||
|
## Specifications and documentation
|
||||||
|
|
||||||
|
- All three deltas synced into existing main specs.
|
||||||
|
- Strict validation passed for `101-phoenix-twap-progress`, `phoenix-perps-service`,
|
||||||
|
`evelyn-perps-service` and `evelyn-perps-overview`.
|
||||||
|
- Project/shared documentation links and `git diff --check` passed.
|
||||||
|
- Checking the entire existing index still reports three trailing-blank-line warnings
|
||||||
|
in the unrelated, pre-existing staged issue #97 specs. The index check passes when
|
||||||
|
restricted to this change; those older files were not edited.
|
||||||
|
|
||||||
|
## Limits
|
||||||
|
|
||||||
|
The current nominal slot duration is 200 ms according to the Solana Foundation's
|
||||||
|
October 2026 update, superseding the old 400 ms assumption. Response receipt is only
|
||||||
|
an approximate time anchor. API lag, varying slot times and execution delays can
|
||||||
|
shift estimates, especially long completion estimates. Cell duration is explicitly
|
||||||
|
as of Updated and does not tick between snapshots. Progress counts child executions,
|
||||||
|
not guaranteed filled quantity. USD sizes are current approximate notionals before
|
||||||
|
fees. The existing polling cycle and live Hub were left unchanged.
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
schema: spec-driven
|
||||||
|
created: 2026-10-24
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# Design
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
See proposal.md. Venue snapshots include successful-empty, not-yet-read and failed scopes. Cross accounts hold capital independently of positions, so position count alone cannot determine visibility.
|
||||||
|
|
||||||
|
## Goals / Non-Goals
|
||||||
|
|
||||||
|
**Goals:** Remove childless headings while preserving capital totals and failure visibility.
|
||||||
|
|
||||||
|
**Non-Goals:** Changing integration reads, snapshot data, wallet visibility or refresh scheduling.
|
||||||
|
|
||||||
|
## Decisions
|
||||||
|
|
||||||
|
- Skip a venue during tree reconciliation only when both its positions and Cross-account lists are empty. Filtering the service snapshot instead would lose unknown scopes and could produce misleading totals.
|
||||||
|
- Include childless venues' read status in wallet tooltip notes so hiding their headings does not hide the reason for unavailable totals. Keep the existing venue tooltips when rows exist.
|
||||||
|
- Reconcile surviving items with the existing IDs; a venue returning after removal is a new expanded group. Its wallet and other venue rows remain retained.
|
||||||
|
|
||||||
|
## Risks / Trade-offs
|
||||||
|
|
||||||
|
- Hidden failed/initial reads can resemble a successfully empty portfolio → preserve unavailable totals and expose the exact status on the wallet row.
|
||||||
|
- A Phoenix account may have capital without positions → treat the Cross account itself as a child.
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
# Proposal
|
||||||
|
|
||||||
|
## Why
|
||||||
|
|
||||||
|
Empty Jupiter and Phoenix headings add clutter to Evelyn's Perps capital/risk overview. The owner wants a venue shown only when it has something beneath it.
|
||||||
|
|
||||||
|
## What Changes
|
||||||
|
|
||||||
|
- Hide venue rows with neither isolated positions nor Cross accounts; show them again when data supplies children.
|
||||||
|
- Retain Phoenix groups containing funded Cross accounts without open positions.
|
||||||
|
- Keep hidden scopes in the service snapshot and totals; expose their read status through wallet tooltips.
|
||||||
|
- Replace documentation that says empty venue headings remain visible.
|
||||||
|
|
||||||
|
## Capabilities
|
||||||
|
|
||||||
|
### New Capabilities
|
||||||
|
|
||||||
|
None.
|
||||||
|
|
||||||
|
### Modified Capabilities
|
||||||
|
|
||||||
|
- `evelyn-perps-overview`: Suppress childless venues without discarding valuation or read-status information.
|
||||||
|
|
||||||
|
## Impact
|
||||||
|
|
||||||
|
Local `.tjava` presentation code, the overview specification, and relevant project/shared documentation only. No integration, refresh, wallet configuration, signing or trading changes. No commits, pushes or live Hub restart.
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: Venue visibility follows available child rows
|
||||||
|
EMC SHALL hide venue rows with neither positions nor Cross accounts and show them when children reappear. Funded empty Cross accounts SHALL keep their venue visible. Hidden venues SHALL remain in snapshots and totals, with read status available in wallet tooltips. Unknown scopes SHALL NOT become zero totals; failed reads SHALL preserve last-success totals. Wallet rows and the root SHALL remain visible.
|
||||||
|
|
||||||
|
#### Scenario: Empty venue
|
||||||
|
- **WHEN** Jupiter or Phoenix has no positions and no Cross accounts
|
||||||
|
- **THEN** its heading is absent from that wallet's displayed children
|
||||||
|
- **AND** a successful empty scope still contributes zero to totals
|
||||||
|
|
||||||
|
#### Scenario: Funded Cross account without positions
|
||||||
|
- **WHEN** Phoenix returns a Cross account with capital but no open positions
|
||||||
|
- **THEN** Phoenix and its Cross account remain visible with that capital counted once
|
||||||
|
|
||||||
|
#### Scenario: Positions disappear and return
|
||||||
|
- **WHEN** a venue loses its last child and later returns a position
|
||||||
|
- **THEN** its heading disappears and reappears with the correct child
|
||||||
|
- **AND** unrelated retained rows keep their selection and expansion state
|
||||||
|
|
||||||
|
#### Scenario: Hidden scope has not been read successfully
|
||||||
|
- **WHEN** a childless venue has never been read successfully and its first read is pending or failed
|
||||||
|
- **THEN** the heading is hidden and its read status is available on the wallet tooltip
|
||||||
|
- **AND** required totals remain unavailable rather than reporting zero
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# Tasks
|
||||||
|
|
||||||
|
## 1. Presentation and documentation
|
||||||
|
|
||||||
|
- [x] 1.1 Filter childless venues and retain their wallet-level read status; verify empty, funded Cross, unknown/failed and returning-position cases in the standalone preview while preserving unrelated tree state.
|
||||||
|
- [x] 1.2 Update the overview specification and relevant project/shared documentation; verify obsolete visible-empty-group claims are removed and links remain valid.
|
||||||
|
|
||||||
|
## 2. Integrated verification
|
||||||
|
|
||||||
|
- [x] 2.1 Run the normal Gradle build and strict validation for the change and affected spec; record the preview/build results.
|
||||||
|
- [x] 2.2 Inspect the final diffs and confirm existing unrelated changes, integration behavior and the no-commit/no-push constraint are preserved.
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# Verification
|
||||||
|
|
||||||
|
Verified locally on 24 October 2026. No commits, pushes or live Hub restart.
|
||||||
|
|
||||||
|
- Normal `JAVA_HOME=/usr/local/software/java/jfx-25 ./gradlew build` passed:
|
||||||
|
42 existing tests, zero failures.
|
||||||
|
- Strict OpenSpec validation passed for this change and `evelyn-perps-overview`;
|
||||||
|
the added requirement is synchronized into the main specification.
|
||||||
|
- Reused the temporary standalone JavaFX preview outside application sources.
|
||||||
|
Checked both empty venues hidden with wallet retained; pending/failed hidden
|
||||||
|
scopes still unavailable in totals with wallet-level status; funded empty
|
||||||
|
Cross account keeping Phoenix visible; Jupiter appearing, disappearing and
|
||||||
|
returning; unrelated Phoenix item identity, selection and collapse retained;
|
||||||
|
a previously successful empty read still contributing zero after a failure.
|
||||||
|
- Existing preview checks for eight columns, formatting and tree reconciliation
|
||||||
|
also passed. Screenshots are under `/tmp/issue100-check/hide-empty-venues/`;
|
||||||
|
the TEST and controlled-case views were visually inspected. The preview reused
|
||||||
|
prior read-only snapshots, with no new exchange reads or application sample data.
|
||||||
|
- Updated `docs/Evelyn.md`, `docs/PhoenixPerps.md` and the canonical shared
|
||||||
|
`r35157_docs/projects/evelyn/README.md`, replacing obsolete claims about visible
|
||||||
|
empty groups and their error/loading indicators. Local links and diff checks passed.
|
||||||
|
- The source change is confined to `PerpsTab.tjava`; snapshots, integrations,
|
||||||
|
totals and refresh timing are unchanged. Existing unrelated staged/deleted
|
||||||
|
OpenSpec artifacts were left untouched.
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
schema: spec-driven
|
||||||
|
created: 2026-10-24
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
# Design
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
See proposal.md for motivation. Positions/account capital and TWAP already travel through immutable integration DTOs and per-venue retained snapshots. EMC polls these snapshots every three seconds, currently skips equal snapshots and reconciles tree items by identity. Risk cannot use closing Value or undiscounted equity as effective collateral.
|
||||||
|
|
||||||
|
## Goals / Non-Goals
|
||||||
|
|
||||||
|
**Goals:** Venue-reported risk with original timestamps, shared-account scope, exact arithmetic and honest incomplete/expired summaries.
|
||||||
|
|
||||||
|
**Non-Goals:** A liquidation engine, position management, notifications, faster polling, external writes or a general risk-policy platform.
|
||||||
|
|
||||||
|
## Decisions
|
||||||
|
|
||||||
|
1. The owner explicitly selected neutral isolated price distance with red only at a reached/crossed boundary. Phoenix tiers retain their venue-specific colors. Do not introduce speculative per-market safety thresholds. Never use rounded display numbers for classification.
|
||||||
|
2. Use five minutes from observation receipt as the maximum age; failures immediately mark retained observations stale. Normal sequential reads across six PROD wallets can approach five minutes, so slow cycles can deliberately produce stale indications. This is a data-quality cutoff, not a claim that prices remain safe for five minutes. Future timestamps are uncertain too. Source/API lag remains an explicit limitation.
|
||||||
|
3. Extend Jupiter's overview-only DTO with micro-USD reference/liquidation prices and the original response observation time. Preserve signing/alarm readers.
|
||||||
|
4. Inject the existing SolanaBlockChain into the Phoenix adapter for local PDA derivation only. Use official program `EtrnLzgbS7nMMy5fbD42kXiUzGg8XQzJ972Xtk1cjWih`, seeds `trader`, authority bytes, one-byte PDA index and one-byte subaccount. GET TraderView per visible account, validate its authority/account/PDA/subaccount and position identities/quantities. Decode value/decimals exactly, ignore UI strings and normalize nonpositive liquidation prices to unavailable. Reuse already-read Phoenix marks with their receipt times; isolated risk freshness uses the oldest required observation. Account risk uses the self-contained TraderView receipt time. No TWAP account key or signature is needed.
|
||||||
|
5. Keep distinct Phoenix risk DTOs and Evelyn risk observations. Cross account risk belongs to CrossAccount; Cross positions only refer to that scope in presentation. Exact signed distance, MM utilization and headroom are domain calculations; colors/text belong to EMC. Missing essential fields remain unknown. A valid account tier can exist without an actionable liquidation price.
|
||||||
|
6. Risk is the second column. A domain helper evaluates each isolated position and each Cross account once, plus unknown/expired venue scopes. Group summaries retain the worst known level, unknown count and stale count, without averaging. Known critical observations remain prominent even if old; incomplete/noncritical summaries are never unconditional green.
|
||||||
|
7. Re-evaluate risk with the existing UI poll and an injectable Clock, including equal snapshots. On a consumer snapshot-read failure, re-render the retained snapshot with an explicit read-failure flag; do not let a failing getter bypass freshness indefinitely. Reconcile immutable row values only when they differ; do not call table.refresh or replace tree items. This expires unchanged snapshots without a new timer/network loop and preserves selection, expansion and TWAP behavior.
|
||||||
|
8. A malformed/rejected risk read fails the venue read and preserves its previous complete observation/status. Missing optional metrics or unknown future risk tiers remain explicitly unavailable. Other wallets/venues continue. Existing request pacing, timeout and cooldown apply to the new GETs.
|
||||||
|
|
||||||
|
## Risks / Trade-offs
|
||||||
|
|
||||||
|
- API/mark/state reads are not atomic → validate identity/position size, use oldest required receipt time and explain reference-price lag.
|
||||||
|
- Extra per-account reads → reuse current pacing; no risk reads for invisible empty isolated accounts; source address derivation is local.
|
||||||
|
- Nullable/sentinel fields → independent valid metrics survive, missing risk never becomes zero or an all-clear.
|
||||||
|
- Different venues have different meanings → isolated distance and Cross MM utilization retain distinct labels and definitions.
|
||||||
|
|
||||||
|
## Migration Plan
|
||||||
|
|
||||||
|
No persisted data or owner configuration migration. Wire the existing Solana dependency in NenjimHub, build and verify with temporary fixtures, UI checks and read-only observations. Update existing docs/shared docs and sync affected specs. Leave deployment and the running Hub unchanged.
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# Proposal
|
||||||
|
|
||||||
|
## Why
|
||||||
|
|
||||||
|
The owner cannot currently distinguish liquidation risk from profitability in EMC. Issue #102 requires venue-reported risk at isolated-position and shared Cross-account level, including honest handling of old or incomplete observations.
|
||||||
|
|
||||||
|
## What Changes
|
||||||
|
|
||||||
|
- Add venue reference/liquidation prices to Jupiter's overview-only observations.
|
||||||
|
- Read Phoenix TraderView risk using officially derived trader addresses independently of TWAP, preserving account thresholds and sentinel handling.
|
||||||
|
- **BREAKING**: Phoenix adapter constructors now require the existing SolanaBlockChain dependency for local address derivation; Hub wiring is updated.
|
||||||
|
- Add a Risk column, scope-aware summaries and freshness expiration during existing UI polling.
|
||||||
|
- Use the owner's selected policy: neutral isolated distance, red only for reached/crossed boundary; Phoenix's own tiers retain their colors.
|
||||||
|
- Expire risk observations at five minutes without implying a five-minute safety guarantee. Preserve prior critical states with age/failure annotations.
|
||||||
|
|
||||||
|
## Capabilities
|
||||||
|
|
||||||
|
### New Capabilities
|
||||||
|
|
||||||
|
None.
|
||||||
|
|
||||||
|
### Modified Capabilities
|
||||||
|
|
||||||
|
- `jupiter-perps-overview`: reported risk prices and observation timestamps.
|
||||||
|
- `phoenix-perps-service`: official address resolution and reported account/position risk.
|
||||||
|
- `evelyn-perps-service`: immutable risk scopes with exact distance/utilization and retained status.
|
||||||
|
- `evelyn-perps-overview`: Risk cells, deduplicated group summaries and expiration of unchanged snapshots.
|
||||||
|
|
||||||
|
## Impact
|
||||||
|
|
||||||
|
Integration DTOs/readers, Phoenix's existing Solana dependency wiring, Evelyn snapshots, JavaFX cells and existing project/shared documentation. No new dependencies, trading, live polling, commits or pushes.
|
||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
## MODIFIED Requirements
|
||||||
|
|
||||||
|
### Requirement: Perps overview displays a collapsible position hierarchy
|
||||||
|
The Perps tab SHALL display Position, Risk, Direction, Margin, Collateral, Leverage, Exposure, Value, uPnL and TWAP beneath the visible Perpetual Contracts root. Isolated hierarchy SHALL be wallet → venue → position; each Phoenix Cross account SHALL add a Cross group above its positions. Groups SHALL show applicable totals and risk summaries, leave direction/aggregate ratios and TWAP blank, and be expanded by default.
|
||||||
|
|
||||||
|
#### Scenario: Open either environment's Perps tab
|
||||||
|
- **WHEN** the owner opens Perps in Production or Test
|
||||||
|
- **THEN** all ten columns and the account-aware hierarchy are available with Risk near the position name
|
||||||
|
|
||||||
|
#### Scenario: Collapse and expand groups
|
||||||
|
- **WHEN** the owner collapses a wallet, venue or Cross account
|
||||||
|
- **THEN** the group still shows its worst known risk and incomplete-observation counts without changing totals
|
||||||
|
|
||||||
|
### Requirement: Polling preserves Perps view state
|
||||||
|
The Perps table SHALL retain matching wallet, venue and position identities across snapshots. Repeated unchanged snapshots SHALL leave retained rows, expansion, selection and formatting untouched except when risk freshness changes, without flicker or table rebuild. New groups SHALL be expanded by default, and added or removed positions SHALL be reconciled by identity without resetting retained groups.
|
||||||
|
|
||||||
|
#### Scenario: Poll unchanged positions
|
||||||
|
- **WHEN** a user collapses a group or selects a position and repeated snapshots are unchanged
|
||||||
|
- **THEN** the same rows remain with the same expansion and selection
|
||||||
|
- **AND** only a changed risk freshness indication requires a row-value update
|
||||||
|
|
||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: Risk cells distinguish distance and account health
|
||||||
|
Risk cells SHALL show neutral isolated distance and red reached/crossed boundaries, while Phoenix tiers use their own severity colors. Cross account cells SHALL label MM utilization; their children SHALL indicate Shared scope. Missing risk SHALL be Unknown. Text, indicators and tooltips SHALL explain units, reference/liquidation prices, account scope, thresholds, original observation time and limitations without changing direction backgrounds.
|
||||||
|
|
||||||
|
#### Scenario: Safe account without liquidation estimate
|
||||||
|
- **WHEN** Phoenix reports a valid Safe account but a nonpositive liquidation-price sentinel
|
||||||
|
- **THEN** account health remains available while the position price estimate is unavailable
|
||||||
|
|
||||||
|
### Requirement: Risk freshness expires unchanged snapshots
|
||||||
|
EMC SHALL mark risk stale after five minutes from its original observation, on read failure or for future timestamps. Existing UI polling SHALL update freshness even when the snapshot is unchanged, without additional venue calls/timers. Old critical states SHALL remain prominent with uncertainty; stale or incomplete noncritical observations SHALL NOT appear fresh green. Reconciliation SHALL preserve row identity, selection, expansion and TWAP state.
|
||||||
|
|
||||||
|
#### Scenario: Unchanged successful snapshot grows old
|
||||||
|
- **WHEN** time passes the five-minute risk age limit without new data or errors
|
||||||
|
- **THEN** existing UI polling changes its freshness indication without resetting the tree
|
||||||
|
|
||||||
|
#### Scenario: Retained critical risk
|
||||||
|
- **WHEN** a previously critical observation fails to refresh
|
||||||
|
- **THEN** its critical state and original time remain visible with stale/retained indication
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: Risk observations preserve scope and exact meaning
|
||||||
|
Snapshots SHALL preserve isolated risk prices and Cross account tiers/thresholds with original observation times. Signed price distance SHALL use the current venue reference as denominator and preserve boundary crossing. Cross MM utilization SHALL use effective collateral, with nonpositive denominators unavailable. Group risk SHALL count each isolated position and shared account once and retain worst known severity plus unknown/stale counts without averaging.
|
||||||
|
|
||||||
|
#### Scenario: Shared positions and incomplete data
|
||||||
|
- **WHEN** a Cross account owns multiple positions and another scope has unknown risk
|
||||||
|
- **THEN** account risk contributes once and the aggregate exposes the unknown count
|
||||||
|
- **AND** its closing Value is not substituted for effective collateral
|
||||||
|
|
||||||
|
#### Scenario: Boundary and zero denominator
|
||||||
|
- **WHEN** isolated distance reaches/crosses zero or effective collateral is nonpositive
|
||||||
|
- **THEN** the signed boundary is preserved or MM percentage is unavailable respectively
|
||||||
|
- **AND** display rounding does not determine risk classification
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: Report venue risk prices with observation time
|
||||||
|
The overview SHALL expose the venue reference and liquidation prices in exact USD units with the original response observation time. Missing or nonpositive risk prices SHALL be unavailable, never zero safety or infinite distance. Stable position identity and the separate transaction/alarm read path SHALL remain unchanged.
|
||||||
|
|
||||||
|
#### Scenario: Missing or sentinel price
|
||||||
|
- **WHEN** a valid position has no actionable liquidation price
|
||||||
|
- **THEN** its other metrics remain present and its liquidation distance is unavailable
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: Read venue account health independently of TWAP
|
||||||
|
The reader SHALL resolve the official trader account for authority/PDA/subaccount and read its venue risk classification, effective collateral, maintenance margin and position liquidation prices. It SHALL validate identities and amounts, use venue reference prices with original observation times and normalize sentinel liquidation prices to unavailable. Existing pacing, cooldown, timeout and failure retention contracts SHALL apply without signing or TWAP dependence.
|
||||||
|
|
||||||
|
#### Scenario: Same-symbol isolated and Cross accounts
|
||||||
|
- **WHEN** the wallet holds the same market in multiple accounts without active TWAPs
|
||||||
|
- **THEN** each visible account receives only its own reported risk and liquidation data
|
||||||
|
- **AND** a mismatched trader identity or position size fails the observation
|
||||||
|
|
||||||
|
#### Scenario: Missing or unsupported risk field
|
||||||
|
- **WHEN** a response has missing optional risk values or an unknown tier
|
||||||
|
- **THEN** those values remain unavailable without assuming a safe state
|
||||||
|
- **AND** a nonpositive liquidation-price sentinel does not invalidate independent account health
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# Tasks
|
||||||
|
|
||||||
|
## 1. Integration risk observations
|
||||||
|
|
||||||
|
- [x] 1.1 Extend Jupiter overview risk prices and Phoenix account/position risk reads with validated local PDA resolution; verify exact units, sentinels, identities and paced failures in temporary fixtures.
|
||||||
|
- [x] 1.2 Update integration documentation for the actual risk sources, units, dependencies and failure behavior; verify against the code and official contracts.
|
||||||
|
|
||||||
|
## 2. Snapshot and presentation
|
||||||
|
|
||||||
|
- [x] 2.1 Add immutable risk mapping and scope-aware assessments/summaries; verify signed distances, tiers, partial scopes, deduplication and nonpositive collateral with temporary checks.
|
||||||
|
- [x] 2.2 Render Risk cells/tooltips and age unchanged snapshots on existing UI polling; verify direction/TWAP preservation, shared scope, critical retention and selection/expansion with JavaFX checks.
|
||||||
|
- [x] 2.3 Update Evelyn and shared r35157_docs documentation with the owner-selected neutral-distance policy and five-minute freshness cutoff; verify links and remove conflicting descriptions.
|
||||||
|
|
||||||
|
## 3. Integration verification
|
||||||
|
|
||||||
|
- [x] 3.1 Run the normal build and focused fixtures/UI/read-only live observations, recording evidence and limitations.
|
||||||
|
- [x] 3.2 Sync all four delta specs, validate affected specs/change and inspect final diffs without committing or pushing.
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
# Verification — issue #102
|
||||||
|
|
||||||
|
Verified locally on 2026-10-24. The owner explicitly selected neutral isolated
|
||||||
|
distance, red only at a reached/crossed boundary, and Phoenix's own tier colors.
|
||||||
|
No commit, push, deployment, Hub restart, trading or signing was performed.
|
||||||
|
Unrelated pre-existing staged/deleted issue #97/#99 OpenSpec files were preserved.
|
||||||
|
|
||||||
|
## Build and focused fixtures
|
||||||
|
|
||||||
|
`JAVA_HOME=/usr/local/software/java/jfx-25 ./gradlew build` passed; all 42 existing
|
||||||
|
tests passed with no failures/errors. Intermittent inability to delete NFS-held
|
||||||
|
test binary output was resolved by normal build retries; no source/build workaround
|
||||||
|
was introduced for it.
|
||||||
|
|
||||||
|
Temporary `/tmp/Issue102Check.java` passed 68 focused assertions. Coverage includes:
|
||||||
|
|
||||||
|
- Exact Long/Short distance, sign-preserved boundary crossing and tiny positive distance.
|
||||||
|
- All supported Phoenix tiers, unknown future tiers and missing/sentinel prices.
|
||||||
|
- Distinct effective collateral, MM utilization and headroom; negative/zero collateral,
|
||||||
|
zero MM and accounts without positions; missing/contradictory Safe inputs stay uncertain.
|
||||||
|
- Five-minute expiration including the exact boundary, future timestamps and failures.
|
||||||
|
- Cross counted once, neutral scopes never becoming a group safety rating, and retained
|
||||||
|
critical severity alongside unknown/stale counts, including hidden empty venues.
|
||||||
|
- Actual local PDA derivation matching the independently observed official Cross address,
|
||||||
|
distinct isolated addresses and no currency lookup/RPC needed for derivation.
|
||||||
|
- HTTP fixtures without active TWAPs, with separate same-market Cross/isolated risk.
|
||||||
|
- Amount objects with different/signed decimal exponents, ignored UI strings and rejected
|
||||||
|
invalid scales; original/oldest observation timestamps.
|
||||||
|
- Trader authority/key/PDA/subaccount, position membership/quantity and duplicate validation.
|
||||||
|
- Missing optional values remain unavailable. HTTP failure retains the prior complete
|
||||||
|
venue/time; independent venue refresh still succeeds; consumer reads cause no I/O.
|
||||||
|
- Risk requests obey shared 429 cooldown; empty wallets need no risk calls.
|
||||||
|
- Captured Jupiter price fields convert from micro-USD and preserve observation time.
|
||||||
|
|
||||||
|
No permanent automated tests were added, consistent with repository conventions.
|
||||||
|
|
||||||
|
## JavaFX and visual checks
|
||||||
|
|
||||||
|
Temporary `Issue102Preview` passed, including repeated assertions that recycled empty
|
||||||
|
cells clear graphics/tooltips. Verified on the actual JavaFX rendering:
|
||||||
|
|
||||||
|
- Risk is second among ten columns; neutral isolated distances and color/text tier indicators.
|
||||||
|
- Tiny positive distance is shown as `<0.1%`; unsupported values never imply safety.
|
||||||
|
- Cross account MM labels, Shared children and owning-account tooltip identification.
|
||||||
|
- Worst known group severity plus visibly rendered unknown/stale counts on a separate line.
|
||||||
|
- Old critical observations stay red; unchanged snapshots expire with only the injected
|
||||||
|
clock advanced; failed snapshot getters suppress fresh green immediately and recovery works.
|
||||||
|
- Stable row-value objects on unchanged fresh data, selection/expansion retention,
|
||||||
|
no table.refresh/rebuild and preserved TWAP duration/graphic behavior.
|
||||||
|
- Horizontal scrolling and clearing removed rows. Actual table/tooltip PNGs inspected
|
||||||
|
under `/tmp/issue102-check/`.
|
||||||
|
|
||||||
|
The initial extra-line implementation was clipped by JavaFX despite its text being
|
||||||
|
present. Final cells use actual primary/secondary graphic labels; the final check
|
||||||
|
also inspects rendered Text nodes and the screenshot for the complete counts.
|
||||||
|
|
||||||
|
## Read-only live observation
|
||||||
|
|
||||||
|
2026-10-24 18:37:35–18:37:49 UTC:
|
||||||
|
|
||||||
|
- Jupiter: six configured PROD positions, all with positive reference/liquidation prices.
|
||||||
|
- Phoenix: ten positions and one account with independently read/validated account risk
|
||||||
|
on the checked PROD wallet; tier `safe`, effective collateral 92.502602 USD,
|
||||||
|
maintenance margin 31.093010 USD, MM utilization 33.613119336902544644%,
|
||||||
|
headroom 61.409592 USD.
|
||||||
|
- TEST Phoenix: zero positions.
|
||||||
|
|
||||||
|
Jupiter and Phoenix were checked on different PROD wallets to keep the additional
|
||||||
|
Phoenix read volume bounded. These are independent observations, not one atomic
|
||||||
|
portfolio valuation; the normal service still passes each configured wallet to
|
||||||
|
both venue readers.
|
||||||
|
|
||||||
|
An earlier attempt to read the larger wallet encountered HTTP 429 on a mark-price
|
||||||
|
request and stopped. No immediate retry occurred; the later smaller-wallet check
|
||||||
|
followed cooldown and completed successfully. Live reads used existing pacing and
|
||||||
|
only GET operations. Verification artifacts contain no signing keys.
|
||||||
|
|
||||||
|
## Specs and documentation
|
||||||
|
|
||||||
|
All four deltas were synchronized into existing main specs. Strict validation passed
|
||||||
|
for the change and `jupiter-perps-overview`, `phoenix-perps-service`,
|
||||||
|
`evelyn-perps-service`, `evelyn-perps-overview`. Relevant project docs and the existing
|
||||||
|
shared Evelyn overview were updated; local link targets and change-scoped whitespace
|
||||||
|
checks passed. Unrelated existing staged issue #97 specs retain their prior trailing-
|
||||||
|
blank-line warnings and were not changed.
|
||||||
|
|
||||||
|
## Limits
|
||||||
|
|
||||||
|
Risk is the latest observation, not a live alarm or safety guarantee. The five-minute
|
||||||
|
cutoff ages receipt timestamps; source APIs can lag the chain, and mark/state/view
|
||||||
|
reads are not atomic. Position changes during Phoenix reads can fail identity/quantity
|
||||||
|
validation and retain the previous observation until a later successful cycle. Slow
|
||||||
|
normal cycles can expire observations. No faster polling, notifications, automatic
|
||||||
|
collateral adjustment or local liquidation engine was introduced. Cross tiers and
|
||||||
|
MM utilization do not promise future execution outcomes; isolated distance has no
|
||||||
|
generic green/yellow safety bands.
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
schema: spec-driven
|
||||||
|
created: 2026-10-26
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# Design
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
See proposal.md for motivation. The adapter currently reads TWAP only when positions exist, and its decoder returns only a position-keyed map. Immutable account/snapshot records and identity-based JavaFX reconciliation already support retained observations.
|
||||||
|
|
||||||
|
## Goals / Non-Goals
|
||||||
|
|
||||||
|
**Goals:** Preserve eligible orders independently of matching, render actionable reasons with exact ownership, and summarize distinct orders through collapsed trees.
|
||||||
|
|
||||||
|
**Non-Goals:** Order cancellation, predicting execution failure, new polling or notifications, changes to capital or liquidation calculations.
|
||||||
|
|
||||||
|
## Decisions
|
||||||
|
|
||||||
|
- Return matched TWAPs and account-owned warning observations from decoding. Warning data holds TWAP address/order sequence, asset/subaccount identity, raw counters/fills, typed reason, optional matched position symbol and original receipt time. Keep compatible constructors for public records. A generic untyped UI message or synthetic position would lose ownership and distort totals.
|
||||||
|
- Read TWAP after trader state for every existing account. Fetch metadata for held positions or eligible filled outstanding orders; never request marks for warning-only markets. Empty unregistered authorities need no TWAP/market reads. Continue strict schema, counters, duplicate and identity validation, including orders that are ultimately hidden.
|
||||||
|
- Missing market and missing open position produce account-owned warnings. A matched order whose next execution estimate is unavailable keeps its progressbar plus a timing warning: this states unavailable timing, not a stopped order. Zero fills and zero remaining children stay invisible. Do not treat absence of optional USD valuation alone as a warning.
|
||||||
|
- Map matched warnings to positions, unmatched Cross warnings to their real Cross group (including zero-capital accounts), and propagate all through venue/wallet/root. An unmatched isolated order with no reliable position remains on Phoenix with its true isolated subaccount in the tooltip; inventing a Cross owner would violate identity. Existing risk reads also cover newly visible warning-only Cross accounts.
|
||||||
|
- Format warnings only in EMC. Build summaries from underlying observations, keyed by wallet plus account/order identity, rather than counting child indicators. Render an amber vector triangle with a readable label, retained/stale qualifier and detailed source tooltip. Five minutes is the same observation-age cutoff as existing risk; it is not an execution deadline. Existing three-second polling reevaluates age without moving source timestamps or rebuilding retained rows.
|
||||||
|
- A successful observation replaces warning lists; failures retain the complete prior venue. Consumer read failures qualify all visible summaries. Normal TWAP bars and Risk remain separate.
|
||||||
|
|
||||||
|
## Risks / Trade-offs
|
||||||
|
|
||||||
|
- Extra reads for registered empty accounts → reuse shared pacing/cooldown; fetch metadata only if needed and no extra marks.
|
||||||
|
- Missing metadata prevents quantities and symbols → show raw asset ID/lots and explicitly unknown values.
|
||||||
|
- Nonatomic state/TWAP reads can temporarily report no matching position → describe only the observed mismatch and let the next successful read reconcile it.
|
||||||
|
- Aggregation could duplicate orders or hide stale sources → distinct identity keys, per-source times/status, multi-wallet and collapsed-tree checks.
|
||||||
|
|
||||||
|
## Migration Plan
|
||||||
|
|
||||||
|
Build and verify locally with temporary harnesses and JavaFX images. Update the three main specs and meaningful docs after implementation. Deployment remains a separate owner action; no commits, pushes or Hub restart. Reverting these source changes restores prior presentation without changing orders.
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# Proposal
|
||||||
|
|
||||||
|
## Why
|
||||||
|
|
||||||
|
Issue #103 addresses a real Phoenix order with fills and remaining children that disappears from EMC because its market metadata is missing. Retaining and propagating a concise warning protects Evelyn's account oversight without introducing trading actions.
|
||||||
|
|
||||||
|
## What Changes
|
||||||
|
|
||||||
|
- Preserve eligible unresolved TWAPs as immutable account-owned warning observations, including orders without open positions.
|
||||||
|
- Show a small amber triangle and short description on the owning position or Cross account, with distinct-order summaries on every ancestor through Perpetual Contracts regardless of expansion state.
|
||||||
|
- Preserve original times and failure/freshness qualifications; clear resolved warnings on successful reads. Continue hiding zero-fill and completed orders.
|
||||||
|
- Keep normal TWAP progress, valuations and margin risk independent. Update meaningful documentation and verify with temporary fixtures and JavaFX previews.
|
||||||
|
|
||||||
|
## Capabilities
|
||||||
|
|
||||||
|
### New Capabilities
|
||||||
|
|
||||||
|
None.
|
||||||
|
|
||||||
|
### Modified Capabilities
|
||||||
|
|
||||||
|
- `phoenix-perps-service`: discover TWAPs for empty existing accounts and retain unresolved filled orders without weakening identity validation.
|
||||||
|
- `evelyn-perps-service`: preserve warning ownership, including warning-only Cross accounts, through immutable snapshots and retained failures.
|
||||||
|
- `evelyn-perps-overview`: display and aggregate accessible TWAP warnings through the complete tree, including collapsed branches.
|
||||||
|
|
||||||
|
## Impact
|
||||||
|
|
||||||
|
Phoenix account and Evelyn snapshot records gain warning lists with compatible constructors. The decoder, reader, mapping and Perps table change; no new dependencies or transaction operations. Repository docs and the shared Evelyn overview change under the owner's explicit documentation instruction. No commits, pushes, Hub restart or deployment.
|
||||||
+76
@@ -0,0 +1,76 @@
|
|||||||
|
## MODIFIED Requirements
|
||||||
|
|
||||||
|
### Requirement: Perps overview displays a collapsible position hierarchy
|
||||||
|
The Perps tab SHALL display Position, Risk, Direction, Margin, Collateral, Leverage, Exposure, Value, uPnL and TWAP beneath the visible Perpetual Contracts root. Isolated hierarchy SHALL be wallet → venue → position; each Phoenix Cross account SHALL add a Cross group above its positions. Groups SHALL show applicable totals and risk summaries, leave direction/aggregate ratios blank, show TWAP warnings when applicable, and be expanded by default.
|
||||||
|
|
||||||
|
#### Scenario: Open either environment's Perps tab
|
||||||
|
- **WHEN** the owner opens Perps in Production or Test
|
||||||
|
- **THEN** all ten columns and the account-aware hierarchy are available with Risk near the position name
|
||||||
|
|
||||||
|
#### Scenario: Collapse and expand groups
|
||||||
|
- **WHEN** the owner collapses a wallet, venue or Cross account
|
||||||
|
- **THEN** the group still shows its worst known risk and incomplete-observation counts without changing totals
|
||||||
|
|
||||||
|
### Requirement: TWAP cells show execution progress and snapshot duration
|
||||||
|
Matching filled Phoenix TWAPs SHALL show a progressbar using executed/total child counts and an overlaid estimated duration to the next child in unbounded hours, minutes and seconds. Cells with no eligible progress or warning SHALL be empty. Matched timing warnings SHALL coexist with their progressbar. Updates SHALL follow existing snapshots without a live timer. Failed retained observations SHALL be labeled retained; due or unknown schedules SHALL not show negative or wrapped times or imply execution.
|
||||||
|
|
||||||
|
#### Scenario: Long interval
|
||||||
|
- **WHEN** the snapshot's estimated wait is 28 hours 32 minutes 10 seconds, or 128 hours 32 minutes 10 seconds
|
||||||
|
- **THEN** the text is approximately 28:32:10 or 128:32:10 respectively
|
||||||
|
|
||||||
|
#### Scenario: Failed or due observation
|
||||||
|
- **WHEN** the venue read fails or the latest observed next slot is already due
|
||||||
|
- **THEN** the cell communicates retained or awaiting-update status without advancing progress
|
||||||
|
|
||||||
|
### Requirement: Venue visibility follows available child rows
|
||||||
|
EMC SHALL hide venue rows with neither positions, Cross accounts nor TWAP warnings and show them when children reappear. Funded empty Cross accounts and accounts with eligible TWAP warnings SHALL keep their venue visible. Unmatched isolated warnings SHALL remain on the venue with true subaccount identity, never on an invented Cross parent. Hidden venues SHALL remain in snapshots and totals, with read status available in wallet tooltips. Unknown scopes SHALL NOT become zero totals; failed reads SHALL preserve last-success totals. Wallet rows and the root SHALL remain visible.
|
||||||
|
|
||||||
|
#### Scenario: Empty venue
|
||||||
|
- **WHEN** Jupiter or Phoenix has no positions, Cross accounts or TWAP warnings
|
||||||
|
- **THEN** its heading is absent from that wallet's displayed children
|
||||||
|
- **AND** a successful empty scope still contributes zero to totals
|
||||||
|
|
||||||
|
#### Scenario: Funded Cross account without positions
|
||||||
|
- **WHEN** Phoenix returns a Cross account with capital but no open positions
|
||||||
|
- **THEN** Phoenix and its Cross account remain visible with that capital counted once
|
||||||
|
|
||||||
|
#### Scenario: Positions disappear and return
|
||||||
|
- **WHEN** a venue loses its last child and later returns a position
|
||||||
|
- **THEN** its heading disappears and reappears with the correct child
|
||||||
|
- **AND** unrelated retained rows keep their selection and expansion state
|
||||||
|
|
||||||
|
#### Scenario: Hidden scope has not been read successfully
|
||||||
|
- **WHEN** a childless venue has never been read successfully and its first read is pending or failed
|
||||||
|
- **THEN** the heading is hidden and its read status is available on the wallet tooltip
|
||||||
|
- **AND** required totals remain unavailable rather than reporting zero
|
||||||
|
|
||||||
|
### Requirement: Polling preserves Perps view state
|
||||||
|
The Perps table SHALL retain matching wallet, venue and position identities across snapshots. Repeated unchanged snapshots SHALL leave retained rows, expansion, selection and formatting untouched except when risk or TWAP-warning freshness changes, without flicker or table rebuild. New groups SHALL be expanded by default, and added or removed positions SHALL be reconciled by identity without resetting retained groups.
|
||||||
|
|
||||||
|
#### Scenario: Poll unchanged positions
|
||||||
|
- **WHEN** a user collapses a group or selects a position and repeated snapshots are unchanged
|
||||||
|
- **THEN** the same rows remain with the same expansion and selection
|
||||||
|
- **AND** only a changed risk or TWAP-warning freshness indication requires a row-value update
|
||||||
|
|
||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: TWAP warnings propagate through every ancestor
|
||||||
|
EMC SHALL show an amber warning triangle and concise text at the owning position or Cross group and on every actual ancestor through Perpetual Contracts. Summaries SHALL count each distinct order once per scope independently of expansion state. Unfilled and completed orders SHALL remain invisible. Risk and capital totals SHALL remain independent.
|
||||||
|
|
||||||
|
#### Scenario: Collapsed Cross branch
|
||||||
|
- **WHEN** a filled outstanding Cross order has missing market metadata and Cross or Phoenix is collapsed
|
||||||
|
- **THEN** Cross, Phoenix, wallet and Perpetual Contracts still carry the warning
|
||||||
|
- **AND** each summary counts that order once
|
||||||
|
|
||||||
|
#### Scenario: Position and multiple wallets
|
||||||
|
- **WHEN** a position warning and an unmatched Cross warning affect different wallets
|
||||||
|
- **THEN** each follows only its actual ancestors and the root counts two orders
|
||||||
|
- **AND** clearing one warning leaves the other and its ancestor summaries intact
|
||||||
|
|
||||||
|
### Requirement: Warning details preserve reason and freshness
|
||||||
|
Warning tooltips SHALL identify wallet/account/position, reason, TWAP address, asset/subaccount IDs, raw progress/fills and original observation time. Unknown metadata SHALL remain unknown without unsupported cancellation/delisting/stoppage claims. Failure retention, future timestamps and the existing five-minute observation-age cutoff SHALL qualify all affected summaries.
|
||||||
|
|
||||||
|
#### Scenario: Retained or aged warning
|
||||||
|
- **WHEN** a warned venue or consumer read fails, or its original observation becomes five minutes old
|
||||||
|
- **THEN** source and inherited warnings remain visible with retained/stale qualification and original times
|
||||||
|
- **AND** existing UI polling updates age without additional venue reads or resetting selection and expansion
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
## MODIFIED Requirements
|
||||||
|
|
||||||
|
### Requirement: TWAP observations belong to existing filled positions
|
||||||
|
The service SHALL publish immutable TWAP observations on matching existing Phoenix positions whose order has a positive actual fill and remaining executions, and account-owned warnings for eligible unresolved orders. It SHALL preserve counters, quantities, nullable price and observation/estimated times without UI formatting. No new position SHALL be synthesized. TWAP errors SHALL retain the complete prior venue observation with failure status. Polling frequency SHALL remain unchanged.
|
||||||
|
|
||||||
|
#### Scenario: Unfilled order or no position
|
||||||
|
- **WHEN** an active TWAP has no actual fills or no matching open position
|
||||||
|
- **THEN** a zero-fill order is absent from every overview row and cannot create a visible venue
|
||||||
|
- **AND** an eligible filled outstanding order without a position is preserved as a warning on its actual account scope
|
||||||
|
|
||||||
|
#### Scenario: TWAP read fails
|
||||||
|
- **WHEN** the TWAP read fails after a successful venue observation
|
||||||
|
- **THEN** prior positions, TWAP observations and original timestamps remain with failed venue status
|
||||||
|
- **AND** other venues and wallets continue refreshing
|
||||||
|
|
||||||
|
#### Scenario: Filled order without position
|
||||||
|
- **WHEN** a filled outstanding order cannot match an open position
|
||||||
|
- **THEN** its warning retains the real account identity and Cross accounts remain present even without capital or positions
|
||||||
|
- **AND** warning-only data does not synthesize positions or valuations
|
||||||
|
|
||||||
|
#### Scenario: Warning recovery and retention
|
||||||
|
- **WHEN** a successful observation resolves or removes a warned order
|
||||||
|
- **THEN** its warning is cleared while unrelated warnings remain
|
||||||
|
- **AND** failed reads instead retain warnings and their original times
|
||||||
+52
@@ -0,0 +1,52 @@
|
|||||||
|
## MODIFIED Requirements
|
||||||
|
|
||||||
|
### Requirement: Read failures cannot masquerade as empty portfolios
|
||||||
|
HTTP, authentication, malformed schema, identity mismatch and numeric conversion errors SHALL fail the read. Requests SHALL have bounded timeouts. Only a valid response with no nonzero positions SHALL produce an empty result. Present mark prices SHALL be positive and finite; an explicitly absent mark SHALL preserve positions and quantities with unavailable price-dependent valuations.
|
||||||
|
|
||||||
|
#### Scenario: Missing mark price
|
||||||
|
- **WHEN** a valid mark response has no mark price for a held position
|
||||||
|
- **THEN** the position and its TWAP quantities remain present with unknown USD valuations
|
||||||
|
- **AND** affected aggregate valuations remain unknown
|
||||||
|
|
||||||
|
#### Scenario: Rejected access or invalid price
|
||||||
|
- **WHEN** an HTTP request is rejected or a present mark price is invalid
|
||||||
|
- **THEN** the read fails instead of returning an empty or partial portfolio
|
||||||
|
|
||||||
|
#### Scenario: Empty account
|
||||||
|
- **WHEN** a valid trader snapshot has no subaccounts
|
||||||
|
- **THEN** an empty result is returned without market-price or TWAP requests; existing empty subaccounts still receive TWAP discovery
|
||||||
|
|
||||||
|
#### Scenario: Public API rate limit
|
||||||
|
- **WHEN** the shared adapter issues requests for PROD and TEST
|
||||||
|
- **THEN** request starts SHALL be spaced at least one second apart
|
||||||
|
- **AND** HTTP 429 SHALL fail the lookup and establish a shared cooldown from Retry-After (seconds or HTTP date, 60 seconds fallback)
|
||||||
|
- **AND** lookups during cooldown SHALL fail without external calls, with no immediate retry
|
||||||
|
|
||||||
|
### Requirement: Read precise TWAP snapshots for held markets
|
||||||
|
The reader SHALL read official TWAP snapshots through its existing paced transport and associate orders by authority, PDA, subaccount and market identity. Only orders with actual fills and remaining children SHALL be exposed. Matched orders SHALL retain position progress; valid unresolved orders SHALL retain account-owned warnings with identity, raw counters/fills and original observation time. Existing accounts without positions SHALL receive TWAP discovery. Quantities SHALL use exact lot conversion and explicit/legacy dust semantics. Unavailable size inputs SHALL remain unknown. Existing mark prices SHALL be reused without Ticker reads.
|
||||||
|
|
||||||
|
#### Scenario: Explicit and legacy dust
|
||||||
|
- **WHEN** total children are 5, regular size 10 lots and explicit dust count/size are 2/3
|
||||||
|
- **THEN** total size is 36 lots and the fourth child is 3 lots
|
||||||
|
- **AND** legacy zero dust count with size 3 instead represents four regular children and a final 3-lot child
|
||||||
|
|
||||||
|
#### Scenario: Identity mismatch or unfilled order
|
||||||
|
- **WHEN** authority/PDA/subaccount/asset identities disagree or an order has no fills
|
||||||
|
- **THEN** disagreement fails the read, while a valid unfilled order is absent from all TWAP data and warnings
|
||||||
|
|
||||||
|
#### Scenario: Missing market or missing position
|
||||||
|
- **WHEN** a filled outstanding order has missing market metadata or no matching open position
|
||||||
|
- **THEN** a warning preserves its identity and raw progress without inventing a symbol, conversion, price or execution status
|
||||||
|
- **AND** healthy positions and account risk remain available
|
||||||
|
|
||||||
|
#### Scenario: Existing empty account
|
||||||
|
- **WHEN** an account has no held positions but has a filled outstanding TWAP
|
||||||
|
- **THEN** TWAP discovery returns its warning without fetching market prices or creating positions
|
||||||
|
|
||||||
|
#### Scenario: Completed order
|
||||||
|
- **WHEN** an order has no remaining children
|
||||||
|
- **THEN** it is absent from active TWAP data and warnings
|
||||||
|
|
||||||
|
#### Scenario: Matched order with unavailable timing
|
||||||
|
- **WHEN** an otherwise valid matched filled order has no usable future next-execution estimate
|
||||||
|
- **THEN** its progress remains available with a timing warning that does not assert execution has stopped
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# Tasks
|
||||||
|
|
||||||
|
## 1. Phoenix warning observations
|
||||||
|
|
||||||
|
- [x] 1.1 Preserve eligible unmatched orders and matched timing warnings with strict identities/counters and original times; verify missing metadata, zero-fill/completed suppression and known-market matching with temporary fixtures.
|
||||||
|
- [x] 1.2 Discover TWAPs for existing empty accounts with existing transport safeguards and document changed reads in docs/PhoenixPerps.md; verify empty-account request paths and HTTP failure behavior.
|
||||||
|
|
||||||
|
## 2. Snapshot ownership
|
||||||
|
|
||||||
|
- [x] 2.1 Map warnings onto positions, real Cross accounts and isolated venue fallbacks without valuation changes, preserving them on failures; verify immutable lists, warning-only accounts, identity and recovery with temporary fixtures and document the snapshot behavior.
|
||||||
|
|
||||||
|
## 3. Tree presentation
|
||||||
|
|
||||||
|
- [x] 3.1 Render the amber warning icon, concise descriptions and detailed tooltips with retained/stale source information; verify actual JavaFX cells, normal bars, Risk and row backgrounds in a preview.
|
||||||
|
- [x] 3.2 Propagate distinct warnings through every actual ancestor to Perpetual Contracts independently of collapse state; verify multiple wallets, clearing and stable selection/expansion, and update docs/Evelyn.md plus the shared Evelyn overview.
|
||||||
|
|
||||||
|
## 4. Integration verification
|
||||||
|
|
||||||
|
- [x] 4.1 Run the normal build and integrated temporary fixture/JavaFX checks, inspect a paced read-only live observation if available, validate/sync affected OpenSpec specs and record evidence in verification.md; confirm no commits, pushes or runtime changes.
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
# Verification — issue #103
|
||||||
|
|
||||||
|
Verified locally on 2026-10-26. No commits, pushes, Hub restart, deployment, signing or order changes.
|
||||||
|
|
||||||
|
## Delivered behavior
|
||||||
|
|
||||||
|
- The Phoenix decoder preserves filled outstanding orders with missing market metadata or no matching open position as typed immutable warnings. It also preserves matched progress with a timing warning when the next execution cannot be estimated.
|
||||||
|
- TWAP discovery includes existing empty accounts. Metadata is conditional and warning-only markets cause no mark-price calls. Existing request pacing, timeout and shared 429 cooldown remain in use.
|
||||||
|
- Warnings map to their real position or Cross account. Empty warning-only Cross accounts remain visible without invented positions or capital. Unmatched isolated orders remain on Phoenix with their real subaccount identity.
|
||||||
|
- Amber vector triangles and concise text appear in TWAP cells. Distinct source orders propagate through every actual ancestor to Perpetual Contracts, independently of expansion. Successful recovery clears only resolved orders; failures and five-minute/future-date staleness remain qualified at every level.
|
||||||
|
- Detail tooltips preserve raw counters, IDs and original receipt times. They have bounded scrollable content and remain available while moving between their owner cell and details. Risk and existing normal TWAP bars remain independent.
|
||||||
|
- Updated docs/Evelyn.md, docs/PhoenixPerps.md and the shared r35157_docs/projects/evelyn/README.md; removed obsolete matching/discovery/group-cell claims. The three affected main OpenSpec specifications are synced. No permanent tests or dependencies were added.
|
||||||
|
|
||||||
|
## Checks
|
||||||
|
|
||||||
|
- `JAVA_HOME=/usr/local/software/java/jfx-25 ./gradlew build`: successful; 42 existing tests, no failures/errors/skips.
|
||||||
|
- `/tmp/Issue103Check.java`: 32 focused assertions passed. Covers the captured real asset-97 response; missing markets; matching and unmatched positions; hidden zero-fill/completed orders; identity/duplicate/counter rejection; immutable lists; correct Cross/isolated mapping; warning-only empty accounts without mark requests; unchanged monetary totals; failure retention/recovery; and 429 cooldown. Uses local HTTP fixtures and the actual decoder/adapter/service.
|
||||||
|
- `/tmp/Issue102Check.java`: all 68 existing temporary risk checks still pass, including exact boundaries, account scope and retained/future observations.
|
||||||
|
- `/tmp/Issue103Preview.java`: 82 JavaFX assertions passed. This is an assertion count, including repeated checks of recycled empty cells, not 82 distinct user scenarios. Checks actual warning graphics, source and ancestor counts over two wallets, collapsed Cross/Phoenix/wallet/root nodes, normal 28-hour progress, timing warning plus bar, margin-risk independence, direction backgrounds, accessible text, tooltip content/scrolling/leave behavior, snapshot failures, five-minute aging, recovery, row identity/selection, warning-only Cross, empty venue hiding and horizontal scrolling.
|
||||||
|
- Rendered JavaFX images inspected in `/tmp/issue103-check/`: `twap-warnings-expanded.png`, `twap-warnings-collapsed.png`, `twap-warning-tooltip.png`, `twap-warning-tooltip-bottom.png`, `twap-warnings-stale.png`, `twap-warning-empty-cross.png`, `twap-warnings-small-window.png`. Long tooltip height and contrast were corrected during inspection. TWAP was scrolled into the viewport before final visual checks.
|
||||||
|
- Strict validation passes for change `103-perps-twap-warnings` and specifications `phoenix-perps-service`, `evelyn-perps-service` and `evelyn-perps-overview`. Existing long modified requirement paragraphs produce informational length hints only.
|
||||||
|
- Changed working-tree and change-specific staged whitespace checks pass; 24 local documentation file links resolve. Pre-existing staged/deleted OpenSpec artifacts for #97 and #99 were left untouched.
|
||||||
|
|
||||||
|
## Read-only live observation
|
||||||
|
|
||||||
|
`/tmp/Issue103Live.java` used the actual default paced Phoenix adapter and local PDA derivation against the owner's reported wallet. One lookup ran from **2026-10-26 05:25:26 to 05:25:38 UTC**. It returned one account with eight held positions and one warning:
|
||||||
|
|
||||||
|
```text
|
||||||
|
PDA/subaccount: 0/0
|
||||||
|
Asset ID: 97
|
||||||
|
Reason: MARKET_UNAVAILABLE
|
||||||
|
Progress: 34/346
|
||||||
|
Remaining: 312
|
||||||
|
Original TWAP receipt: 2026-10-26T05:25:28.553550180Z
|
||||||
|
Mapped Cross warnings: 1
|
||||||
|
Venue fallback warnings: 0
|
||||||
|
```
|
||||||
|
|
||||||
|
The order's market identity and execution status remain unresolved; this verification does not claim cancellation, delisting or stopped execution. It confirms local reader/mapping behavior, not deployment to the running Hub. Live reads are separate observations, not an atomic portfolio snapshot.
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user