Context
From PR #115 review. WebhookSink.flush() is not serialized — if a timer-triggered flush and an emit-triggered flush overlap, the second overwrites this.inflightFlush, and close() may not await the first POST.
Files
src/audit/sinks/webhook.ts (lines 54-61, 102-110)
Proposed fix
Use a flush queue or mutex pattern so concurrent calls chain rather than overwrite. On close(), drain the full chain before returning.
Also: failed batches re-enqueued after the timer is cleared in close() have no retry path. Consider a final drain-and-discard after the last flush.
Priority
Low — the webhook sink is explicitly best-effort, and the race window is narrow. No data loss in normal operation.
Context
From PR #115 review.
WebhookSink.flush()is not serialized — if a timer-triggered flush and an emit-triggered flush overlap, the second overwritesthis.inflightFlush, andclose()may not await the first POST.Files
src/audit/sinks/webhook.ts(lines 54-61, 102-110)Proposed fix
Use a flush queue or mutex pattern so concurrent calls chain rather than overwrite. On
close(), drain the full chain before returning.Also: failed batches re-enqueued after the timer is cleared in
close()have no retry path. Consider a final drain-and-discard after the last flush.Priority
Low — the webhook sink is explicitly best-effort, and the race window is narrow. No data loss in normal operation.