83: Remove lifecycle operations from the EvelynIOUBurnerService API
This commit is contained in:
+2
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-09-04
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
## Context
|
||||
|
||||
The Burner implementation is already both `EvelynIOUBurnerService` and `NenjimProcess`, but the service contract contains no domain operation: it exposes only duplicated startup and an implementation-specific stop lifecycle. The canonical `NenjimProcess` contract supports only Hub-owned `start()` and deliberately provides no common stop or restart.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
|
||||
- Preserve `EvelynIOUBurnerService` as an empty typed component contract for Registry lookup and dependency injection.
|
||||
- Keep startup exclusively on the implementation's `NenjimProcess` view.
|
||||
- Remove public normal stop and the internal fields/helpers used only by that operation.
|
||||
- Preserve immediate fixed-delay worker startup and cleanup if worker scheduling fails.
|
||||
- Describe loss of process-local uncertainty state only across a complete process restart.
|
||||
|
||||
**Non-Goals:**
|
||||
|
||||
- No stop or restart addition to `NenjimProcess`.
|
||||
- No Hub shutdown or worker-control API.
|
||||
- No new external Burner domain operation.
|
||||
- No transaction, polling, notification, dependency, or test behavior change.
|
||||
|
||||
## Decisions
|
||||
|
||||
### Keep an empty typed service view
|
||||
|
||||
The empty interface remains a `NenjimComponent`. This preserves the domain-specific type, Registry visibility, and future extension point without leaking Hub-owned lifecycle to consumers or exposing the concrete implementation.
|
||||
|
||||
### Retain only process startup
|
||||
|
||||
`EvelynIOUBurnerServiceImpl.start()` remains unchanged in responsibility and implements `NenjimProcess.start()`. The Hub already constructs and registers the same object, resolves it through `NenjimProcess`, and starts it through that view.
|
||||
|
||||
### Remove general stop state without weakening failed-start cleanup
|
||||
|
||||
Delete public `stop()`, its `stopping` flag, termination wait timeout, and interruption-aware stop helper. The `start()` scheduling failure path continues to call `shutdownNow()`, clear the worker reference, reset process-local state, and throw before startup succeeds. A successful process has no public stop/restart path until Nenjim defines one centrally.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- [A successful worker cannot be stopped through this implementation] → This is intentional; lifecycle expansion belongs in the common Hub/process architecture.
|
||||
- [An empty service interface has no callable API] → It still supplies meaningful typed identity and prevents consumers from depending on implementation classes.
|
||||
- [Operational recovery now requires process restart] → Document that restart forgets pending uncertainty state and requires external reconciliation first.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
1. Recompile consumers against the empty service interface.
|
||||
2. Remove direct Burner start/stop calls and select startup through the existing Hub process configuration.
|
||||
3. Restart the complete process only after reconciling uncertain on-chain activity; no data or configuration migration is required.
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
## Why
|
||||
|
||||
`EvelynIOUBurnerService` currently contains only `start()` and `stop()`, so ordinary service consumers receive process control instead of a domain API. This conflicts with the current Nenjim architecture, where the Hub owns lifecycle through an implementation's separate `NenjimProcess` view.
|
||||
|
||||
## What Changes
|
||||
|
||||
- **BREAKING**: Make `EvelynIOUBurnerService` an empty lifecycle-free typed service contract until external domain operations exist.
|
||||
- Keep `EvelynIOUBurnerServiceImpl.start()` solely as its `NenjimProcess.start()` implementation.
|
||||
- Remove the implementation's public general stop operation and its now-unused stopping state and termination timeout.
|
||||
- Preserve startup-failure cleanup, worker scheduling, transaction safety, and Hub startup wiring.
|
||||
- Replace explicit stop/start reset semantics with process-restart semantics in JavaDoc, OpenSpec, and Nenjim documentation.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `evelyn-iou-burner-service`: Separates the empty typed service view from Hub-owned process startup and removes unsupported public stop/restart control.
|
||||
|
||||
## Impact
|
||||
|
||||
- Changes the public `com.fanitas.evelyn.service.burner.EvelynIOUBurnerService` source contract and public methods of its reference implementation.
|
||||
- Existing direct lifecycle callers must use Hub process configuration for startup and cannot request shutdown until common process stop semantics exist.
|
||||
- Adds no Hub shutdown, `NenjimProcess.stop()`, replacement domain operation, dependency, or automated test.
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Evelyn-specific public service boundary
|
||||
The system SHALL expose an empty lifecycle-free `EvelynIOUBurnerService` under `com.fanitas.evelyn.service.burner` as the typed domain service and Registry view until external domain operations exist, and SHALL keep its reference implementation under `com.fanitas.evelyn.service.burner.impl.ref`. The same reference implementation object SHALL implement both `EvelynIOUBurnerService` and `NenjimProcess`; the Hub SHALL start it only through its `NenjimProcess` view. Neither the service interface nor the implementation SHALL expose a general stop, restart, worker-control, or other lifecycle operation. The service SHALL remain specific to Evelyn IOU and SHALL NOT introduce a generic token-burner abstraction, burner-specific input interfaces, records, or class hierarchies.
|
||||
|
||||
#### Scenario: Downstream lifecycle consumer
|
||||
- **WHEN** a downstream component depends on the Evelyn burner API
|
||||
- **THEN** it receives a typed component contract with no callable lifecycle or domain operation and does not depend on reference-implementation types or a generic burner model
|
||||
|
||||
#### Scenario: Input model remains domain-level
|
||||
- **WHEN** the Hub configures accepted buyback currencies while constructing the process implementation
|
||||
- **THEN** it uses existing `MoneyAmount` values rather than burner-specific Solana, SPL-token, or input-asset types
|
||||
|
||||
### 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 burn information and 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 structured outcome SHALL suspend all later swaps in that process. Because the Jupiter `IOException` and interruption contracts can represent an unknown post-submission result, either SHALL conservatively suspend later swaps. The service SHALL never automatically rebuild, re-sign, retry, or resubmit such a swap; while swaps are suspended, later iterations SHALL continue only their burn path. Recovery SHALL require external reconciliation followed by a complete process restart, not a Burner service stop/start operation.
|
||||
|
||||
#### 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 and all later swaps remain suspended until external reconciliation and a complete process restart
|
||||
|
||||
#### 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: Single complete-balance burn submission and pending state
|
||||
For a positive EVE balance with a usable price, the service SHALL call the wallet burn operation exactly once with the fixed EVE mint and complete observed balance. 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, submitted EVE amount, 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 complete observed EVE balance 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
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
## 1. Burner API and implementation
|
||||
|
||||
- [x] 1.1 Remove `start()` and `stop()` from `EvelynIOUBurnerService` while retaining its typed `NenjimComponent` identity.
|
||||
- [x] 1.2 Keep implementation startup through `NenjimProcess` and remove public stop plus stop-only state and helpers.
|
||||
- [x] 1.3 Preserve worker-scheduling failure cleanup and unchanged Hub startup wiring.
|
||||
|
||||
## 2. Specification and documentation
|
||||
|
||||
- [x] 2.1 Update the Burner capability for a lifecycle-free service view, Hub-owned startup, and process-restart-only reset semantics.
|
||||
- [x] 2.2 Update relevant JavaDoc and Nenjim architecture, public, and terminology documentation.
|
||||
|
||||
## 3. Verification
|
||||
|
||||
- [x] 3.1 Run strict OpenSpec validation and archive the completed change into the canonical specification.
|
||||
- [x] 3.2 Run the complete Gradle build and inspect the compiled public API and final diff.
|
||||
@@ -3,19 +3,17 @@
|
||||
## Purpose
|
||||
|
||||
Defines the Evelyn-specific service that safely converts prioritized wallet assets into Evelyn IOU, burns the complete visible EVE balance, and announces only finalized burns without automatically repeating transactions whose outcomes may be unknown.
|
||||
|
||||
## Requirements
|
||||
|
||||
### Requirement: Evelyn-specific public service boundary
|
||||
The system SHALL expose `EvelynIOUBurnerService` under `com.fanitas.evelyn.service.burner` with only `start()` and `stop()` operations, and SHALL keep its reference implementation under `com.fanitas.evelyn.service.burner.impl.ref`. The service SHALL remain specific to Evelyn IOU and SHALL NOT introduce a generic token-burner abstraction, burner-specific input interfaces, records, or class hierarchies.
|
||||
The system SHALL expose an empty lifecycle-free `EvelynIOUBurnerService` under `com.fanitas.evelyn.service.burner` as the typed domain service and Registry view until external domain operations exist, and SHALL keep its reference implementation under `com.fanitas.evelyn.service.burner.impl.ref`. The same reference implementation object SHALL implement both `EvelynIOUBurnerService` and `NenjimProcess`; the Hub SHALL start it only through its `NenjimProcess` view. Neither the service interface nor the implementation SHALL expose a general stop, restart, worker-control, or other lifecycle operation. The service SHALL remain specific to Evelyn IOU and SHALL NOT introduce a generic token-burner abstraction, burner-specific input interfaces, records, or class hierarchies.
|
||||
|
||||
#### Scenario: Downstream lifecycle consumer
|
||||
- **WHEN** a downstream component depends on the Evelyn burner API
|
||||
- **THEN** it can start and stop the service without depending on reference-implementation types or a generic burner model
|
||||
- **THEN** it receives a typed component contract with no callable lifecycle or domain operation and does not depend on reference-implementation types or a generic burner model
|
||||
|
||||
#### Scenario: Input model remains domain-level
|
||||
- **WHEN** callers configure accepted buyback currencies
|
||||
- **THEN** they use existing `MoneyAmount` values rather than burner-specific Solana, SPL-token, or input-asset types
|
||||
- **WHEN** the Hub configures accepted buyback currencies while constructing the process implementation
|
||||
- **THEN** it uses existing `MoneyAmount` values rather than burner-specific Solana, SPL-token, or input-asset types
|
||||
|
||||
### 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 pricing and processing. An empty list SHALL be valid and SHALL still permit directly deposited EVE to be burned.
|
||||
@@ -65,21 +63,21 @@ The service SHALL fix the EVE mint as `meveYG2iXYSkgSUn1T1uxcthH1EGMZdRHGgCntXZA
|
||||
- **THEN** it uses the fixed EVE mint and the canonical EVE/USDT pair without accepting replacements through construction
|
||||
|
||||
### Requirement: Immediate sequential fixed-delay lifecycle
|
||||
Each service instance SHALL own at most one dedicated background worker. A successful `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. `stop()` SHALL prevent new iterations, interrupt and stop the worker cleanly, and preserve interruption if the stopping thread is interrupted. A repeated start while active SHALL NOT create another worker.
|
||||
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 burn information and swap/burn suspension flags SHALL be process-local only. A completed explicit stop followed by start SHALL clear those states. The public JavaDoc SHALL disclose that this reset, or a process restart, loses reconciliation state and can weaken duplicate-prevention until unknown transactions have been reconciled externally.
|
||||
Pending burn information and 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 service starts and one iteration takes longer than usual
|
||||
- **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** `start()` is called while the instance already owns an active worker
|
||||
- **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 service with pending or suspended process-local state is cleanly stopped and then explicitly started
|
||||
- **THEN** the new lifecycle begins without that state and reloads wallet and chain state normally
|
||||
- **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: Pending burn is handled before all new work
|
||||
At the start of an iteration, if a prior burn signature is pending, the service SHALL only await that exact signature at `FINALIZED`. It SHALL submit no swap or burn in that iteration. An unknown timeout or I/O outcome SHALL retain the pending state and end the iteration; definitive success SHALL make the one associated notification attempt and end the iteration; and definitive on-chain failure SHALL log the slot and complete failure details, clear pending state, send no notification, and defer any new wallet decision to a later iteration.
|
||||
@@ -144,7 +142,7 @@ The SOL reserve comparison SHALL use the native balance without estimating or su
|
||||
### 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-lifecycle swaps. A timed-out structured outcome SHALL suspend all later swaps in that lifecycle. Because the Jupiter `IOException` and interruption contracts can represent an unknown post-submission result, either SHALL conservatively suspend later swaps. The service SHALL never automatically rebuild, re-sign, retry, or resubmit such a swap; while swaps are suspended, later iterations SHALL continue only their burn path.
|
||||
A structured definitive on-chain swap failure SHALL be logged without suspending later-process swaps. A timed-out structured outcome SHALL suspend all later swaps in that process. Because the Jupiter `IOException` and interruption contracts can represent an unknown post-submission result, either SHALL conservatively suspend later swaps. The service SHALL never automatically rebuild, re-sign, retry, or resubmit such a swap; while swaps are suspended, later iterations SHALL continue only their burn path. Recovery SHALL require external reconciliation followed by a complete process restart, not a Burner service stop/start operation.
|
||||
|
||||
#### Scenario: First swap fails definitively
|
||||
- **WHEN** the first attempted input returns a definitive on-chain failure
|
||||
@@ -152,7 +150,7 @@ A structured definitive on-chain swap failure SHALL be logged without suspending
|
||||
|
||||
#### 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 and all later swaps remain suspended until explicit stop/start
|
||||
- **THEN** no equivalent or alternative swap is attempted and all later swaps remain suspended until external reconciliation and a complete process restart
|
||||
|
||||
#### Scenario: Swap returns normally
|
||||
- **WHEN** Jupiter returns a successful result whose transaction already reached `CONFIRMED`
|
||||
@@ -183,7 +181,7 @@ Immediately before a new burn, the service SHALL obtain the latest canonical EVE
|
||||
### Requirement: Single complete-balance burn submission and pending state
|
||||
For a positive EVE balance with a usable price, the service SHALL call the wallet burn operation exactly once with the fixed EVE mint and complete observed balance. 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, submitted EVE amount, 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 lifecycle. It MAY continue ordinary iteration work that does not submit another burn. Explicit stop/start SHALL reset that suspension.
|
||||
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 complete observed EVE balance and returns a non-blank signature
|
||||
@@ -191,7 +189,7 @@ If burn submission fails without a usable signature and its outcome may be unkno
|
||||
|
||||
#### 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 explicit stop/start
|
||||
- **THEN** the service performs no further burn submission until external reconciliation and a complete process restart
|
||||
|
||||
### Requirement: Finalized burn outcome governs completion
|
||||
Every known burn signature SHALL be awaited through the Solana blockchain at `FINALIZED` with exactly the configured finalization timeout. `SUCCEEDED` SHALL authorize notification; `FAILED` SHALL log the slot and complete on-chain failure details, clear pending state, and send no notification; `TIMED_OUT` and `IOException` SHALL keep the pending signature because its result remains unknown; and `InterruptedException` SHALL preserve interruption and pending state. None of these outcomes SHALL cause automatic transaction resubmission.
|
||||
|
||||
Reference in New Issue
Block a user