and accepts an ΩNotificationMessageΩ, but valid messages currently result in UnsupportedOperationException.
The notification architecture intentionally separates consumers from transports. A future EvelynBurnerService, for example, may receive a configured BoundNotificationService, but it must not know whether notifications are delivered through Discord, Pushover, SMTP, Telegram, or another transport.
Discord-specific endpoints, HTTP configuration, JSON payloads and response handling must therefore remain encapsulated inside DiscordNotificationService.
Objective
Implement synchronous, text-only Discord incoming-webhook delivery in DiscordNotificationService.
After construction, the service must be fully configured and usable solely through:
The precise formatting may follow existing project conventions, but these type semantics must be preserved.
ΩRestEndpointΩ resolves to URI and is already present in the authoritative DeTag configuration. It is the appropriate existing ValueTag for this endpoint.
The implementation must:
Store the fully bound Discord webhook endpoint.
Use the supplied timeout as both the HTTP connection timeout and the individual request timeout.
Build its internal HttpClient during construction.
Disable HTTP redirect following.
Remove the existing constructor accepting URL.
Not add a compatibility constructor accepting URL or raw String.
Not add or modify any ValueTag definitions.
Because ΩRestEndpointΩ resolves to URI, the .tjava source must retain the required java.net.URI import even if the backing type name is only introduced by DeTag generation.
Constructor validation
All constructor arguments must be validated before the service instance is usable and before any network activity can occur.
Reject the webhook endpoint with IllegalArgumentException if it is null or is not a structurally valid canonical Discord incoming-webhook endpoint.
Validation must require:
An absolute URI.
The https scheme.
The canonical discord.com host, compared case-insensitively.
No user-info component.
No fragment.
No pre-existing query string, because the service owns the execution query parameters.
No non-HTTPS port.
A Discord execute-webhook path containing non-blank webhook ID and token segments.
Either the canonical unversioned webhook path or a valid versioned Discord API webhook path.
The validation must be structural only. Construction must not perform a network request to test whether the webhook currently exists.
Reject the timeout with IllegalArgumentException if it is:
null
zero
negative
Validation exceptions, logs and diagnostic output must never contain the complete webhook endpoint or its token.
Notification-message validation
push() must continue to reject an ΩNotificationMessageΩ that is:
null
empty
blank
These cases must throw IllegalArgumentException.
Additionally, reject messages that exceed Discord’s 2,000-character content limit with IllegalArgumentException.
All message validation must occur before:
JSON serialization
HttpRequest construction
any HTTP or transport operation
A valid message must not be trimmed or otherwise rewritten. Its content must be preserved exactly when placed in the Discord JSON payload.
Discord HTTP request
For every valid push() call, DiscordNotificationService must synchronously execute one Discord webhook request.
The request must:
Use HTTP POST.
Use the configured Discord webhook endpoint.
Add the query parameter:
wait=true
Set Content-Type to application/json with UTF-8 encoding.
Set Accept to application/json.
Provide a valid Discord HTTP API User-Agent identifying the Nenjim client.
Apply the configured request timeout.
Send a JSON object containing the message in Discord’s content field.
The payload must be semantically equivalent to:
{"content":"the notification message"}
Use an existing JSON library already present in the project, preferably Jackson, to produce the payload. Do not construct JSON by manually concatenating or escaping strings.
The webhook token embedded in the endpoint is the authentication mechanism. Do not add a bot Authorization header.
The call must use synchronous HttpClient.send(...). Do not use sendAsync(...), background executors or fire-and-forget delivery.
Using wait=true is required so Discord confirms that the message was saved instead of merely acknowledging the request without reporting a later save failure.
HTTP response handling
Any HTTP status in the 2xx range must be treated as successful delivery.
Any other status must cause push() to throw IOException, including:
redirects
client errors
invalid or deleted webhooks
invalid payload responses
429 Too Many Requests
server errors
The IOException should include the HTTP status code and, when present and useful, Discord’s response body. It must not include the webhook URI or webhook token.
This issue must not implement automatic retry or sleeping after a 429 response. Rate limiting is reported synchronously as an IOException.
After a successful response, push() returns normally.
Communication and interruption failures
Network, connection, timeout, request-body preparation and other delivery failures must be reported through the existing synchronous IOException contract.
If HttpClient.send(...) throws InterruptedException, the implementation must:
Restore the current thread’s interrupt status:
Thread.currentThread().interrupt();
Throw an IOException describing that Discord delivery was interrupted.
Preserve the original InterruptedException as the cause.
A valid Discord notification must no longer throw UnsupportedOperationException.
Secret handling
A Discord webhook endpoint contains a secret token and must be treated as a credential.
The implementation must not:
Log the complete webhook endpoint.
Include it in exception messages.
Print it during debugging or manual verification.
Add a real webhook endpoint to source code, examples, OpenSpec artifacts or repository configuration.
Follow redirects that could disclose the webhook token to another endpoint.
If live manual verification is performed, the webhook must be supplied through a non-versioned local mechanism and removed afterwards.
Existing inline example
Update the current private inline usage example in DiscordNotificationService as required by the new constructor signature, or remove it if it no longer provides useful compile-time documentation.
It must not contain a real Discord webhook, a credential-like placeholder, or a network-executable example.
This is not permission to add a test.
OpenSpec expectations
Create an active OpenSpec change for this issue using the change ID:
The delta specification must modify the existing notification-services capability. In particular, it must replace the current requirement that Discord delivery remains unimplemented with requirements and scenarios covering:
fully configured construction
webhook endpoint and timeout validation
notification-message validation
JSON webhook delivery
synchronous confirmation with wait=true
successful HTTP responses
non-successful HTTP responses
communication failures
interrupted delivery with restored interrupt status
webhook-secret protection
The OpenSpec design must record the constructor types, URI validation rules, timeout semantics, HTTP status policy and the decision not to implement retries or composition-root wiring.
Run strict OpenSpec validation before considering the implementation complete.
Keep the OpenSpec change active and unarchived after implementation. Do not sync or archive it unless explicitly requested separately.
Scope boundaries
This issue may change:
DiscordNotificationService
its inline documentation or compile-time example
the active OpenSpec change artifacts
unavoidable directly affected production references, if any exist
This issue must not:
Load a webhook from a configuration file.
Change NenjimHubImpl.startAutoRunProcesses().
Wire Discord into Nenjim, Evelyn or a Burner service.
Implement or modify EvelynBurnerService.
Change the notification API interfaces.
Change PushoverNotificationService.
Change SMTPNotificationService or SMTPDestination.
Change alarm-specific notification code.
Add or modify ValueTags or the DeTag configuration.
Add asynchronous delivery.
Add retries, rate-limit waiting, queues, persistence or an outbox.
Configuration loading and composition-root wiring must be handled in a separate follow-up issue after this service implementation has been reviewed.
Unit-test restriction
Do not add, generate or modify unit tests as part of this issue.
Existing tests may be compiled or executed unchanged as part of normal verification, but no test source files may be changed.
Do not introduce a mock HTTP server, test-only constructor or production seam solely for unit testing.
Verification
Verification must include:
Compilation of the generated main sources from the .tjava source.
Compilation of the existing unchanged test sources.
The repository’s normal assembly or build.
Strict validation of the active OpenSpec change.
Structural confirmation that the old URL constructor and UnsupportedOperationException delivery stub are gone.
Structural confirmation that no real webhook endpoint or token was added.
Review of the complete diff for changes outside the permitted scope.
Confirmation that no unit-test files were added or modified.
If a real webhook is explicitly available, one manual live text delivery may additionally be performed. Absence of a live webhook must not be worked around by committing credentials or weakening endpoint validation.
Acceptance criteria
DiscordNotificationService remains a BoundNotificationService.
Consumers still supply only ΩNotificationMessageΩ to push().
The constructor accepts an ΩRestEndpointΩ and a positive Duration.
The old raw URL constructor has been removed without a compatibility overload.
No new ValueTag is introduced.
Invalid endpoints and invalid timeouts fail during construction with IllegalArgumentException.
Null, empty, blank and over-2,000-character messages fail before JSON or HTTP work begins.
A valid message is serialized safely into the Discord content JSON field.
The service sends exactly one synchronous HTTP POST request per valid push().
The request uses wait=true, the configured timeout, valid JSON headers and a valid Discord API User-Agent.
HTTP redirects are not followed.
Successful 2xx responses return normally.
Every non-2xx response is reported as IOException.
HTTP 429 is reported without automatic retry.
Network and timeout failures use the IOException contract.
Interrupted delivery restores the thread’s interrupt status and throws IOException with the original interruption as its cause.
Valid delivery no longer throws UnsupportedOperationException.
The webhook endpoint and token never appear in logs or exception messages.
The active OpenSpec change passes strict validation and remains unarchived.
The project compiles and assembles successfully.
## Background
Issue #70 refactored the notification framework into the transport-independent API under:
```text
com.r35157.service.notification
```
The resulting `DiscordNotificationService` currently exists as an intentionally unimplemented stub:
```text
com.r35157.service.notification.impl.discord.DiscordNotificationService
```
It already implements:
```java
BoundNotificationService
```
and accepts an `ΩNotificationMessageΩ`, but valid messages currently result in `UnsupportedOperationException`.
The notification architecture intentionally separates consumers from transports. A future `EvelynBurnerService`, for example, may receive a configured `BoundNotificationService`, but it must not know whether notifications are delivered through Discord, Pushover, SMTP, Telegram, or another transport.
Discord-specific endpoints, HTTP configuration, JSON payloads and response handling must therefore remain encapsulated inside `DiscordNotificationService`.
## Objective
Implement synchronous, text-only Discord incoming-webhook delivery in `DiscordNotificationService`.
After construction, the service must be fully configured and usable solely through:
```java
BoundNotificationService notificationService = discordNotificationService;
notificationService.push(message);
```
Each `push()` call must require only the `ΩNotificationMessageΩ`.
## Constructor and configuration
Replace the current raw `URL` constructor with a constructor equivalent to:
```java
public DiscordNotificationService(
@NotNull ΩRestEndpointΩ discordWebhookEndpoint,
@NotNull Duration timeout
)
```
The precise formatting may follow existing project conventions, but these type semantics must be preserved.
`ΩRestEndpointΩ` resolves to `URI` and is already present in the authoritative DeTag configuration. It is the appropriate existing ValueTag for this endpoint.
The implementation must:
* Store the fully bound Discord webhook endpoint.
* Use the supplied timeout as both the HTTP connection timeout and the individual request timeout.
* Build its internal `HttpClient` during construction.
* Disable HTTP redirect following.
* Remove the existing constructor accepting `URL`.
* Not add a compatibility constructor accepting `URL` or raw `String`.
* Not add or modify any ValueTag definitions.
Because `ΩRestEndpointΩ` resolves to `URI`, the `.tjava` source must retain the required `java.net.URI` import even if the backing type name is only introduced by DeTag generation.
## Constructor validation
All constructor arguments must be validated before the service instance is usable and before any network activity can occur.
Reject the webhook endpoint with `IllegalArgumentException` if it is null or is not a structurally valid canonical Discord incoming-webhook endpoint.
Validation must require:
* An absolute URI.
* The `https` scheme.
* The canonical `discord.com` host, compared case-insensitively.
* No user-info component.
* No fragment.
* No pre-existing query string, because the service owns the execution query parameters.
* No non-HTTPS port.
* A Discord execute-webhook path containing non-blank webhook ID and token segments.
* Either the canonical unversioned webhook path or a valid versioned Discord API webhook path.
The validation must be structural only. Construction must not perform a network request to test whether the webhook currently exists.
Reject the timeout with `IllegalArgumentException` if it is:
* null
* zero
* negative
Validation exceptions, logs and diagnostic output must never contain the complete webhook endpoint or its token.
## Notification-message validation
`push()` must continue to reject an `ΩNotificationMessageΩ` that is:
* null
* empty
* blank
These cases must throw `IllegalArgumentException`.
Additionally, reject messages that exceed Discord’s 2,000-character content limit with `IllegalArgumentException`.
All message validation must occur before:
* JSON serialization
* `HttpRequest` construction
* any HTTP or transport operation
A valid message must not be trimmed or otherwise rewritten. Its content must be preserved exactly when placed in the Discord JSON payload.
## Discord HTTP request
For every valid `push()` call, `DiscordNotificationService` must synchronously execute one Discord webhook request.
The request must:
* Use HTTP `POST`.
* Use the configured Discord webhook endpoint.
* Add the query parameter:
```text
wait=true
```
* Set `Content-Type` to `application/json` with UTF-8 encoding.
* Set `Accept` to `application/json`.
* Provide a valid Discord HTTP API `User-Agent` identifying the Nenjim client.
* Apply the configured request timeout.
* Send a JSON object containing the message in Discord’s `content` field.
The payload must be semantically equivalent to:
```json
{
"content": "the notification message"
}
```
Use an existing JSON library already present in the project, preferably Jackson, to produce the payload. Do not construct JSON by manually concatenating or escaping strings.
The webhook token embedded in the endpoint is the authentication mechanism. Do not add a bot `Authorization` header.
The call must use synchronous `HttpClient.send(...)`. Do not use `sendAsync(...)`, background executors or fire-and-forget delivery.
Using `wait=true` is required so Discord confirms that the message was saved instead of merely acknowledging the request without reporting a later save failure.
## HTTP response handling
Any HTTP status in the `2xx` range must be treated as successful delivery.
Any other status must cause `push()` to throw `IOException`, including:
* redirects
* client errors
* invalid or deleted webhooks
* invalid payload responses
* `429 Too Many Requests`
* server errors
The `IOException` should include the HTTP status code and, when present and useful, Discord’s response body. It must not include the webhook URI or webhook token.
This issue must not implement automatic retry or sleeping after a `429` response. Rate limiting is reported synchronously as an `IOException`.
After a successful response, `push()` returns normally.
## Communication and interruption failures
Network, connection, timeout, request-body preparation and other delivery failures must be reported through the existing synchronous `IOException` contract.
If `HttpClient.send(...)` throws `InterruptedException`, the implementation must:
1. Restore the current thread’s interrupt status:
```java
Thread.currentThread().interrupt();
```
2. Throw an `IOException` describing that Discord delivery was interrupted.
3. Preserve the original `InterruptedException` as the cause.
A valid Discord notification must no longer throw `UnsupportedOperationException`.
## Secret handling
A Discord webhook endpoint contains a secret token and must be treated as a credential.
The implementation must not:
* Log the complete webhook endpoint.
* Include it in exception messages.
* Print it during debugging or manual verification.
* Add a real webhook endpoint to source code, examples, OpenSpec artifacts or repository configuration.
* Follow redirects that could disclose the webhook token to another endpoint.
If live manual verification is performed, the webhook must be supplied through a non-versioned local mechanism and removed afterwards.
## Existing inline example
Update the current private inline usage example in `DiscordNotificationService` as required by the new constructor signature, or remove it if it no longer provides useful compile-time documentation.
It must not contain a real Discord webhook, a credential-like placeholder, or a network-executable example.
This is not permission to add a test.
## OpenSpec expectations
Create an active OpenSpec change for this issue using the change ID:
```text
71-implement-discord-webhook-notification-delivery
```
The OpenSpec artifacts must include:
* proposal
* design
* delta specification
* implementation tasks
The delta specification must modify the existing `notification-services` capability. In particular, it must replace the current requirement that Discord delivery remains unimplemented with requirements and scenarios covering:
* fully configured construction
* webhook endpoint and timeout validation
* notification-message validation
* JSON webhook delivery
* synchronous confirmation with `wait=true`
* successful HTTP responses
* non-successful HTTP responses
* communication failures
* interrupted delivery with restored interrupt status
* webhook-secret protection
The OpenSpec design must record the constructor types, URI validation rules, timeout semantics, HTTP status policy and the decision not to implement retries or composition-root wiring.
Run strict OpenSpec validation before considering the implementation complete.
Keep the OpenSpec change active and unarchived after implementation. Do not sync or archive it unless explicitly requested separately.
## Scope boundaries
This issue may change:
* `DiscordNotificationService`
* its inline documentation or compile-time example
* the active OpenSpec change artifacts
* unavoidable directly affected production references, if any exist
This issue must not:
* Load a webhook from a configuration file.
* Change `NenjimHubImpl.startAutoRunProcesses()`.
* Wire Discord into Nenjim, Evelyn or a Burner service.
* Implement or modify `EvelynBurnerService`.
* Change the notification API interfaces.
* Change `PushoverNotificationService`.
* Change `SMTPNotificationService` or `SMTPDestination`.
* Change alarm-specific notification code.
* Add or modify ValueTags or the DeTag configuration.
* Add asynchronous delivery.
* Add retries, rate-limit waiting, queues, persistence or an outbox.
* Add Discord embeds, attachments, polls, components, TTS, thread selection, username overrides or avatar overrides.
* Add bot authentication.
* Add or modify unit tests.
Configuration loading and composition-root wiring must be handled in a separate follow-up issue after this service implementation has been reviewed.
## Unit-test restriction
Do not add, generate or modify unit tests as part of this issue.
Existing tests may be compiled or executed unchanged as part of normal verification, but no test source files may be changed.
Do not introduce a mock HTTP server, test-only constructor or production seam solely for unit testing.
## Verification
Verification must include:
* Compilation of the generated main sources from the `.tjava` source.
* Compilation of the existing unchanged test sources.
* The repository’s normal assembly or build.
* Strict validation of the active OpenSpec change.
* Structural confirmation that the old `URL` constructor and `UnsupportedOperationException` delivery stub are gone.
* Structural confirmation that no real webhook endpoint or token was added.
* Review of the complete diff for changes outside the permitted scope.
* Confirmation that no unit-test files were added or modified.
If a real webhook is explicitly available, one manual live text delivery may additionally be performed. Absence of a live webhook must not be worked around by committing credentials or weakening endpoint validation.
## Acceptance criteria
* `DiscordNotificationService` remains a `BoundNotificationService`.
* Consumers still supply only `ΩNotificationMessageΩ` to `push()`.
* The constructor accepts an `ΩRestEndpointΩ` and a positive `Duration`.
* The old raw `URL` constructor has been removed without a compatibility overload.
* No new ValueTag is introduced.
* Invalid endpoints and invalid timeouts fail during construction with `IllegalArgumentException`.
* Null, empty, blank and over-2,000-character messages fail before JSON or HTTP work begins.
* A valid message is serialized safely into the Discord `content` JSON field.
* The service sends exactly one synchronous HTTP `POST` request per valid `push()`.
* The request uses `wait=true`, the configured timeout, valid JSON headers and a valid Discord API `User-Agent`.
* HTTP redirects are not followed.
* Successful `2xx` responses return normally.
* Every non-`2xx` response is reported as `IOException`.
* HTTP `429` is reported without automatic retry.
* Network and timeout failures use the `IOException` contract.
* Interrupted delivery restores the thread’s interrupt status and throws `IOException` with the original interruption as its cause.
* Valid delivery no longer throws `UnsupportedOperationException`.
* The webhook endpoint and token never appear in logs or exception messages.
* No real webhook credential is committed.
* Pushover, SMTP, alarm code, Burner code and Nenjim composition-root wiring remain unchanged.
* No unit tests are added or modified.
* The active OpenSpec change passes strict validation and remains unarchived.
* The project compiles and assembles successfully.
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
Issue #70 refactored the notification framework into the transport-independent API under:
The resulting
DiscordNotificationServicecurrently exists as an intentionally unimplemented stub:It already implements:
and accepts an
ΩNotificationMessageΩ, but valid messages currently result inUnsupportedOperationException.The notification architecture intentionally separates consumers from transports. A future
EvelynBurnerService, for example, may receive a configuredBoundNotificationService, but it must not know whether notifications are delivered through Discord, Pushover, SMTP, Telegram, or another transport.Discord-specific endpoints, HTTP configuration, JSON payloads and response handling must therefore remain encapsulated inside
DiscordNotificationService.Objective
Implement synchronous, text-only Discord incoming-webhook delivery in
DiscordNotificationService.After construction, the service must be fully configured and usable solely through:
Each
push()call must require only theΩNotificationMessageΩ.Constructor and configuration
Replace the current raw
URLconstructor with a constructor equivalent to:The precise formatting may follow existing project conventions, but these type semantics must be preserved.
ΩRestEndpointΩresolves toURIand is already present in the authoritative DeTag configuration. It is the appropriate existing ValueTag for this endpoint.The implementation must:
HttpClientduring construction.URL.URLor rawString.Because
ΩRestEndpointΩresolves toURI, the.tjavasource must retain the requiredjava.net.URIimport even if the backing type name is only introduced by DeTag generation.Constructor validation
All constructor arguments must be validated before the service instance is usable and before any network activity can occur.
Reject the webhook endpoint with
IllegalArgumentExceptionif it is null or is not a structurally valid canonical Discord incoming-webhook endpoint.Validation must require:
httpsscheme.discord.comhost, compared case-insensitively.The validation must be structural only. Construction must not perform a network request to test whether the webhook currently exists.
Reject the timeout with
IllegalArgumentExceptionif it is:Validation exceptions, logs and diagnostic output must never contain the complete webhook endpoint or its token.
Notification-message validation
push()must continue to reject anΩNotificationMessageΩthat is:These cases must throw
IllegalArgumentException.Additionally, reject messages that exceed Discord’s 2,000-character content limit with
IllegalArgumentException.All message validation must occur before:
HttpRequestconstructionA valid message must not be trimmed or otherwise rewritten. Its content must be preserved exactly when placed in the Discord JSON payload.
Discord HTTP request
For every valid
push()call,DiscordNotificationServicemust synchronously execute one Discord webhook request.The request must:
POST.Content-Typetoapplication/jsonwith UTF-8 encoding.Accepttoapplication/json.User-Agentidentifying the Nenjim client.contentfield.The payload must be semantically equivalent to:
Use an existing JSON library already present in the project, preferably Jackson, to produce the payload. Do not construct JSON by manually concatenating or escaping strings.
The webhook token embedded in the endpoint is the authentication mechanism. Do not add a bot
Authorizationheader.The call must use synchronous
HttpClient.send(...). Do not usesendAsync(...), background executors or fire-and-forget delivery.Using
wait=trueis required so Discord confirms that the message was saved instead of merely acknowledging the request without reporting a later save failure.HTTP response handling
Any HTTP status in the
2xxrange must be treated as successful delivery.Any other status must cause
push()to throwIOException, including:429 Too Many RequestsThe
IOExceptionshould include the HTTP status code and, when present and useful, Discord’s response body. It must not include the webhook URI or webhook token.This issue must not implement automatic retry or sleeping after a
429response. Rate limiting is reported synchronously as anIOException.After a successful response,
push()returns normally.Communication and interruption failures
Network, connection, timeout, request-body preparation and other delivery failures must be reported through the existing synchronous
IOExceptioncontract.If
HttpClient.send(...)throwsInterruptedException, the implementation must:IOExceptiondescribing that Discord delivery was interrupted.InterruptedExceptionas the cause.A valid Discord notification must no longer throw
UnsupportedOperationException.Secret handling
A Discord webhook endpoint contains a secret token and must be treated as a credential.
The implementation must not:
If live manual verification is performed, the webhook must be supplied through a non-versioned local mechanism and removed afterwards.
Existing inline example
Update the current private inline usage example in
DiscordNotificationServiceas required by the new constructor signature, or remove it if it no longer provides useful compile-time documentation.It must not contain a real Discord webhook, a credential-like placeholder, or a network-executable example.
This is not permission to add a test.
OpenSpec expectations
Create an active OpenSpec change for this issue using the change ID:
The OpenSpec artifacts must include:
The delta specification must modify the existing
notification-servicescapability. In particular, it must replace the current requirement that Discord delivery remains unimplemented with requirements and scenarios covering:wait=trueThe OpenSpec design must record the constructor types, URI validation rules, timeout semantics, HTTP status policy and the decision not to implement retries or composition-root wiring.
Run strict OpenSpec validation before considering the implementation complete.
Keep the OpenSpec change active and unarchived after implementation. Do not sync or archive it unless explicitly requested separately.
Scope boundaries
This issue may change:
DiscordNotificationServiceThis issue must not:
NenjimHubImpl.startAutoRunProcesses().EvelynBurnerService.PushoverNotificationService.SMTPNotificationServiceorSMTPDestination.Configuration loading and composition-root wiring must be handled in a separate follow-up issue after this service implementation has been reviewed.
Unit-test restriction
Do not add, generate or modify unit tests as part of this issue.
Existing tests may be compiled or executed unchanged as part of normal verification, but no test source files may be changed.
Do not introduce a mock HTTP server, test-only constructor or production seam solely for unit testing.
Verification
Verification must include:
.tjavasource.URLconstructor andUnsupportedOperationExceptiondelivery stub are gone.If a real webhook is explicitly available, one manual live text delivery may additionally be performed. Absence of a live webhook must not be worked around by committing credentials or weakening endpoint validation.
Acceptance criteria
DiscordNotificationServiceremains aBoundNotificationService.ΩNotificationMessageΩtopush().ΩRestEndpointΩand a positiveDuration.URLconstructor has been removed without a compatibility overload.IllegalArgumentException.contentJSON field.POSTrequest per validpush().wait=true, the configured timeout, valid JSON headers and a valid Discord APIUser-Agent.2xxresponses return normally.2xxresponse is reported asIOException.429is reported without automatic retry.IOExceptioncontract.IOExceptionwith the original interruption as its cause.UnsupportedOperationException.