Watchdog notice reader: pin timeout, spawn-error, 5xx/429 and guard behaviour; say what non-2xx is
Tests only, plus wording. No change to what the reader or bridge does.
- A spawn timeout and a spawn error (ENOBUFS from a bridge that floods
stdout) record unknown, are never re-posted, and block the closure.
WATCHDOG_NOTICE_BRIDGE_TIMEOUT_MS overrides the 60 s timeout so the test
reaches that branch; the default is unchanged.
- A 500/502/503/429 answer to the message POST exits 75 from the bridge, and
through the reader is retried with the same nonce until it completes.
- allowed_mentions {parse: []} is asserted in the posted body; status exits
non-zero while an entry is unknown and zero when none is.
- The bridge comment and docs/watchdog-notice.md no longer call a non-2xx
answer "definite": it is retried with the same nonce, relying on
enforce_nonce inside Discord's window, and may double-send if Discord acted
on a 5xx outside it.
Builds. Two tests fail on Windows (SIGKILL status), as before; they pass on
Yggdrasil.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
1f001e11d2
commit
ff087a554b
4 changed files with 77 additions and 18 deletions
|
|
@ -471,9 +471,12 @@ async function postDiscordMessage(options) {
|
|||
}
|
||||
|
||||
// discord-dm exits NOT_SENT_EXIT only when it knows no message was delivered:
|
||||
// anything before the message POST, or a definite non-2xx answer to it. A
|
||||
// failure after the POST was sent (network error, receipt completion, output)
|
||||
// keeps exit 1, so the caller treats the outcome as unknown.
|
||||
// anything before the message POST, or a non-2xx answer to it. A 5xx or 429
|
||||
// is not proof of non-delivery (Discord may have created the message before
|
||||
// answering): the caller retries with the same nonce and relies on
|
||||
// enforce_nonce inside Discord's window. A failure after the POST was sent
|
||||
// (network error, receipt completion, output) keeps exit 1, so the caller
|
||||
// treats the outcome as unknown.
|
||||
const NOT_SENT_EXIT = 75;
|
||||
class NotSentError extends Error {}
|
||||
class DiscordRejection extends Error {}
|
||||
|
|
|
|||
|
|
@ -28,15 +28,17 @@ const INCIDENT_SCHEMA = "idunn.operator_incident.v1";
|
|||
const JOURNAL_TYPE = "bifrost.watchdog_notice_execution";
|
||||
const JOURNAL_SCHEMA = "bifrost.watchdog_notice_execution.v1";
|
||||
const MAX_ATTEMPTS = 5;
|
||||
const BRIDGE_TIMEOUT_MS = 60_000;
|
||||
// The override exists so a test can reach the timeout branch without a 60 s wait.
|
||||
const BRIDGE_TIMEOUT_MS = Number(process.env.WATCHDOG_NOTICE_BRIDGE_TIMEOUT_MS) || 60_000;
|
||||
const STATUSES = ["running", "completed", "failed", "unknown"];
|
||||
const NAME = /^[a-z][a-z-]{0,63}$/;
|
||||
// Idunn's require_id (GameCult/Idunn src/control_plane.rs:9122-9131 at 7f7528b):
|
||||
// 1-256 bytes of [A-Za-z0-9-_.:/]. The notice puts the subject in inline code.
|
||||
const SUBJECT = /^[A-Za-z0-9_.:/-]{1,256}$/;
|
||||
const MAX_DATE_MS = 8.64e15;
|
||||
// bifrost-bridge.mjs discord-dm exits 75 (EX_TEMPFAIL) only when it knows the
|
||||
// message was not sent. Every other non-success leaves the outcome unknown.
|
||||
// bifrost-bridge.mjs discord-dm exits 75 (EX_TEMPFAIL) for failures before
|
||||
// the message POST and for any non-2xx answer to it: retried with the same nonce.
|
||||
// Every other non-success leaves the outcome unknown.
|
||||
const BRIDGE_NOT_SENT = 75;
|
||||
|
||||
class NoticeError extends Error {}
|
||||
|
|
@ -175,7 +177,7 @@ function noticeText(record, notice) {
|
|||
}
|
||||
|
||||
// The one send primitive. The outcome is completed with a message id; failed
|
||||
// only when the bridge reports exit 75 (known not sent); otherwise unknown. The
|
||||
// only when the bridge reports exit 75 (retryable, same nonce); otherwise unknown. The
|
||||
// error is a fixed code: bridge stdout and stderr never reach the journal.
|
||||
async function sendNotice({ bridgeCli, recipientId, receiptStore, key, nonce, record, content }) {
|
||||
const dir = await mkdtemp(resolve(tmpdir(), "watchdog-notice-"));
|
||||
|
|
|
|||
Loading…
Reference in a new issue