## Context

See `proposal.md` for motivation. `DiscordNotificationService` is the only affected production class and currently validates only null/blank messages before throwing `UnsupportedOperationException`. The existing Jackson dependency can safely serialize the payload; no notification API, caller, or composition-root change is required.

## Goals / Non-Goals

**Goals:**

- Construct a fully bound synchronous Discord webhook service from `ΩRestEndpointΩ` and a positive `Duration`.
- Validate the endpoint locally, preserve message text exactly, and make successful response handling explicit.
- Keep the webhook URI confidential in all validation and delivery paths.

**Non-Goals:**

- Configuration loading or Nenjim composition-root wiring.
- Retries, rate-limit waiting, asynchronous execution, or non-text Discord features.
- Changes to the notification API, Pushover, SMTP, alarms, Evelyn, or tests.

## Decisions

### Strict local URI validation before client construction

The constructor validates the URI shape with URI components and a fixed path grammar: `https://discord.com/api/webhooks/{id}/{token}` or `https://discord.com/api/v{version}/webhooks/{id}/{token}`. Versioned paths accept only Discord API versions 6 through 10, which are currently non-discontinued; the host is case-insensitive and all token-bearing endpoint values are absent from diagnostics. A network probe is rejected because construction must be side-effect free and valid webhooks can change remotely.

### One synchronous JSON request per valid push

The service uses a construction-time `HttpClient` configured with the supplied connect timeout and `Redirect.NEVER`. Every valid message is serialized with Jackson into `content`, then posted with `wait=true`, the same request timeout, JSON headers, and a `DiscordBot (url, version)` User-Agent identifying Nenjim. This preserves the BoundNotificationService contract and lets Discord confirm persistence. Manual JSON escaping and asynchronous delivery are rejected.

### Failure policy preserves secrets and interruption

Only 2xx is successful. Other statuses become `IOException` containing only status and useful body. I/O failures are wrapped with non-sensitive context; interruption restores the flag and is the cause of an `IOException`. The service neither retries nor waits after 429, so each valid `push()` owns exactly one send attempt.

## Risks / Trade-offs

- **[Webhook endpoint is a credential]** → Never log or interpolate it into exceptions; redirect following is disabled.
- **[Strict URI validation could reject a future Discord route]** → Accept canonical unversioned and conventional versioned execute-webhook paths only; future route support requires an explicit spec change.
- **[No automated tests by issue instruction]** → Compile generated sources and unchanged tests, assemble, validate OpenSpec, and perform structural and diff checks without a live webhook.

## Migration Plan

1. Replace the URL-based stub constructor and delivery path in place.
2. Update the Discord requirement in the active capability delta.
3. Verify compilation, assembly, strict OpenSpec validation, and absence of old-stub patterns or credentials.

Rollback is a single-class and active-change revert; no consumer migration is needed.
