98: Populate EvelynPerpsService with real Jupiter positions

This commit is contained in:
2026-10-22 13:37:29 +02:00
parent 0ec22d5215
commit df5cc0eda7
15 changed files with 334 additions and 113 deletions
@@ -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.
@@ -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
@@ -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.
+16 -19
View File
@@ -2,7 +2,7 @@
## Purpose
Let the owner inspect Evelyn perpetual positions grouped by wallet and venue in Mission Control, beginning with a clearly labeled sample-data prototype.
Let the owner inspect Evelyn perpetual positions grouped by wallet and venue in Mission Control, with stable row identities across service snapshot updates.
## Requirements
@@ -18,24 +18,6 @@ The Perps tab SHALL display a tree table with columns `Position`, `Direction`, a
- **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 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
### 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.
@@ -59,3 +41,18 @@ The Perps table SHALL retain matching wallet, venue and position identities acro
#### 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
### 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
+33 -17
View File
@@ -6,32 +6,48 @@ Provide a view-only source of current Evelyn perpetual positions independently o
## 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.
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 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
- **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.
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 makes the complete sample snapshot available without starting a polling or integration worker
- **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 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.
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 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
- **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
### 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