2026-06-15 — Service Bus bridge + webhook stability hardening¶
Motivation¶
A focused self-critique pass (contract/state-machine, liveness, idempotency, partial-failure, observability) over the recently-shipped Service Bus unified-ingress + completion-bridge + webhook code surfaced four defects. None were caught by the existing focused tests because they live in the design seams the mechanical review cannot see (out-of-order delivery, one-item-aborts-the-batch, dead branches, swallowed error reasons).
User-facing change¶
- Webhook no longer resurrects a finished job (Medium). The sibling
elb-openapipod fires lifecycle webhooks with a 3-retry exponential backoff, so a stalerunning/submittednotification can be delivered after the job already reached a terminal state. The receiver only guarded arunningrow against backwardsubmitted/queuedevents — a terminalcompleted/failed/cancelledrow could be flipped back torunningby a late event, making a finished job re-appear as in-flight on the dashboard. Terminal rows are now immutable against non-terminal events (reason: "terminal_locked"); a genuine terminal→terminal correction is still accepted (the sibling stays authoritative for terminals). - One flaky bridge no longer stalls the whole completion tick (Low-Medium).
publish_transitionshad no per-bridge exception isolation: a transient tracking-store write (mark_published/mark_done) raising aborted the entire tick and starved the remaining active bridges until the next beat run. The per-bridge body is now isolated (matchingdrain_requests/reconcile_stale_jobs); a failing bridge increments anerrorscounter and the tick continues. Any already-published event is deduped byevent_idon the subscriber; the bridge marker advances on the next tick.
API / internal diff summary¶
api/routes/blast/external_webhook.py—_apply_to_jobstateadds a terminal-lock guard (cur_statusterminal + incoming non-terminal → ignore). New responsereason: "terminal_locked".api/tasks/servicebus/tasks.py— extracted_publish_one_bridgehelper (returns(published_delta, finished_delta));publish_transitionswraps it per bridge and reports a newerrorscounter (additive — existing keyed consumers unaffected). Removed a deadattempt = 2 if …branch (it was unreachable; the equal-status casecontinues earlier, soattemptwas always 1) and corrected the_transition_eventdocstring to state thatevent_idis the authoritative dedup key.api/services/blast/message_trace.py—record_stageexception handler now logs the real exception instead of re-logging the stage name (the failure cause was being silently dropped).
Validation evidence¶
- New regression tests:
test_external_webhook.py::test_register_external_job_terminal_row_not_resurrected_by_late_event(12 parametrized cases: 3 terminal × 4 late non-terminal statuses) +…_terminal_correction_still_applies(3 cases).test_servicebus_tasks.py::test_publish_transitions_isolates_one_failing_bridge.uv run ruff check api— clean.uv run pytest -q api/tests— 3623 passed, 3 skipped (was 3607; +16 new).- No behaviour change for the
attemptfield (it was already always 1) or thepublish_transitionsreturn contract (theerrorskey is additive).