Compare commits
21
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1af26cb9a0 | ||
|
|
805e2e7e6e | ||
|
|
cfb49ed896 | ||
|
|
e71a8daa8d | ||
|
|
2f30712004 | ||
|
|
28640f3203 | ||
|
|
7e36b7ef95 | ||
|
|
df5cc0eda7 | ||
|
|
0ec22d5215 | ||
|
|
7aab307381 | ||
|
|
1ab439b578 | ||
|
|
2dce841cf4 | ||
|
|
cc7003ac94 | ||
|
|
59c5c62170 | ||
|
|
f1f31ab8f0 | ||
|
|
d057b0f3e6 | ||
|
|
e283f0b8f2 | ||
|
|
00a7f13c6f | ||
|
|
49df1ac9d0 | ||
|
|
152137ed87 | ||
|
|
adb5f0235a |
@@ -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
|
||||||
|
|||||||
+287
-4
@@ -99,10 +99,107 @@ 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:
|
||||||
|
|
||||||
|
| Token | Reserve | Minimum swap-input | Maksimum pr. iteration |
|
||||||
|
| --- | ---: | ---: | ---: |
|
||||||
|
| USDT | 0,1 USDT | 0,05 USDT | 0,25 USDT |
|
||||||
|
| SyrupUSDC | 0,095 SyrupUSDC | 0,045 SyrupUSDC | 0,2 SyrupUSDC |
|
||||||
|
|
||||||
|
Hub'en angiver reserver, minimumsbeløb og maksimummer som `MoneyAmount`.
|
||||||
|
Reservelisten bestemmer accepterede valutaer og prioritet; minimumsbeløb og
|
||||||
|
maksimummer matches efter valutaens kanoniske UUID uanset listernes rækkefølge,
|
||||||
|
navn og symbol. Hver accepteret valuta skal have præcis ét positivt minimum og
|
||||||
|
maksimum, og minimum må ikke overstige maksimum. Alle tre lister kopieres ved
|
||||||
|
oprettelse af burneren; tre tomme lister tillader direkte EVE-burn.
|
||||||
|
|
||||||
|
Burneren vælger det mindste af saldo minus reserve og valutaens eget maksimum,
|
||||||
|
afrundet ned til mintens præcision. Er dette faktiske beløb under valutaens
|
||||||
|
minimum, springes inputtet over, og næste input undersøges i samme iteration.
|
||||||
|
Lighed med minimum accepteres; der rundes aldrig op eller bruges af reserven for
|
||||||
|
at nå minimum. For eksempel springes 0,000001 spendabel USDT over, så en
|
||||||
|
SyrupUSDC-saldo på 0,502048 kan give et swap på 0,2 SyrupUSDC. Inputminimummer
|
||||||
|
begrænser ikke burn af eksisterende EVE. Grænserne angives i tokens: SyrupUSDC er ikke
|
||||||
|
lig med USDC, og 0,2 SyrupUSDC garanterer ikke en bestemt USD-værdi. Der kræves
|
||||||
|
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
|
||||||
|
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.
|
||||||
|
Efter det mulige buyback, også når prisbetingelsen springer købet over,
|
||||||
|
genlæses hele den synlige EVE-saldo, også ved direkte
|
||||||
|
EVE-indbetalinger. Ejeren har valgt et produktionsminimum på **0,1 EVE pr. 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
|
||||||
|
validitetsoplysninger i processen. Senere iterationer undersøger samme signatur
|
||||||
|
uden at gensende transaktionen. Bekræftet succes eller fejl ved mindst `CONFIRMED`
|
||||||
|
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
|
||||||
|
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.
|
||||||
|
Jupiter-udfyldte sponsorsignaturer kan afklares ved status, men bruges ikke til at
|
||||||
|
bevise udløb af den lokalt signerede transaktion.
|
||||||
|
|
||||||
|
Mens et swap er uafklaret, fortsætter eksisterende EVE-burn. Et allerede ventende
|
||||||
|
burn har førsteprioritet og afklares alene. Fejl bevist før indsendelse tillader et
|
||||||
|
nyt forsøg i en senere iteration; mulig indsendelse uden troværdig signatur kræver
|
||||||
|
manuel afklaring. Loggen viser valuta, beløb, signatur, fase/årsagsklasser og næste
|
||||||
|
handling uden signerdata eller transaktionspayloads. Der findes ingen persistent
|
||||||
|
journal: genstart mister pending swap/burn og suspensioner, så ukendte
|
||||||
|
transaktioner skal afklares eksternt før genstart og genoptagelse.
|
||||||
|
|
||||||
### 2.3 Finansiering af udvikling
|
### 2.3 Finansiering af udvikling
|
||||||
Udvikling i Evelyn sker gennem en task-baseret model. Alle opgaver oprettes i IntelliJ YouTrack og bliver
|
Udvikling i Evelyn sker gennem en task-baseret model. Alle opgaver oprettes i IntelliJ YouTrack og bliver
|
||||||
@@ -334,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 `—`.
|
||||||
+2
@@ -0,0 +1,2 @@
|
|||||||
|
schema: spec-driven
|
||||||
|
created: 2026-10-05
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
# Design
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
See proposal.md for motivation and issue #90 for the agreed operating values. The existing burner already supports ordered SOL/SPL inputs, downward precision rounding and Jupiter exact-input swaps. Its reserve list is the authoritative accepted-input configuration. The constructor's scalar USDC cap and input-price lookup are the only blockers to selecting SyrupUSDC without additional Ticker observations.
|
||||||
|
|
||||||
|
## Goals / Non-Goals
|
||||||
|
|
||||||
|
**Goals:** Keep selection independent of input prices while rejecting incomplete or ambiguous limits before any worker starts. Change the amount policy without changing transaction outcomes, recovery or burning.
|
||||||
|
|
||||||
|
**Non-Goals:** Introduce another input model, a general configuration subsystem, or change how the latest EVE/USDT observation is selected.
|
||||||
|
|
||||||
|
## Decisions
|
||||||
|
|
||||||
|
1. Accept maxima as a second `List<MoneyAmount>` and copy their validated values into an immutable UUID-keyed map. The reserve list keeps its original defensive copy and order. UUID lookup permits the maxima list to arrive in any order; matching positions or metadata would create avoidable ambiguity. Existing immutable `MoneyAmount` values suffice without new types or ValueTags.
|
||||||
|
2. Validate maxima entries, amounts, currencies and UUIDs, canonical CIS identity, positivity and uniqueness, then require exact agreement with the accepted reserve UUIDs. Keep existing reserve and Solana-representation validation unchanged. Empty lists match and retain direct-EVE handling. A compatibility scalar constructor would preserve obsolete price semantics, so it is not retained.
|
||||||
|
3. Select `min(balance - reserve, maximum.amount())` only for positive spendable balance, then use the existing downward mint-precision rounding and zero skip. Remove the USDC canonical field and all input price construction/queries. Keep EVE/USDT canonical construction and fresh-price enforcement intact.
|
||||||
|
4. Add the agreed two reserve and maximum entries in Hub construction. Retain every other argument and the existing wallet/Jupiter bindings. Jupiter continues to choose pool and route; no Raydium transaction path is added.
|
||||||
|
5. Compile through the integrated Gradle/Detag build, and verify the compiled implementation with temporary offline controlled dependencies outside the repository. Do not add a permanent test artifact. Verify amount boundaries, UUID matching, invalid configuration, rounding, and existing failure/pending-burn behavior without network or real transaction submission.
|
||||||
|
|
||||||
|
## Risks / Trade-offs
|
||||||
|
|
||||||
|
- Token limits vary in USD value → document that 0.2 SyrupUSDC is a token amount and retain explicit small limits.
|
||||||
|
- Constructor signature changes → update the single repository call site and compile all relevant sources.
|
||||||
|
- USDT can defer SyrupUSDC indefinitely while spendable → preserve the agreed priority and one-attempt rule rather than changing scheduling.
|
||||||
|
- Unknown transaction outcomes must not be retried → leave outcome handlers unchanged and verify suspension/pending behavior offline.
|
||||||
|
|
||||||
|
## Migration Plan
|
||||||
|
|
||||||
|
Implementation is local and reviewable; deployment is a separate task. A later deployment replaces the application with the compiled change while preserving wallet, startup configuration and existing Ticker history. External callers must replace a scalar cap with exactly one token-denominated maximum per accepted reserve UUID. Rollback restores the prior implementation and USDT-only Hub configuration after reconciling any unknown transaction outcomes.
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
# Proposal
|
||||||
|
|
||||||
|
## Why
|
||||||
|
|
||||||
|
The production Evelyn IOU burner accepts only USDT, and its common USDC-equivalent ceiling needs input prices for other tokens. Issue #90 requires deposited SyrupUSDC to fund the same Jupiter buyback and burn pipeline using a small, explicit token-denominated limit.
|
||||||
|
|
||||||
|
## What Changes
|
||||||
|
|
||||||
|
- **BREAKING**: Replace the constructor's scalar `maximumSwapAmountInUSDC` with `List<MoneyAmount> inputCurrencyMaximumSwapAmounts`, validated and matched to accepted inputs by canonical currency UUID.
|
||||||
|
- Calculate each exact input amount from spendable balance and that currency's maximum, rounding downward to mint precision without querying an input/USDC price.
|
||||||
|
- Keep USDT first with reserve 0.1 and maximum 0.25; add SyrupUSDC second with reserve 0.095 and maximum 0.2 in `constructEvelynIOUBurnerProdProcess()`.
|
||||||
|
- Preserve Jupiter routing, EVE/USDT freshness before burning, all transaction/recovery behavior, and Hub-owned lifecycle.
|
||||||
|
|
||||||
|
## Capabilities
|
||||||
|
|
||||||
|
### New Capabilities
|
||||||
|
|
||||||
|
None.
|
||||||
|
|
||||||
|
### Modified Capabilities
|
||||||
|
|
||||||
|
- `evelyn-iou-burner-service`: Token-denominated maxima, UUID-matched configuration validation, price-independent input selection, and production SyrupUSDC support.
|
||||||
|
|
||||||
|
## Impact
|
||||||
|
|
||||||
|
Changes are limited to the burner reference constructor/selection logic, its Hub construction, directly affected documentation and OpenSpec requirements. Verification compiles normal sources and uses controlled offline dependencies without live financial transactions. No Ticker sources, API behavior, history paths, ValueTags, direct Raydium swaps, deployment, or unrelated refactoring are included.
|
||||||
+129
@@ -0,0 +1,129 @@
|
|||||||
|
# Spec Delta
|
||||||
|
|
||||||
|
## MODIFIED Requirements
|
||||||
|
|
||||||
|
### Requirement: Ordered AssetAZ input configuration
|
||||||
|
The authoritative constructor SHALL accept a non-null ordered `List<MoneyAmount>` named `inputCurrencyMinimumReserves`, defensively copy it without changing order, and interpret inclusion as acceptance, list position as priority, each `currencyType().id()` as the authoritative AssetAZ identity, and each `amount()` as the minimum balance that must remain. It SHALL ignore supplied currency name and symbol metadata by resolving each UUID through the injected Currency Identity Service and using the canonical returned currency for processing. The constructor SHALL also accept a non-null `List<MoneyAmount>` named `inputCurrencyMaximumSwapAmounts`, defensively copy its values, and match each maximum to an accepted input by canonical currency UUID, independently of maximum-list order or supplied metadata. Each maximum SHALL be expressed in that same currency's own units. Empty reserves and maxima lists SHALL be valid and SHALL still permit directly deposited EVE to be burned.
|
||||||
|
|
||||||
|
#### Scenario: Stale configured metadata
|
||||||
|
- **WHEN** a configured `MoneyAmount` carries a known UUID but stale name or symbol metadata
|
||||||
|
- **THEN** the service processes the current canonical currency resolved for that UUID
|
||||||
|
|
||||||
|
#### Scenario: Priority is preserved
|
||||||
|
- **WHEN** multiple configured entries have spendable balances and usable Solana data
|
||||||
|
- **THEN** the earliest entry in the defensively copied list is the only input selected in that iteration
|
||||||
|
|
||||||
|
#### Scenario: Empty input list
|
||||||
|
- **WHEN** no buyback inputs are configured but the wallet contains EVE
|
||||||
|
- **THEN** the service skips input selection and still evaluates the complete EVE balance for burning
|
||||||
|
|
||||||
|
#### Scenario: Maximum list order is independent
|
||||||
|
- **WHEN** maxima are supplied in a different order from the reserve list or carry stale display metadata for known UUIDs
|
||||||
|
- **THEN** each input receives the maximum for its UUID and reserve-list priority is preserved
|
||||||
|
|
||||||
|
#### Scenario: Caller changes its configuration lists
|
||||||
|
- **WHEN** a caller changes either supplied list after construction
|
||||||
|
- **THEN** the accepted currencies, their priority, reserves and maxima remain as validated during construction
|
||||||
|
|
||||||
|
### Requirement: Complete constructor validation
|
||||||
|
Construction SHALL require all injected services, both input configuration lists, and durations to be non-null; every list entry, entry amount, entry currency, and currency UUID to be non-null; each reserve to be zero or greater; each maximum to be strictly positive; each UUID to be known to the injected Currency Identity Service; UUIDs to be unique within each list; the maximum UUID set to exactly equal the accepted reserve UUID set; and EVE not to be an input currency. It SHALL require slippage to be from 0 through 10000 basis points inclusive, `maximumPriceAge` to be greater than zero milliseconds, and both finalization timeout and iteration interval to be positive and non-zero.
|
||||||
|
|
||||||
|
Every configured non-SOL UUID SHALL resolve through reverse CIS lookup to exactly one external reference in the `solana-mint` namespace, and that reference's external ID SHALL be non-null and non-blank. Construction SHALL fail for a missing, ambiguous, null, or blank non-SOL mint representation. These burner-specific rules SHALL NOT change the general-purpose `MoneyAmount` contract.
|
||||||
|
|
||||||
|
#### Scenario: Valid configuration
|
||||||
|
- **WHEN** every dependency and value is valid, reserves are non-negative, every accepted input has exactly one positive maximum, UUIDs are distinct and known within each list, EVE is absent, and every non-SOL input has one non-blank Solana mint reference
|
||||||
|
- **THEN** construction succeeds and preserves the configured priority order
|
||||||
|
|
||||||
|
#### Scenario: Invalid reserve or operating limit
|
||||||
|
- **WHEN** a reserve is negative or null, a token maximum is null or non-positive, slippage is outside its inclusive range, maximum price age is non-positive, or either duration is null or non-positive
|
||||||
|
- **THEN** construction fails before a worker or transaction is created
|
||||||
|
|
||||||
|
#### Scenario: Invalid input identity
|
||||||
|
- **WHEN** an entry or its currency or UUID is null, a UUID is unknown or duplicated, or the EVE UUID is configured as input
|
||||||
|
- **THEN** construction fails with a diagnostic identifying the invalid configuration
|
||||||
|
|
||||||
|
#### Scenario: Non-SOL representation is not unique and usable
|
||||||
|
- **WHEN** a configured non-SOL currency has zero or multiple `solana-mint` references or its sole external ID is null or blank
|
||||||
|
- **THEN** construction fails without adding mint or token-program selection to Evelyn's configuration model
|
||||||
|
|
||||||
|
#### Scenario: Maximum configuration is incomplete or ambiguous
|
||||||
|
- **WHEN** a maximum is missing for an accepted input, duplicated by UUID, or supplied for an unaccepted currency
|
||||||
|
- **THEN** construction fails before a worker or transaction is created
|
||||||
|
|
||||||
|
#### Scenario: Invalid maximum identity or value
|
||||||
|
- **WHEN** a maximum entry, amount, currency or UUID is null, or its UUID is unknown or resolves to an invalid canonical identity
|
||||||
|
- **THEN** construction fails with a diagnostic identifying the invalid maximum configuration
|
||||||
|
|
||||||
|
### Requirement: Fixed Evelyn domain values and internally resolved integration values
|
||||||
|
The service SHALL fix the EVE mint as `meveYG2iXYSkgSUn1T1uxcthH1EGMZdRHGgCntXZA3Y`, construct the EVE/USDT notification-price pair from canonical CIS resolutions of the stable EVE and USDT UUIDs, and use fixed notification wording. Native SOL SHALL be identified only by the stable SOL UUID; when sent to Jupiter, its input mint SHALL be the canonical CIS Solana-mint reference `So11111111111111111111111111111111111111112`. Mint addresses, token programs, price pairs, notification wording, and a separate SOL reserve SHALL NOT be constructor configuration.
|
||||||
|
|
||||||
|
#### Scenario: Native SOL configuration
|
||||||
|
- **WHEN** a configured input UUID is the stable SOL UUID
|
||||||
|
- **THEN** the service reads the native wallet balance and later supplies Jupiter with the CIS-resolved canonical SOL/WSOL mint rather than treating SOL as an SPL-token holding
|
||||||
|
|
||||||
|
#### Scenario: Fixed EVE behavior
|
||||||
|
- **WHEN** the service evaluates a burn and notification
|
||||||
|
- **THEN** it uses the fixed EVE mint and the canonical EVE/USDT pair without accepting replacements through construction
|
||||||
|
|
||||||
|
### Requirement: Prioritized input inspection with internal Solana translation
|
||||||
|
Unless swaps are suspended, an iteration SHALL inspect configured input entries in order until it finds the first usable candidate. For every entry it SHALL resolve the configured UUID to its canonical currency. Native SOL SHALL use `getSolanaBalance().amount()` and nine-decimal precision. Every other input SHALL use its unique CIS-resolved Solana mint; the service SHALL inspect the mint account owner to detect the original SPL Token Program or Token-2022, use that program for wallet balance lookup, and obtain precision from Solana mint metadata. A missing SPL-token account SHALL equal zero balance.
|
||||||
|
|
||||||
|
For each entry the service SHALL subtract its configured reserve and skip non-positive spendable balance. Missing or unusable balance, mint account, supported program, or mint metadata SHALL produce a warning and allow later configured entries to be examined. Interruption SHALL preserve the thread interrupt state and end processing rather than becoming a skipped candidate. Failure to select an input SHALL NOT prevent the later complete-EVE burn evaluation.
|
||||||
|
|
||||||
|
#### Scenario: Missing token account
|
||||||
|
- **WHEN** a configured non-SOL mint is valid but the wallet has no token account under its detected program
|
||||||
|
- **THEN** that input is treated as a zero balance and later inputs are considered
|
||||||
|
|
||||||
|
#### Scenario: Missing or unsupported mint account
|
||||||
|
- **WHEN** a configured token's mint account is absent or owned by an unsupported program
|
||||||
|
- **THEN** the service warns, skips that input, considers later priorities, and still reaches the EVE-balance step
|
||||||
|
|
||||||
|
#### Scenario: Candidate read is interrupted
|
||||||
|
- **WHEN** wallet, CIS-adjacent processing, or Solana access is interrupted while inspecting an input
|
||||||
|
- **THEN** the service preserves interruption and ends processing without treating the input as an ordinary failure
|
||||||
|
|
||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### 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 SHALL never exceed either bound through rounding. Input selection SHALL require no input/USDC Ticker observation. Maxima SHALL limit buybacks only; EVE burns SHALL remain uncapped. 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, 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, 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, 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 and the mint supports six decimals
|
||||||
|
- **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
|
||||||
|
|
||||||
|
### Requirement: Production burner accepts USDT and SyrupUSDC
|
||||||
|
The production Hub SHALL configure USDT first with reserve 0.1 and maximum input 0.25 USDT, and SyrupUSDC second with reserve 0.095 and maximum input 0.2 SyrupUSDC. These SHALL be token-denominated limits without a USD-value guarantee. The existing burner wallet, Jupiter route selection, registration, hourly interval, slippage, EVE/USDT freshness, confirmation and 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 two exact reserves and maxima as MoneyAmount values in USDT-then-SyrupUSDC priority and retains the other existing production arguments
|
||||||
|
|
||||||
|
## REMOVED Requirements
|
||||||
|
|
||||||
|
### Requirement: Fresh canonical input prices
|
||||||
|
**Reason**: Buyback limits are expressed in each accepted input's own units, so input/USDC observations are no longer needed.
|
||||||
|
**Migration**: Configure exactly one token-denominated maximum per accepted input; keep the existing fresh EVE/USDT observation before burning.
|
||||||
|
|
||||||
|
### Requirement: Exact USDC-capped amount and per-currency reserve
|
||||||
|
**Reason**: Replaced by exact token-capped amount and per-currency reserve.
|
||||||
|
**Migration**: Replace the scalar USDC-equivalent constructor maximum with the UUID-matched per-currency MoneyAmount list.
|
||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
# Tasks
|
||||||
|
|
||||||
|
## 1. Token-denominated constructor and selection
|
||||||
|
|
||||||
|
- [x] 1.1 Replace the scalar maximum with an immutable UUID-matched configuration, preserve reserve validation, and verify reordered maxima, defensive copying, empty lists and invalid maximum configurations with offline controlled dependencies.
|
||||||
|
- [x] 1.2 Remove input/USDC resolution and pricing, select the lesser spendable/token-maximum amount with existing downward precision rounding, and verify the SyrupUSDC balance examples, reserve boundary and zero-rounding skip offline.
|
||||||
|
- [x] 1.3 Update constructor and domain-service JavaDoc for token limits and EVE-only price freshness; verify no obsolete input-price or scalar-limit contract remains in current source documentation.
|
||||||
|
|
||||||
|
## 2. Production configuration and documentation
|
||||||
|
|
||||||
|
- [x] 2.1 Configure USDT reserve 0.1/maximum 0.25 first and SyrupUSDC reserve 0.095/maximum 0.2 second in Hub construction; verify exact values, unchanged remaining arguments and successful Gradle compilation.
|
||||||
|
- [x] 2.2 Document the production limits, Jupiter routing, unchanged latest-price API and direct-EVE behavior; verify documentation matches the source configuration and delta requirements.
|
||||||
|
|
||||||
|
## 3. Integration verification and OpenSpec completion
|
||||||
|
|
||||||
|
- [x] 3.1 Verify one-attempt priority, confirmed/failed/unknown swap handling, pending FINALIZED burns, complete EVE burning and stale/missing EVE observations offline without financial transactions; record results and limitations in the change.
|
||||||
|
- [x] 3.2 Validate the OpenSpec change, perform diff/whitespace checks, synchronize directly affected main requirements and verify the change is ready for archive with all implementation tasks and requirements complete.
|
||||||
+33
@@ -0,0 +1,33 @@
|
|||||||
|
# Verification
|
||||||
|
|
||||||
|
## Completed checks
|
||||||
|
|
||||||
|
- `openspec validate 90-add-syrupusdc-burner-input-with-per-currency-limits --strict`: passed.
|
||||||
|
- `openspec validate evelyn-iou-burner-service --type spec --strict`: passed, with informational long-description hints for existing/modified requirements.
|
||||||
|
- `git diff --check`: passed.
|
||||||
|
- Main-spec merge comparison: all four modified requirements and both added requirements match the delta, both removed requirements are absent, and unaffected requirements are preserved.
|
||||||
|
- Source comparison against HEAD confirms that lifecycle and iteration sequencing, swap execution and outcome handling, complete EVE reload/burn, pending burn recovery, FINALIZED waiting, notification behavior, fresh-price validation, Solana precision resolution and reserve validation are unchanged byte for byte.
|
||||||
|
- Current source documentation contains no obsolete scalar USDC limit, USDC canonical field, input-price conversion or fresh input/USDC requirement. The production values and priority match issue #90, including SyrupUSDC reserve 0.095.
|
||||||
|
|
||||||
|
## Compilation and controlled checks
|
||||||
|
|
||||||
|
The parent agent compiled the updated main sources and existing test sources successfully with the normal integrated Gradle/Detag workflow:
|
||||||
|
|
||||||
|
```text
|
||||||
|
JAVA_HOME=/usr/local/software/java/jfx-25 ./gradlew compileJava compileTestJava --offline --no-daemon
|
||||||
|
```
|
||||||
|
|
||||||
|
Result: `BUILD SUCCESSFUL in 13s`, four tasks executed. No permanent automated tests were added.
|
||||||
|
|
||||||
|
The temporary harness at `/tmp/evelyn-issue90/BurnerVerification.java` was compiled and executed with the same JDK against the newly built classes. Result: **PASS: 52 focused offline assertions**. These cover:
|
||||||
|
|
||||||
|
- SyrupUSDC balances 0.5 → 0.2 and 0.245 → 0.150, at/below 0.095 → skipped.
|
||||||
|
- USDT balance 0.35 → 0.25, with USDT first even when maxima are reversed.
|
||||||
|
- Downward six-decimal rounding and positive amounts that round to zero.
|
||||||
|
- Defensive configuration copying, empty lists and ten invalid-maxima configurations, including missing, duplicate, extra, non-positive, null and unknown values/identities.
|
||||||
|
- No input/USDC Ticker observation, and both missing and stale EVE/USDT observations postponing burns while still permitting input selection.
|
||||||
|
- Complete direct-EVE burning with empty input lists, FINALIZED confirmation, unchanged timeout and one proof notification using the burn signature.
|
||||||
|
|
||||||
|
All financial dependencies and notification delivery were controlled. No worker, live transaction or external notification was started. Swap/burn failure and pending recovery paths were verified by exact source comparison against HEAD; the harness does not claim exhaustive runtime coverage of those unchanged branches.
|
||||||
|
|
||||||
|
Main requirements are synchronized and all implementation tasks are complete. No deployment, push, Gitea write or live financial transaction has been performed.
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
schema: spec-driven
|
||||||
|
created: 2026-10-05
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
# Design
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
See proposal.md and issue #91. The burner currently loses a known timed-out swap's identity behind `swapsSuspended`. Jupiter retains its order's last valid block height internally, but structured outcomes expose no validity metadata and confirmation I/O failures expose a signature only in text. Solana awaiting already uses historical status lookup and a shared five-second gate.
|
||||||
|
|
||||||
|
## Goals / Non-Goals
|
||||||
|
|
||||||
|
**Goals:** Resume buybacks after a safely resolved original transaction while maintaining duplicate prevention, process-local state and the existing burn path.
|
||||||
|
|
||||||
|
**Non-Goals:** Build persistent submission infrastructure or change token amount policy, provider selection, burn confirmation or worker scheduling.
|
||||||
|
|
||||||
|
## Decisions
|
||||||
|
|
||||||
|
1. Keep the shared transaction outcome enum unchanged. Add a small separate reconciliation result and original transaction-validity value in the Solana package, and a read-only reconciliation method. The cached decorator delegates it directly. An additive conservative default permits existing blockchain implementations to await but never infer expiry.
|
||||||
|
2. Extend the existing structured Jupiter outcome exception with optional validity metadata while preserving the three-argument constructor used by perps. Add typed IOException and InterruptedException subclasses carrying submission-stage certainty, optional trustworthy signature, original validity and the original cause. Preparation failures are explicitly pre-submission; ambiguous execution failures without a signature retain manual suspension. A known confirmation signature is retained even when confirmation I/O or interruption fails.
|
||||||
|
3. Capture a finalized pre-submission slot, taker address and the order's last valid block height. Extract the original recent blockhash from the serialized transaction message and reject a signer response that changes it. Bind the execution signature to the signed transaction's first signature when it is populated. A sponsor signature completed by Jupiter retains provider identity for status checks, but cannot prove expiry of the local payload. No unrelated latest blockhash participates in expiry evidence.
|
||||||
|
4. Reconcile in a bounded thirty-second operation using the existing shared RPC gate/deadline. A historical status at CONFIRMED or stronger resolves success/failure. Any lower-commitment observation remains pending. For an absent signature, require finalized epoch height above the original validity height, the original blockhash to be invalid at that finalized context, local `minimumLedgerSlot` at/before the original submission anchor, and a subsequent finalized signer-address history lookup with `minContextSlot` at/after that finalized context and no original signature. One page (limit 1000) must extend strictly before the submission anchor or exhaust the covered retained history; a full page ending at/after the anchor remains unresolved. Missing, pruned, stale, inconsistent or failed evidence remains unresolved. This trusts the configured RPC's retained-history contract; it does not treat a remote archive's earliest block as contiguous local coverage.
|
||||||
|
5. Pending burns retain absolute priority. Otherwise pending swaps are reconciled before input selection; resolution clears state and selection reloads wallet balances. Unresolved swaps block only new swaps and still reach the existing complete-EVE burn path. Never call `swap()` as reconciliation or resubmit/re-sign an unresolved operation.
|
||||||
|
6. Log canonical symbol/UUID, attempted amount, known signature, status and next action. Log safe phase/cause classes instead of transport body, signer payload, transaction bytes or arbitrary cause messages.
|
||||||
|
|
||||||
|
## Risks / Trade-offs
|
||||||
|
|
||||||
|
- History pruned before the retained anchor or incomplete/contradictory evidence → keep pending and diagnose the missing evidence; no speculative recovery.
|
||||||
|
- A process restart discards pending state → document external reconciliation before restart and retain no persistent journal.
|
||||||
|
- Additional read-only preparation/reconciliation calls add gate latency → use shared pacing and a bounded reconciliation deadline; add no immediate retry loop.
|
||||||
|
- RPC evidence cannot be independently audited against the entire chain → rely only on the configured history-enabled RPC's local-retention contract and preserve pending on detected uncertainty.
|
||||||
|
|
||||||
|
## Primary references
|
||||||
|
|
||||||
|
- [Solana historical signature status](https://solana.com/docs/rpc/http/getsignaturestatuses).
|
||||||
|
- [Solana transaction retry guidance](https://solana.com/developers/cookbook/transactions/retry).
|
||||||
|
- [minimumLedgerSlot](https://solana.com/docs/rpc/http/minimumledgerslot) identifies the lowest retained local ledger slot, which may advance as history is purged.
|
||||||
|
- [isBlockhashValid](https://solana.com/docs/rpc/http/isblockhashvalid) supports commitment and minimum context slot.
|
||||||
|
- [Finalized address history](https://solana.com/docs/rpc/http/getsignaturesforaddress) binds absence to a minimum finalized context; Agave rejects a finalized request if its highest super-majority root has not reached that context.
|
||||||
|
- [Agave RPC implementation](https://github.com/anza-xyz/agave/blob/master/rpc/src/rpc.rs) checks transaction-history availability for historical signature lookup; getFirstAvailableBlock may include BigTable and is not used as local coverage evidence.
|
||||||
|
|
||||||
|
## Migration Plan
|
||||||
|
|
||||||
|
Deployment remains separate. Replace the application only after reconciling unknown transactions in any old process; restarting either version loses in-memory pending state. No configuration or history migration is required, and current reserves/maxima remain unchanged.
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# Proposal
|
||||||
|
|
||||||
|
## Why
|
||||||
|
|
||||||
|
Issue #91 addresses known Jupiter swaps that time out and currently suspend Evelyn buybacks until a Hub restart. Retaining and reconciling the original transaction safely lets normal hourly buybacks resume after its outcome is resolved, without risking duplicate economic operations.
|
||||||
|
|
||||||
|
## What Changes
|
||||||
|
|
||||||
|
- Retain process-local pending swap signature, input, attempted amount and original validity metadata after an unknown outcome.
|
||||||
|
- Reconcile that same signature read-only in later iterations at CONFIRMED or stronger; resume selection only after success, on-chain failure or demonstrated non-executed expiry, using freshly read balances.
|
||||||
|
- Bind original validity to the actual signed transaction identity where available; sponsor-completed identity can resolve status but cannot prove local-payload expiry.
|
||||||
|
- Preserve known signatures and original causes in structured confirmation errors; classify proven preparation failures as retryable and retain manual handling for uncertain failures without a trustworthy signature.
|
||||||
|
- Preserve pending-burn priority, existing EVE burns while a swap remains unresolved, one new swap per iteration, all current reserves/maxima and Ticker behavior.
|
||||||
|
- Add safe operational diagnostics and focused offline verification.
|
||||||
|
|
||||||
|
## Capabilities
|
||||||
|
|
||||||
|
### New Capabilities
|
||||||
|
|
||||||
|
None.
|
||||||
|
|
||||||
|
### Modified Capabilities
|
||||||
|
|
||||||
|
- `evelyn-iou-burner-service`: Later-iteration reconciliation and safe recovery of pending swaps.
|
||||||
|
- `jupiter-swap-service`: Structured submission stage/signature/cause and original validity context.
|
||||||
|
- `solana-transaction-awaiting`: Small read-only reconciliation support with conservative expiry/history evidence.
|
||||||
|
|
||||||
|
## Impact
|
||||||
|
|
||||||
|
The burner, Jupiter swap integration, shared structured Jupiter outcome exception, Solana API/reference implementation/cached delegate, and directly affected documentation/specifications are involved. Existing outcome statuses and perps callers remain compatible. No persistent journal, rebroadcast, automatic retry/re-signing, provider routing, live transaction, deployment, #92 minimum configuration, ValueTag change or unrelated burn/lifecycle work is included.
|
||||||
+69
@@ -0,0 +1,69 @@
|
|||||||
|
# Spec Delta
|
||||||
|
|
||||||
|
## MODIFIED Requirements
|
||||||
|
|
||||||
|
### Requirement: Immediate sequential fixed-delay lifecycle
|
||||||
|
Each reference implementation instance SHALL own at most one dedicated background worker. A successful Hub-owned `NenjimProcess.start()` SHALL begin the first iteration immediately; every later iteration SHALL begin only after the previous iteration completes and the configured fixed delay elapses; and iterations SHALL never overlap. A repeated process start while active SHALL NOT create another worker. If scheduling the worker fails during startup, the implementation SHALL stop the newly created executor, clear its worker reference and process-local state, and report startup failure.
|
||||||
|
|
||||||
|
Pending swap/burn information and manual swap/burn suspension flags SHALL be process-local only. A complete process restart SHALL clear those states. The public JavaDoc SHALL disclose that restart loses reconciliation state and can weaken duplicate prevention until unknown transactions have been reconciled externally. Neither the service interface nor the implementation SHALL expose ordinary stop or restart operations.
|
||||||
|
|
||||||
|
#### Scenario: First and later iterations
|
||||||
|
- **WHEN** the Hub starts the Burner process and one iteration takes longer than usual
|
||||||
|
- **THEN** the first iteration starts without initial delay and the next starts only after completion plus the configured interval
|
||||||
|
|
||||||
|
#### Scenario: Active service is started again
|
||||||
|
- **WHEN** `NenjimProcess.start()` is called while the instance already owns an active worker
|
||||||
|
- **THEN** the call is rejected without creating an overlapping worker
|
||||||
|
|
||||||
|
#### Scenario: Explicit lifecycle reset
|
||||||
|
- **WHEN** a complete process restart occurs after pending or suspended process-local state has been externally reconciled
|
||||||
|
- **THEN** the new process begins without that prior in-memory state and reloads wallet and chain state normally
|
||||||
|
|
||||||
|
### Requirement: At most one confirmed swap attempt per iteration
|
||||||
|
After selecting a positive exact amount, 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 and maximum input 0.25 USDT, and SyrupUSDC second with reserve 0.095 and maximum input 0.2 SyrupUSDC. These SHALL be token-denominated limits without a USD-value guarantee. 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 two exact reserves and maxima as MoneyAmount values in USDT-then-SyrupUSDC priority and retains the other existing production arguments
|
||||||
|
|
||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: Pending swaps reconcile before new input selection
|
||||||
|
After pending-burn priority, a retained swap SHALL be checked read-only by its original signature at CONFIRMED or stronger in a bounded operation. Success, on-chain failure or demonstrated non-executed expiry SHALL clear it and permit newly read balances to drive at most one new swap. Unknown, absent without expiry proof or I/O outcomes SHALL remain pending without a new swap while allowing existing EVE burns. Interruption SHALL propagate with pending state intact.
|
||||||
|
|
||||||
|
#### Scenario: Original swap resolves
|
||||||
|
- **WHEN** a later check resolves the original signature as successful, definitively failed or proven non-executed and expired
|
||||||
|
- **THEN** pending swap state is cleared, complete failure details are logged when applicable, and any new selection uses newly read balances
|
||||||
|
|
||||||
|
#### Scenario: Original swap remains unresolved
|
||||||
|
- **WHEN** a later check times out, lacks trustworthy expiry/history evidence, observes only lower commitment or encounters I/O failure
|
||||||
|
- **THEN** the same pending signature is retained for the next scheduled iteration, no new swap occurs and the existing EVE burn path still runs
|
||||||
|
|
||||||
|
#### Scenario: Burn remains pending
|
||||||
|
- **WHEN** both a swap and a burn are pending at iteration start
|
||||||
|
- **THEN** only the burn signature is awaited and no swap reconciliation or new transaction is performed
|
||||||
|
|
||||||
|
### Requirement: Safe swap recovery diagnostics
|
||||||
|
Swap diagnostics SHALL identify canonical input symbol/UUID, attempted amount, known signature, submission or reconciliation state, safe cause details and next action. Timeout SHALL be described as unknown. Credentials, arbitrary provider/signer bodies and serialized transaction payloads SHALL NOT be logged. Pending swap state SHALL remain process-local; restart SHALL lose it and require external reconciliation before safely resuming uncertain activity.
|
||||||
|
|
||||||
|
#### Scenario: Unknown outcome is diagnosed
|
||||||
|
- **WHEN** a swap outcome remains unknown
|
||||||
|
- **THEN** diagnostics identify whether its original signature will be checked next iteration or manual investigation is required, without exposing secret or payload data
|
||||||
|
|
||||||
+59
@@ -0,0 +1,59 @@
|
|||||||
|
# Spec Delta
|
||||||
|
|
||||||
|
## MODIFIED Requirements
|
||||||
|
|
||||||
|
### Requirement: Confirmed execution result validation
|
||||||
|
The service SHALL first accept a Jupiter execution response only when it has status `Success`, explicitly has result code `0`, contains a non-blank transaction signature, and contains positive valid actual total input and output raw amounts. It SHALL convert each actual amount using the independently resolved precision of its mint. After this provider-response validation, the service SHALL independently await the returned signature on Solana at `CONFIRMED` using its configured confirmation timeout and SHALL return the already validated swap result only for a `SUCCEEDED` outcome. Non-success HTTP responses, malformed responses, expired or rejected swaps, failed or contradictory status/code combinations, missing result codes, blank signatures, and invalid result amounts SHALL remain provider failures and SHALL not be represented as successful swaps.
|
||||||
|
|
||||||
|
#### Scenario: Provider response and on-chain confirmation succeed
|
||||||
|
- **WHEN** Jupiter reports status `Success`, code `0`, a signature, and valid actual total input and output amounts, and that signature independently reaches `CONFIRMED` with a `SUCCEEDED` outcome
|
||||||
|
- **THEN** the service returns the signature and those actual amounts in human-readable units
|
||||||
|
|
||||||
|
#### Scenario: Jupiter rejects or fails execution
|
||||||
|
- **WHEN** Jupiter returns a failed status, a missing or non-zero result code, a contradictory status/code combination, expiration, rejection, non-success HTTP status, malformed body, blank signature, or invalid actual amount
|
||||||
|
- **THEN** the service reports an I/O failure without treating provider validation as an on-chain confirmation
|
||||||
|
|
||||||
|
#### Scenario: Provider succeeds but transaction fails on-chain
|
||||||
|
- **WHEN** a fully validated Jupiter execution response contains a signature whose independent `CONFIRMED` outcome is `FAILED`
|
||||||
|
- **THEN** the service throws a structured checked outcome exception preserving the signature, requested commitment, slot, and complete compact Solana failure details
|
||||||
|
|
||||||
|
#### Scenario: Provider succeeds but confirmation times out
|
||||||
|
- **WHEN** a fully validated Jupiter execution response contains a signature whose independent `CONFIRMED` outcome is `TIMED_OUT`
|
||||||
|
- **THEN** the service throws a structured checked outcome exception preserving the signature, requested commitment, original validity metadata and unknown outcome warning and does not automatically resubmit an equivalent transaction
|
||||||
|
|
||||||
|
### Requirement: Post-submission communication and interruption policy
|
||||||
|
If independent confirmation fails with an I/O error after the signature is known, the service SHALL throw a structured `IOException` preserving the original signature, original transaction-validity metadata, submission uncertainty and underlying cause. Interruption SHALL propagate as `InterruptedException` while preserving known signature/validity context in structured form without another RPC or Jupiter request. Neither condition SHALL trigger rebuilding, re-signing, retrying, or resubmitting a transaction.
|
||||||
|
|
||||||
|
#### Scenario: Confirmation communication fails
|
||||||
|
- **WHEN** independent confirmation throws an I/O error for a known submitted signature
|
||||||
|
- **THEN** the service reports the structured signature, original validity metadata and unknown on-chain outcome while preserving the original error and performs no retry or resubmission
|
||||||
|
|
||||||
|
#### Scenario: Confirmation is interrupted
|
||||||
|
- **WHEN** independent confirmation throws `InterruptedException`
|
||||||
|
- **THEN** that interruption retains its known signature and validity context, propagates normally and the service performs no subsequent request
|
||||||
|
|
||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: Submission certainty and original validity remain structured
|
||||||
|
The integration SHALL expose submission certainty, known signature, original cause and original validity in structured form. Original validity SHALL retain the order height, actual blockhash, taker and finalized submission anchor. Signing SHALL preserve the hash and required taker signer. Expiry evidence SHALL be bound to a populated local first signature; contradictory provider identity SHALL require manual handling. Existing perps outcome construction SHALL remain compatible.
|
||||||
|
|
||||||
|
#### Scenario: Preparation fails
|
||||||
|
- **WHEN** mint/order acquisition, validation, signing or pre-submission validity preparation fails before execution submission
|
||||||
|
- **THEN** the error is structured as pre-submission and no execution or automatic retry occurs
|
||||||
|
|
||||||
|
#### Scenario: Execution response is lost
|
||||||
|
- **WHEN** execution may have been submitted and no trustworthy signature is available
|
||||||
|
- **THEN** the error remains structured as uncertain submission and does not authorize automatic recovery or resubmission
|
||||||
|
|
||||||
|
#### Scenario: Provider identity contradicts the signed transaction
|
||||||
|
- **WHEN** an execution response signature differs from the populated first signature of the signed payload
|
||||||
|
- **THEN** the result is not treated as confirmed and uncertain submission requires manual reconciliation
|
||||||
|
|
||||||
|
#### Scenario: Sponsor identity is completed by Jupiter
|
||||||
|
- **WHEN** Jupiter supplies a valid provider signature while the local first signature is blank
|
||||||
|
- **THEN** the signature can be awaited and reconciled by status, but original local validity cannot establish non-executed expiry
|
||||||
|
|
||||||
|
#### Scenario: Timeout retains original validity
|
||||||
|
- **WHEN** confirmation of a known submitted signature times out
|
||||||
|
- **THEN** the exceptional outcome exposes its original validity data without substituting a new transaction or blockhash
|
||||||
|
|
||||||
+33
@@ -0,0 +1,33 @@
|
|||||||
|
# Spec Delta
|
||||||
|
|
||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: Read-only transaction reconciliation
|
||||||
|
The blockchain SHALL support bounded reconciliation of an existing signature at CONFIRMED or stronger, retaining normal await behavior and outcome statuses. Reconciliation SHALL distinguish definitive success/failure, demonstrated non-executed expiry and unresolved evidence. It SHALL use the existing shared RPC gate/deadline and historical status lookup without submission, caching, immediate retries or a new pacing mechanism. Unsupported expiry evidence SHALL remain unresolved.
|
||||||
|
|
||||||
|
#### Scenario: Definitive historical status
|
||||||
|
- **WHEN** the original signature is observed at or above requested commitment
|
||||||
|
- **THEN** reconciliation returns its successful or failed outcome with slot and complete failure details as applicable
|
||||||
|
|
||||||
|
#### Scenario: Below commitment or interrupted
|
||||||
|
- **WHEN** the signature is only observed below commitment or the check is interrupted
|
||||||
|
- **THEN** expiry is not inferred; an unresolved result is returned for lower commitment and interruption propagates normally
|
||||||
|
|
||||||
|
#### Scenario: Cached decorator
|
||||||
|
- **WHEN** reconciliation is invoked through the cached decorator
|
||||||
|
- **THEN** it reaches the delegate independently on every call
|
||||||
|
|
||||||
|
### Requirement: Expiry requires original validity and reliable local history absence
|
||||||
|
Expiry SHALL require signature-bound original validity, finalized height above its last valid height, original blockhash invalidity, retained local history through its submission anchor and fresh finalized signer-history absence. Missing, pruned, stale, lower-commitment or inconsistent evidence SHALL remain unresolved. Absence, elapsed time or blockhash invalidity alone SHALL NOT authorize clearing a pending transaction.
|
||||||
|
|
||||||
|
#### Scenario: Non-executed expiry is established
|
||||||
|
- **WHEN** all original-validity, finalized-height, original-blockhash, local-history-coverage and finalized signer-history absence checks succeed, with `minContextSlot` at/after the expired finalized context and a bounded page reaching before the submission anchor or exhausting covered retained history
|
||||||
|
- **THEN** reconciliation identifies a non-executed expired transaction without resubmitting it
|
||||||
|
|
||||||
|
#### Scenario: Evidence is incomplete
|
||||||
|
- **WHEN** original metadata is missing, history is unavailable/pruned, a lookup fails, a response is inconsistent/stale or the original blockhash is still valid
|
||||||
|
- **THEN** no expiry result is returned and the diagnostic explains the missing evidence
|
||||||
|
|
||||||
|
#### Scenario: Bounded history page is incomplete
|
||||||
|
- **WHEN** a full history page ends at or after the submission anchor
|
||||||
|
- **THEN** the transaction remains unresolved instead of treating partial absence as proof
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# Tasks
|
||||||
|
|
||||||
|
## 1. Preserve submitted swap identity
|
||||||
|
|
||||||
|
- [x] 1.1 Add compatible structured validity, I/O and interruption metadata; verify original signature/cause and pre-submission certainty offline.
|
||||||
|
- [x] 1.2 Retain the actual transaction blockhash, original last-valid height and pre-submission anchor; verify signer hash consistency without live submission.
|
||||||
|
|
||||||
|
## 2. Reconcile pending swaps safely
|
||||||
|
|
||||||
|
- [x] 2.1 Add bounded read-only Solana reconciliation and direct cached delegation; verify confirmed outcomes and conservative finalized-history expiry evidence with controlled RPC responses.
|
||||||
|
- [x] 2.2 Preserve process-local pending swaps and safe diagnostics in the burner; verify scheduled recovery, unresolved suppression, fresh balances, EVE burn continuation and pending-burn priority offline.
|
||||||
|
|
||||||
|
## 3. Complete the documented change
|
||||||
|
|
||||||
|
- [x] 3.1 Update service documentation and operational restart limitations; review unchanged amount policy, transaction submission and burn recovery behavior.
|
||||||
|
- [x] 3.2 Compile existing main/test sources, run the temporary focused verification harness, record evidence and validate OpenSpec.
|
||||||
|
- [x] 3.3 Sync main specifications and archive the completed change after checking implementation and verification evidence.
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# Verification
|
||||||
|
|
||||||
|
Verified on 2026-10-06 with JDK `/usr/local/software/java/jfx-25`.
|
||||||
|
|
||||||
|
## Compilation and offline checks
|
||||||
|
|
||||||
|
- Final parent compilation: `JAVA_HOME=/usr/local/software/java/jfx-25 ./gradlew compileJava compileTestJava --offline --no-daemon` → **BUILD SUCCESSFUL in 7s**; `detagMain` and `compileJava` executed against the final safe order-diagnostic correction and `compileTestJava` was up-to-date. The preceding successful 9s build also executed `compileTestJava`.
|
||||||
|
- Temporary harness compiled with `javac --enable-preview --release 25 -cp 'build/classes/java/main:build/libs/*' -d /tmp/evelyn-issue91 /tmp/evelyn-issue91/Issue91Verification.java`.
|
||||||
|
- `java --enable-preview -cp '/tmp/evelyn-issue91:build/classes/java/main:build/libs/*' Issue91Verification` → **PASS: 102 focused offline assertions**. Every wallet, swap and HTTP dependency is controlled; no live signing, submission or remote RPC occurs.
|
||||||
|
- The harness's initial deadline fixture omitted the gate's completed-call flag. Correcting that temporary fixture produced the successful run; no implementation change was necessary. An earlier harness compilation preceded the parent build and saw old classes; it is superseded by this final compilation/run.
|
||||||
|
|
||||||
|
Coverage includes later-iteration success/failure/expiry recovery using fresh balances; same-signature bounded CONFIRMED checks; unresolved/I/O/interruption suppression; pre-submission retry and manual signature-less uncertainty; EVE burn continuation, stale-price gate and pending-burn priority; original timeout/signature/validity/cause retention; compatible perps construction; actual legacy/v0 blockhash and required-taker extraction; blank sponsor identity; invalid provider signature rejection; and finalized expiry evidence with pruning, full history pages, minContextSlot, lower commitment, stale/inconsistent evidence and deadline handling. Safe numeric RPC and Jupiter order HTTP/provider codes are retained without provider bodies/error messages.
|
||||||
|
|
||||||
|
## Review and OpenSpec
|
||||||
|
|
||||||
|
- `git diff --check`: passed.
|
||||||
|
- Strict validation of the active change and all three synced main specifications: passed. All ten delta requirements match the main specs exactly. After archive, `openspec list --json` reports no active changes and `openspec validate --archived --json --report findings` passes all 29 archived changes with no findings. Existing long-requirement informational hints remain. After intentional main-spec synchronization the change validator may note that ADDED requirements already exist; the semantic archive workflow preserves that synchronized state.
|
||||||
|
- Independent parent review found no remaining material findings; its safety corrections were implemented before the successful final build.
|
||||||
|
- Static diff review confirms current reserves/maxima, Hub factory, input selection, Ticker behavior, EVE balance/price gate, burn submission/await/notification and fixed-delay scheduling are unchanged. The provider/local signature contradiction branch retains manual handling. The cached decorator delegates reconciliation directly.
|
||||||
|
- There remains one managed execution call and no automatic resubmission, persistent journal, ValueTag change or issue #92 minimum-input policy.
|
||||||
|
|
||||||
|
## Limits
|
||||||
|
|
||||||
|
The harness is temporary and outside the repository; no permanent automated test was added. Full live Jupiter/provider integration was not exercised. Expiry relies on the configured RPC's reliable retained-history contract and remains pending when original signature binding, history coverage or finalized evidence is insufficient. Sponsor-completed signatures can resolve by status but cannot establish local-payload expiry. Restart loses process-local pending state, requiring external reconciliation before safely resuming unknown activity. No deployment, push or Gitea change was made.
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
schema: spec-driven
|
||||||
|
created: 2026-10-06
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# Design
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
See proposal.md and issue #92. The constructor currently owns ordered reserve configuration and immutable UUID-indexed maxima. Candidate selection already rounds down before invoking Jupiter; pending recovery precedes selection.
|
||||||
|
|
||||||
|
## Goals / Non-Goals
|
||||||
|
|
||||||
|
**Goals:** Extend existing token amount policy safely with a lower bound on actual attempted inputs.
|
||||||
|
|
||||||
|
**Non-Goals:** Introduce a configuration hierarchy, compatibility bypass, precision rounding for minima, or change execution/recovery behavior.
|
||||||
|
|
||||||
|
## Decisions
|
||||||
|
|
||||||
|
1. Add required minima between reserves and maxima. Reuse one private positive per-UUID amount validator for minima and maxima, copy them to immutable maps with canonical CurrencyType values, and reject minimum > maximum after both maps exist. Reserve order alone retains priority. A duplicated independent validator would unnecessarily duplicate the existing identity/set rules.
|
||||||
|
2. Compare the unchanged rounded candidate with its minimum using `compareTo`, preserving numeric equality across decimal scales. A pre-round comparison would permit attempted inputs below minimum; rounding upward or reserve top-up would violate existing spend bounds. Minima need not be representable at mint precision: they remain exact lower bounds, and an unachievable minimum simply makes that candidate ineligible.
|
||||||
|
3. Make production minima explicit MoneyAmount values in the existing Hub factory and update the single current call site. No old-signature overload bypasses the required policy.
|
||||||
|
4. Keep offline verification temporary, covering issue examples, precision/scale edges, config validation/copying, direct EVE and the prior recovery harness with only its constructor adapted.
|
||||||
|
|
||||||
|
## Risks / Trade-offs
|
||||||
|
|
||||||
|
- Incorrect UUID matching or list reordering → use canonical UUID maps and verify stale display metadata/reordered lists.
|
||||||
|
- Precision makes a configured minimum unachievable → skip the rounded candidate; never promote amount.
|
||||||
|
- Constructor signature changes → update the only current construction site and compile all existing main/test sources.
|
||||||
|
|
||||||
|
## Migration Plan
|
||||||
|
|
||||||
|
Implementation adds explicit production configuration; deployment remains a separate task. No persistent data, ticker history or ValueTag migration is required.
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# Proposal
|
||||||
|
|
||||||
|
## Why
|
||||||
|
|
||||||
|
Issue #92 prevents unsuitable tiny buybacks above the reserve from blocking useful later inputs. Token-denominated minima let Evelyn skip those candidates while preserving reserve safety, priority and existing recovery.
|
||||||
|
|
||||||
|
## What Changes
|
||||||
|
|
||||||
|
- **BREAKING** Add a required `List<MoneyAmount>` of per-currency minimum swap inputs to the reference constructor beside reserves and maxima; match canonical UUIDs and copy configuration defensively.
|
||||||
|
- Reject missing, extra, unknown, duplicate or null minimum identities/values, non-positive minima and minima above their matched maxima.
|
||||||
|
- Compare the actual downward-rounded candidate with its minimum. Skip smaller candidates and continue input inspection; accept equality without spending reserves or rounding upward.
|
||||||
|
- Configure production USDT minimum 0.05 and SyrupUSDC minimum 0.045, with current reserve/maxima values and USDT priority retained.
|
||||||
|
- Update directly affected documentation/specs and perform focused offline verification without permanent tests or live transactions.
|
||||||
|
|
||||||
|
## Capabilities
|
||||||
|
|
||||||
|
### New Capabilities
|
||||||
|
|
||||||
|
None.
|
||||||
|
|
||||||
|
### Modified Capabilities
|
||||||
|
|
||||||
|
- `evelyn-iou-burner-service`: Per-input minimum configuration, validation and qualification after precision rounding.
|
||||||
|
|
||||||
|
## Impact
|
||||||
|
|
||||||
|
The burner reference constructor and selection, the Hub production call site, domain/constructor JavaDoc and Evelyn configuration documentation change. Jupiter, Ticker, token discovery, lifecycle, pending swap/burn recovery, notification and ValueTags remain unchanged. Implementation and deployment remain separate.
|
||||||
+125
@@ -0,0 +1,125 @@
|
|||||||
|
# Spec Delta
|
||||||
|
|
||||||
|
## MODIFIED Requirements
|
||||||
|
|
||||||
|
### Requirement: Ordered AssetAZ input configuration
|
||||||
|
The authoritative constructor SHALL accept a non-null ordered `List<MoneyAmount>` named `inputCurrencyMinimumReserves`, defensively copy it without changing order, and interpret inclusion as acceptance, list position as priority, each `currencyType().id()` as the authoritative AssetAZ identity, and each `amount()` as the minimum balance that must remain. It SHALL ignore supplied currency name and symbol metadata by resolving each UUID through the injected Currency Identity Service and using the canonical returned currency for processing. The constructor SHALL also accept a non-null `List<MoneyAmount>` named `inputCurrencyMaximumSwapAmounts`, defensively copy its values, and match each maximum to an accepted input by canonical currency UUID, independently of maximum-list order or supplied metadata. Each maximum SHALL be expressed in that same currency's own units. The constructor SHALL additionally accept `inputCurrencyMinimumSwapAmounts`, defensively copy its values and match strictly positive token-denominated minima by canonical UUID independently of order/name/symbol. Empty reserves, minima and maxima lists SHALL be valid and SHALL still permit directly deposited EVE to be burned.
|
||||||
|
|
||||||
|
#### Scenario: Stale configured metadata
|
||||||
|
- **WHEN** a configured `MoneyAmount` carries a known UUID but stale name or symbol metadata
|
||||||
|
- **THEN** the service processes the current canonical currency resolved for that UUID
|
||||||
|
|
||||||
|
#### Scenario: Priority is preserved
|
||||||
|
- **WHEN** multiple configured entries have rounded amounts at/above their own minimum and usable Solana data
|
||||||
|
- **THEN** the earliest entry in the defensively copied list is the only input selected in that iteration
|
||||||
|
|
||||||
|
#### Scenario: Empty input list
|
||||||
|
- **WHEN** no buyback inputs are configured but the wallet contains EVE
|
||||||
|
- **THEN** the service skips input selection and still evaluates the complete EVE balance for burning
|
||||||
|
|
||||||
|
#### Scenario: Maximum list order is independent
|
||||||
|
- **WHEN** maxima are supplied in a different order from the reserve list or carry stale display metadata for known UUIDs
|
||||||
|
- **THEN** each input receives the maximum for its UUID and reserve-list priority is preserved
|
||||||
|
|
||||||
|
#### Scenario: Caller changes its configuration lists
|
||||||
|
- **WHEN** a caller changes any supplied list after construction
|
||||||
|
- **THEN** the accepted currencies, their priority, reserves, minima and maxima remain as validated during construction
|
||||||
|
|
||||||
|
#### Scenario: Minimum list order and display metadata are independent
|
||||||
|
- **WHEN** minima are reordered or use stale names/symbols for known canonical UUIDs
|
||||||
|
- **THEN** each input receives its UUID's minimum and only reserve-list order controls priority
|
||||||
|
|
||||||
|
### Requirement: Complete constructor validation
|
||||||
|
Construction SHALL require all injected services, all three input configuration lists, and durations to be non-null; every list entry, entry amount, entry currency, and currency UUID to be non-null; each reserve to be zero or greater; each minimum and maximum to be strictly positive; each minimum to be no greater than its matched maximum; each UUID to be known to the injected Currency Identity Service; UUIDs to be unique within each list; both minimum and maximum UUID sets to exactly equal the accepted reserve UUID set; and EVE not to be an input currency. It SHALL require slippage to be from 0 through 10000 basis points inclusive, `maximumPriceAge` to be greater than zero milliseconds, and both finalization timeout and iteration interval to be positive and non-zero.
|
||||||
|
|
||||||
|
Every configured non-SOL UUID SHALL resolve through reverse CIS lookup to exactly one external reference in the `solana-mint` namespace, and that reference's external ID SHALL be non-null and non-blank. Construction SHALL fail for a missing, ambiguous, null, or blank non-SOL mint representation. These burner-specific rules SHALL NOT change the general-purpose `MoneyAmount` contract.
|
||||||
|
|
||||||
|
#### Scenario: Valid configuration
|
||||||
|
- **WHEN** every dependency and value is valid, reserves are non-negative, every accepted input has exactly one positive minimum and maximum with minimum <= maximum, UUIDs are distinct and known within each list, EVE is absent, and every non-SOL input has one non-blank Solana mint reference
|
||||||
|
- **THEN** construction succeeds and preserves the configured priority order
|
||||||
|
|
||||||
|
#### Scenario: Invalid reserve or operating limit
|
||||||
|
- **WHEN** a reserve is negative or null, a token maximum is null or non-positive, slippage is outside its inclusive range, maximum price age is non-positive, or either duration is null or non-positive
|
||||||
|
- **THEN** construction fails before a worker or transaction is created
|
||||||
|
|
||||||
|
#### Scenario: Invalid input identity
|
||||||
|
- **WHEN** an entry or its currency or UUID is null, a UUID is unknown or duplicated, or the EVE UUID is configured as input
|
||||||
|
- **THEN** construction fails with a diagnostic identifying the invalid configuration
|
||||||
|
|
||||||
|
#### Scenario: Non-SOL representation is not unique and usable
|
||||||
|
- **WHEN** a configured non-SOL currency has zero or multiple `solana-mint` references or its sole external ID is null or blank
|
||||||
|
- **THEN** construction fails without adding mint or token-program selection to Evelyn's configuration model
|
||||||
|
|
||||||
|
#### Scenario: Maximum configuration is incomplete or ambiguous
|
||||||
|
- **WHEN** a maximum is missing for an accepted input, duplicated by UUID, or supplied for an unaccepted currency
|
||||||
|
- **THEN** construction fails before a worker or transaction is created
|
||||||
|
|
||||||
|
#### Scenario: Invalid maximum identity or value
|
||||||
|
- **WHEN** a maximum entry, amount, currency or UUID is null, or its UUID is unknown or resolves to an invalid canonical identity
|
||||||
|
- **THEN** construction fails with a diagnostic identifying the invalid maximum configuration
|
||||||
|
|
||||||
|
#### Scenario: Invalid minimum configuration
|
||||||
|
- **WHEN** the minimum list or an entry, amount, currency or UUID is null; a UUID is unknown, duplicated, extra or missing; a minimum is non-positive; or it exceeds that UUID's maximum
|
||||||
|
- **THEN** construction fails before a worker or transaction is created
|
||||||
|
|
||||||
|
#### Scenario: Minimum equals maximum
|
||||||
|
- **WHEN** a strictly positive minimum is numerically equal to its matched maximum
|
||||||
|
- **THEN** that amount configuration is accepted without requiring equal decimal scales
|
||||||
|
|
||||||
|
#### Scenario: Direct EVE only configuration
|
||||||
|
- **WHEN** all three configuration lists are empty
|
||||||
|
- **THEN** construction succeeds and direct EVE remains eligible for burning
|
||||||
|
|
||||||
|
### 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 unaffected. 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 burned by the existing rules
|
||||||
|
|
||||||
|
### 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 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 and retains the other existing production arguments
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
# Tasks
|
||||||
|
|
||||||
|
## 1. Configure and qualify minimum buybacks
|
||||||
|
|
||||||
|
- [x] 1.1 Add immutable canonical-UUID minima and constructor validation; verify reordered/copied configuration, invalid values and minimum <= maximum with a temporary offline harness.
|
||||||
|
- [x] 1.2 Apply minima to actual rounded candidates and update domain/constructor JavaDoc; verify issue examples, precision/scale boundaries, later-input continuation and unchanged reserve/max bounds offline.
|
||||||
|
- [x] 1.3 Add exact production minima and update Evelyn configuration documentation; review USDT priority and all other unchanged production arguments.
|
||||||
|
|
||||||
|
## 2. Verify and finalize integration
|
||||||
|
|
||||||
|
- [x] 2.1 Compile existing main/test sources and run focused temporary selection/config checks plus the adapted #91 recovery harness; record results and review unchanged execution, Ticker, burn and recovery code.
|
||||||
|
- [x] 2.2 Strictly validate the change, sync and validate the main spec, check whitespace and archive the completed change after confirming all requirements/tasks are satisfied.
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
# Verification
|
||||||
|
|
||||||
|
Verified on 2026-10-06 with JDK `/usr/local/software/java/jfx-25`.
|
||||||
|
|
||||||
|
## Compilation and controlled checks
|
||||||
|
|
||||||
|
- Parent compilation: `JAVA_HOME=/usr/local/software/java/jfx-25 ./gradlew compileJava compileTestJava --offline --no-daemon` → **BUILD SUCCESSFUL in 6s**; `detagMain`, `compileJava` and `compileTestJava` all executed against the final source.
|
||||||
|
- Temporary harnesses compiled with `javac --enable-preview --release 25 -cp 'build/classes/java/main:build/libs/*' -d /tmp/evelyn-issue92 /tmp/evelyn-issue92/Issue92Verification.java /tmp/evelyn-issue92/Issue91Verification.java`.
|
||||||
|
- `java --enable-preview -cp '/tmp/evelyn-issue92:build/classes/java/main:build/libs/*' Issue92Verification` → **PASS: 89 focused minimum-amount assertions**.
|
||||||
|
- The same invocation for `Issue91Verification` → **PASS: 102 recovery assertions**. Its original temporary file is preserved in `/tmp/evelyn-issue91`; the copy changes only the burner constructor call to add SyrupUSDC minimum 0.045.
|
||||||
|
- All wallet, swap, HTTP and pricing dependencies are controlled. No live financial operation or real RPC request occurs.
|
||||||
|
|
||||||
|
The #92 harness covers every issue example, equality across decimal scales, minimum == maximum, fractional minima above rounded candidates, downward rounding of maximum, zero after precision rounding, next-input continuation, canonical UUID matching with reordered/stale display metadata, caller mutations of all three copied lists, invalid minima, preserved maximum validation, direct EVE when inputs are below minima or all three lists are empty, stale EVE price and at most one attempted qualifying swap even after failure.
|
||||||
|
|
||||||
|
An initial temporary null-UUID fixture attempted to construct an invalid CurrencyType, whose existing constructor already rejects null IDs before burner configuration. The check was moved to that proper pre-burner rejection point, and the #92 harness rerun passed. No production fix was required.
|
||||||
|
|
||||||
|
## Review and scope
|
||||||
|
|
||||||
|
Parent independent source/documentation review found no material findings. Static diff review confirms the changes are confined to the reference constructor/amount configuration validator, rounded-candidate qualification, domain JavaDoc, production minima and Evelyn configuration docs. Reserves/maxima, USDT priority, pending recovery, Jupiter, Ticker, token discovery, burn/notification and lifecycle are preserved. No ValueTag, deployment file or permanent automated test changed.
|
||||||
|
|
||||||
|
Strict validation of the active change and synced main specification passed. All four delta requirements match the main spec exactly; unrelated requirements/scenarios remain intact. Existing long-requirement informational hints remain under the rule that modified requirement blocks stay whole. `git diff --check` passed. All five tasks and four planning artifacts are complete.
|
||||||
|
|
||||||
|
After archival, `openspec list --json` reports no active changes. `openspec validate --archived --json --report findings` passed for all 30 archived changes, with 0 failures and 0 findings.
|
||||||
|
|
||||||
|
No live integration, deployment, push or Gitea write was performed. Temporary harnesses remain outside the repository; no permanent automated tests were added.
|
||||||
@@ -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.
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user