Although these message types implement the empty NotificationMessage marker interface, Java generic invariance means that consumers still need to know the concrete transport-specific message type.
This prevents a consumer from receiving a fully configured notification service without knowing whether it sends through Discord, Pushover, SMTP, or another future transport.
The intended architecture is that a composition root creates and configures a notification service and then injects only a fully bound, transport-independent service into the consumer.
For example, the future EvelynBurnerService must only know that it can push a notification message. It must not know whether the message is delivered through Discord, Pushover, SMTP, Telegram, or another implementation.
Before implementing the Discord integration, refactor and repair the existing notification abstraction.
Objective
Replace the current notifier/message design with a service-oriented API where:
All transport-specific and destination-specific configuration is bound before the service is injected into a consumer.
A consumer only supplies a semantically tagged text message.
A consumer does not import transport-specific message classes.
The package and class names consistently describe notification services rather than notifiers.
SMTP can bind both a destination email address and a fixed subject before being passed to a consumer.
The address and subject must not be supplied again on every push(...).
Reject null or blank email addresses and subjects when the SMTP destination or binding is created.
Do not retain EmailMessage, EmailBody, or the current EmailSubject record merely to transport these values.
The actual SMTP delivery implementation is not part of this issue. Preserve its current unimplemented status while adapting its public API to the repaired framework.
ValueTag prerequisites
The DeTag setup is expected to provide the following ValueTags, all resolving to String:
It must not need to import anything from an impl package.
Do not use the existing alarm-specific Pushover implementation as an architectural model. The alarm integration was implemented separately as a time-critical solution and is outside the scope of this refactor.
Out of scope
This issue must not:
Implement Discord webhook delivery.
Implement the future EvelynBurnerService.
Change Solana, wallet, Jupiter, or token-burning code.
Add notification configuration loading to Nenjim.
Change NenjimHubImpl.startAutoRunProcesses().
Refactor the existing alarm actions.
Implement SMTP delivery.
Add asynchronous notification delivery.
Add retry, queue, outbox, persistence, or timeout policies.
Introduce transport-specific message types into the public service API.
Add or modify unit tests.
Unit-test restriction
Do not add any unit tests for this issue.
Do not modify existing unit tests as part of this issue.
Verification must be limited to compilation, assembly, existing unaffected verification mechanisms, and relevant manual or structural checks.
Acceptance criteria
The notification framework resides under com.r35157.service.notification.
No notification classes or compatibility aliases remain under com.r35157.libs.notification.
BoundNotifier and AddressedNotifier no longer exist.
BoundNotificationService accepts ΩNotificationMessageΩ directly and has no message generic.
AddressedNotificationService is generic only over the destination type.
NotificationMessage, PushMessage, DiscordMessage, EmailMessage, EmailBody, and the EmailSubject record have been removed.
PushOverNotifier has become PushoverNotificationService.
DiscordNotifier has become DiscordNotificationService.
SMTPNotifier has become SMTPNotificationService.
Pushover retains its existing functional behavior.
Discord and SMTP retain their current unimplemented delivery status.
SMTP can bind an ΩEmailAddressΩ and an ΩEmailSubjectΩ into a BoundNotificationService.
A bound service requires only an ΩNotificationMessageΩ when push(...) is called.
Null and blank notification messages are rejected before delivery is attempted.
Consumers do not need transport-specific message classes.
All affected production code compiles successfully.
No unit tests have been added or modified.
## Background
The existing notification framework under:
```text
com.r35157.libs.notification
```
does not provide the intended transport-independent abstraction.
The current API uses transport-specific message types:
```java
BoundNotifier<PushMessage>
BoundNotifier<DiscordMessage>
BoundNotifier<EmailMessage>
```
Although these message types implement the empty `NotificationMessage` marker interface, Java generic invariance means that consumers still need to know the concrete transport-specific message type.
This prevents a consumer from receiving a fully configured notification service without knowing whether it sends through Discord, Pushover, SMTP, or another future transport.
The intended architecture is that a composition root creates and configures a notification service and then injects only a fully bound, transport-independent service into the consumer.
For example, the future `EvelynBurnerService` must only know that it can push a notification message. It must not know whether the message is delivered through Discord, Pushover, SMTP, Telegram, or another implementation.
Before implementing the Discord integration, refactor and repair the existing notification abstraction.
## Objective
Replace the current notifier/message design with a service-oriented API where:
* All transport-specific and destination-specific configuration is bound before the service is injected into a consumer.
* A consumer only supplies a semantically tagged text message.
* A consumer does not import transport-specific message classes.
* The package and class names consistently describe notification services rather than notifiers.
* SMTP can bind both a destination email address and a fixed subject before being passed to a consumer.
## Package relocation
Move the complete notification package from:
```text
com.r35157.libs.notification
```
to:
```text
com.r35157.service.notification
```
Move the implementation subpackages accordingly:
```text
com.r35157.service.notification.impl.discord
com.r35157.service.notification.impl.pushover
com.r35157.service.notification.impl.smtp
```
Use the singular package segment `service`, exactly as shown above.
Update all affected imports and usages.
No classes or compatibility aliases must remain under the old `com.r35157.libs.notification` package.
## Rename the public interfaces
Rename:
```text
BoundNotifier
-> BoundNotificationService
AddressedNotifier
-> AddressedNotificationService
```
Move `NotificationDestination` to the new package without changing its fundamental purpose.
## Remove transport-specific message generics
`BoundNotificationService` must not be generic over a message type.
Its public contract must be equivalent to:
```java
public interface BoundNotificationService {
void push(ΩNotificationMessageΩ message) throws IOException;
}
```
`ΩNotificationMessageΩ` resolves to `String`.
`AddressedNotificationService` may remain generic only over its destination type:
```java
public interface AddressedNotificationService<
D extends NotificationDestination>
{
void push(
D destination,
ΩNotificationMessageΩ message
) throws IOException;
default BoundNotificationService bind(D destination) {
return message -> push(destination, message);
}
}
```
The precise formatting may follow existing project conventions, but the resulting type semantics must be as described above.
## Remove the obsolete message abstraction
Remove the empty marker interface:
```text
NotificationMessage
```
Remove the transport-specific message and wrapper classes that become unnecessary:
```text
DiscordMessage
PushMessage
EmailMessage
EmailBody
EmailSubject
```
Do not replace these classes with a new `TextMessage`, record, marker interface, or general message wrapper.
The semantic message type must be expressed directly by `ΩNotificationMessageΩ`.
## Message validation
Every public `push(...)` entry point must reject a notification message that is:
* `null`
* empty
* blank
Such invalid input must fail before any network or transport operation is attempted.
Use `IllegalArgumentException` consistently for invalid notification messages.
The validation belongs in the notification service push logic. Do not introduce a separate runtime message object only to perform this validation.
## Rename the implementations
Rename the existing implementation classes:
```text
PushOverNotifier
-> PushoverNotificationService
DiscordNotifier
-> DiscordNotificationService
SMTPNotifier
-> SMTPNotificationService
```
Use `Pushover`, not `PushOver`, in class names, filenames, package references, variables, and updated examples.
The resulting implementations must use the new service interfaces:
```java
PushoverNotificationService
implements BoundNotificationService
DiscordNotificationService
implements BoundNotificationService
SMTPNotificationService
implements AddressedNotificationService<SMTPDestination>
```
## Pushover behavior
Adapt the existing functional Pushover implementation to accept `ΩNotificationMessageΩ` directly.
Preserve its existing delivery behavior and synchronous `IOException`-based error contract.
This issue is an API and architecture refactor. Do not add unrelated Pushover behavior, retries, asynchronous execution, or configuration changes.
## SMTP binding semantics
An SMTP notification must be fully bound to both:
* its destination email address
* its fixed email subject
before it is injected into a consumer.
`SMTPDestination` must therefore represent the complete destination-specific SMTP binding required to send a plain notification message. It may contain:
```java
ΩEmailAddressΩ emailAddress
ΩEmailSubjectΩ subject
```
`SMTPNotificationService` must provide a convenient way to create a bound service directly from these two values, equivalent to:
```java
BoundNotificationService bind(
ΩEmailAddressΩ emailAddress,
ΩEmailSubjectΩ subject
);
```
The resulting bound service must require only the notification message when used:
```java
BoundNotificationService notificationService =
smtpNotificationService.bind(emailAddress, subject);
notificationService.push(message);
```
The address and subject must not be supplied again on every `push(...)`.
Reject null or blank email addresses and subjects when the SMTP destination or binding is created.
Do not retain `EmailMessage`, `EmailBody`, or the current `EmailSubject` record merely to transport these values.
The actual SMTP delivery implementation is not part of this issue. Preserve its current unimplemented status while adapting its public API to the repaired framework.
## ValueTag prerequisites
The DeTag setup is expected to provide the following ValueTags, all resolving to `String`:
```text
ΩNotificationMessageΩ
ΩEmailAddressΩ
ΩEmailSubjectΩ
```
In particular, `ΩEmailAddressΩ` and `ΩEmailSubjectΩ` must be treated as existing prerequisites when this issue is implemented.
Do not introduce fallback Java wrapper types for these values.
Do not add, redefine, or modify the externally managed ValueTag definitions as part of this issue.
## Discord scope
Rename and adapt the existing Discord stub so that it implements:
```java
BoundNotificationService
```
and accepts `ΩNotificationMessageΩ`.
Do not implement Discord webhook delivery in this issue. That will be handled by a separate follow-up issue after this refactor has been completed.
## Existing consumers and examples
Update all production-code imports, inline examples, and compile-time usages affected by the package, class, and API changes.
After this refactor, a transport-independent consumer must be able to depend solely on:
```java
com.r35157.service.notification.BoundNotificationService
```
and call:
```java
notificationService.push(message);
```
It must not need to import anything from an `impl` package.
Do not use the existing alarm-specific Pushover implementation as an architectural model. The alarm integration was implemented separately as a time-critical solution and is outside the scope of this refactor.
## Out of scope
This issue must not:
* Implement Discord webhook delivery.
* Implement the future `EvelynBurnerService`.
* Change Solana, wallet, Jupiter, or token-burning code.
* Add notification configuration loading to Nenjim.
* Change `NenjimHubImpl.startAutoRunProcesses()`.
* Refactor the existing alarm actions.
* Implement SMTP delivery.
* Add asynchronous notification delivery.
* Add retry, queue, outbox, persistence, or timeout policies.
* Introduce transport-specific message types into the public service API.
* Add or modify unit tests.
## Unit-test restriction
Do not add any unit tests for this issue.
Do not modify existing unit tests as part of this issue.
Verification must be limited to compilation, assembly, existing unaffected verification mechanisms, and relevant manual or structural checks.
## Acceptance criteria
* The notification framework resides under `com.r35157.service.notification`.
* No notification classes or compatibility aliases remain under `com.r35157.libs.notification`.
* `BoundNotifier` and `AddressedNotifier` no longer exist.
* `BoundNotificationService` accepts `ΩNotificationMessageΩ` directly and has no message generic.
* `AddressedNotificationService` is generic only over the destination type.
* `NotificationMessage`, `PushMessage`, `DiscordMessage`, `EmailMessage`, `EmailBody`, and the `EmailSubject` record have been removed.
* `PushOverNotifier` has become `PushoverNotificationService`.
* `DiscordNotifier` has become `DiscordNotificationService`.
* `SMTPNotifier` has become `SMTPNotificationService`.
* Pushover retains its existing functional behavior.
* Discord and SMTP retain their current unimplemented delivery status.
* SMTP can bind an `ΩEmailAddressΩ` and an `ΩEmailSubjectΩ` into a `BoundNotificationService`.
* A bound service requires only an `ΩNotificationMessageΩ` when `push(...)` is called.
* Null and blank notification messages are rejected before delivery is attempted.
* Consumers do not need transport-specific message classes.
* All affected production code compiles successfully.
* No unit tests have been added or modified.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Background
The existing notification framework under:
does not provide the intended transport-independent abstraction.
The current API uses transport-specific message types:
Although these message types implement the empty
NotificationMessagemarker interface, Java generic invariance means that consumers still need to know the concrete transport-specific message type.This prevents a consumer from receiving a fully configured notification service without knowing whether it sends through Discord, Pushover, SMTP, or another future transport.
The intended architecture is that a composition root creates and configures a notification service and then injects only a fully bound, transport-independent service into the consumer.
For example, the future
EvelynBurnerServicemust only know that it can push a notification message. It must not know whether the message is delivered through Discord, Pushover, SMTP, Telegram, or another implementation.Before implementing the Discord integration, refactor and repair the existing notification abstraction.
Objective
Replace the current notifier/message design with a service-oriented API where:
Package relocation
Move the complete notification package from:
to:
Move the implementation subpackages accordingly:
Use the singular package segment
service, exactly as shown above.Update all affected imports and usages.
No classes or compatibility aliases must remain under the old
com.r35157.libs.notificationpackage.Rename the public interfaces
Rename:
Move
NotificationDestinationto the new package without changing its fundamental purpose.Remove transport-specific message generics
BoundNotificationServicemust not be generic over a message type.Its public contract must be equivalent to:
ΩNotificationMessageΩresolves toString.AddressedNotificationServicemay remain generic only over its destination type:The precise formatting may follow existing project conventions, but the resulting type semantics must be as described above.
Remove the obsolete message abstraction
Remove the empty marker interface:
Remove the transport-specific message and wrapper classes that become unnecessary:
Do not replace these classes with a new
TextMessage, record, marker interface, or general message wrapper.The semantic message type must be expressed directly by
ΩNotificationMessageΩ.Message validation
Every public
push(...)entry point must reject a notification message that is:nullSuch invalid input must fail before any network or transport operation is attempted.
Use
IllegalArgumentExceptionconsistently for invalid notification messages.The validation belongs in the notification service push logic. Do not introduce a separate runtime message object only to perform this validation.
Rename the implementations
Rename the existing implementation classes:
Use
Pushover, notPushOver, in class names, filenames, package references, variables, and updated examples.The resulting implementations must use the new service interfaces:
Pushover behavior
Adapt the existing functional Pushover implementation to accept
ΩNotificationMessageΩdirectly.Preserve its existing delivery behavior and synchronous
IOException-based error contract.This issue is an API and architecture refactor. Do not add unrelated Pushover behavior, retries, asynchronous execution, or configuration changes.
SMTP binding semantics
An SMTP notification must be fully bound to both:
before it is injected into a consumer.
SMTPDestinationmust therefore represent the complete destination-specific SMTP binding required to send a plain notification message. It may contain:SMTPNotificationServicemust provide a convenient way to create a bound service directly from these two values, equivalent to:The resulting bound service must require only the notification message when used:
The address and subject must not be supplied again on every
push(...).Reject null or blank email addresses and subjects when the SMTP destination or binding is created.
Do not retain
EmailMessage,EmailBody, or the currentEmailSubjectrecord merely to transport these values.The actual SMTP delivery implementation is not part of this issue. Preserve its current unimplemented status while adapting its public API to the repaired framework.
ValueTag prerequisites
The DeTag setup is expected to provide the following ValueTags, all resolving to
String:In particular,
ΩEmailAddressΩandΩEmailSubjectΩmust be treated as existing prerequisites when this issue is implemented.Do not introduce fallback Java wrapper types for these values.
Do not add, redefine, or modify the externally managed ValueTag definitions as part of this issue.
Discord scope
Rename and adapt the existing Discord stub so that it implements:
and accepts
ΩNotificationMessageΩ.Do not implement Discord webhook delivery in this issue. That will be handled by a separate follow-up issue after this refactor has been completed.
Existing consumers and examples
Update all production-code imports, inline examples, and compile-time usages affected by the package, class, and API changes.
After this refactor, a transport-independent consumer must be able to depend solely on:
and call:
It must not need to import anything from an
implpackage.Do not use the existing alarm-specific Pushover implementation as an architectural model. The alarm integration was implemented separately as a time-critical solution and is outside the scope of this refactor.
Out of scope
This issue must not:
EvelynBurnerService.NenjimHubImpl.startAutoRunProcesses().Unit-test restriction
Do not add any unit tests for this issue.
Do not modify existing unit tests as part of this issue.
Verification must be limited to compilation, assembly, existing unaffected verification mechanisms, and relevant manual or structural checks.
Acceptance criteria
com.r35157.service.notification.com.r35157.libs.notification.BoundNotifierandAddressedNotifierno longer exist.BoundNotificationServiceacceptsΩNotificationMessageΩdirectly and has no message generic.AddressedNotificationServiceis generic only over the destination type.NotificationMessage,PushMessage,DiscordMessage,EmailMessage,EmailBody, and theEmailSubjectrecord have been removed.PushOverNotifierhas becomePushoverNotificationService.DiscordNotifierhas becomeDiscordNotificationService.SMTPNotifierhas becomeSMTPNotificationService.ΩEmailAddressΩand anΩEmailSubjectΩinto aBoundNotificationService.ΩNotificationMessageΩwhenpush(...)is called.