From dff71545a617e27f37fbac47a42cf11e4716f07e Mon Sep 17 00:00:00 2001 From: teconomix Date: Fri, 20 Mar 2026 10:21:19 +0000 Subject: [PATCH] fix(mattermost): track patchInflight in P4; use direct await in onSettled cleanup Add patchInflight Promise tracking to P4 (feat/mattermost-block-streaming-rebased), mirroring the existing P5 approach. The onSettled cleanup previously used a 3-second busy-wait on patchSending, which would race on slow Mattermost links: if the first preview POST takes longer than 3s the cleanup exits early, patchSending is forced false, and when the POST later resolves it creates an orphan post that is never deleted. Fix: track the interval tick's async function as patchInflight. onSettled awaits it directly so the cleanup always captures the final streamMessageId, regardless of how long the POST takes. (Codex ID=2964616785) --- .../mattermost/src/mattermost/monitor.ts | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/extensions/mattermost/src/mattermost/monitor.ts b/extensions/mattermost/src/mattermost/monitor.ts index cdddf595c1e..ef629a61f60 100644 --- a/extensions/mattermost/src/mattermost/monitor.ts +++ b/extensions/mattermost/src/mattermost/monitor.ts @@ -1409,6 +1409,9 @@ export async function monitorMattermostProvider(opts: MonitorMattermostOpts = {} let lastSentText = ""; let patchInterval: ReturnType | null = null; let patchSending = false; // prevents concurrent network calls + // Tracks the currently in-flight sendMessageMattermost / patchMattermostPost + // promise so that onSettled can await it directly rather than busy-waiting. + let patchInflight: Promise | null = null; // Latches true after the first send/edit failure to prevent the interval // from being re-armed by a later onPartialReply call (ID=2964357928). let previewSendFailed = false; @@ -1491,7 +1494,7 @@ export async function monitorMattermostProvider(opts: MonitorMattermostOpts = {} // re-patching with the same truncated content every 200 ms and hit rate limits. if (text === lastSentText) return; patchSending = true; - void (async () => { + const runTick = async () => { try { if (!streamMessageId) { try { @@ -1528,7 +1531,13 @@ export async function monitorMattermostProvider(opts: MonitorMattermostOpts = {} } finally { patchSending = false; } - })(); + }; + const inflightRun = runTick(); + patchInflight = inflightRun; + inflightRun.finally(() => { + if (patchInflight === inflightRun) patchInflight = null; + }); + void inflightRun; }, STREAM_PATCH_INTERVAL_MS); }; // ── End P4 streaming state ──────────────────────────────────────────────── @@ -1720,10 +1729,10 @@ export async function monitorMattermostProvider(opts: MonitorMattermostOpts = {} lastSentText = ""; const client = blockStreamingClient; void (async () => { - // Wait for any in-flight send/patch to settle so we get the final messageId. - const deadline = Date.now() + 3000; - while (patchSending && Date.now() < deadline) { - await new Promise((r) => setTimeout(r, 50)); + // Await the in-flight promise directly so we never miss a late-resolving + // POST — a 3s timeout would race on slow Mattermost links (ID=2964616785). + if (patchInflight) { + await patchInflight.catch(() => {}); } patchSending = false; const orphanId = streamMessageId;