Problem
A callback result can arrive after the previous stream finalized and after the next invocation_created event has already advanced the thread. When that callback websocket payload does not carry a reliable invocationId, the client cannot exact-match it back to the original stream bubble.
Today this forces the frontend to fall back to invocationless placeholder matching or finalized_fallback heuristics. Those heuristics can mitigate some races, but they also add coordination state and can reopen regressions such as stale callback suppression against a live stream.
User-visible symptom
- Late callback from invocation A arrives after invocation B has started.
- The client no longer has a precise replacement target for the callback.
- The callback may render as a duplicate bubble instead of replacing the original stream bubble.
- Related heuristic fixes can also risk suppressing legitimate stream chunks from the current invocation if the wrong bubble is matched.
Root cause
The backend callback routes already have invocationId in most internal paths, but not every callback-origin websocket/broadcast payload guarantees that field to the client. That gap forces the frontend to infer identity after the fact.
Preferred fix
Make invocationId a hard protocol invariant for every websocket event whose origin is callback.
If callback events always carry invocationId, the client can use exact replacement matching and stop depending on invocationless fallback / finalized-fallback fencing for correctness.
Acceptance criteria
- Every callback-origin websocket payload sent to the web client includes the correct
invocationId.
- Late callbacks after
invocation_created still replace the original stream bubble instead of creating a duplicate bubble.
- The web client no longer needs heuristic callback->finalized-stream bridging when exact invocation identity is available.
- Regression coverage includes:
- same-content late callback across invocation boundary
- different-content late callback across invocation boundary
- background-thread callback after a new invocation starts
- callback-first / stream-late ordering
Related
Problem
A callback result can arrive after the previous stream finalized and after the next
invocation_createdevent has already advanced the thread. When that callback websocket payload does not carry a reliableinvocationId, the client cannot exact-match it back to the original stream bubble.Today this forces the frontend to fall back to
invocationlessplaceholder matching orfinalized_fallbackheuristics. Those heuristics can mitigate some races, but they also add coordination state and can reopen regressions such as stale callback suppression against a live stream.User-visible symptom
Root cause
The backend callback routes already have
invocationIdin most internal paths, but not every callback-origin websocket/broadcast payload guarantees that field to the client. That gap forces the frontend to infer identity after the fact.Preferred fix
Make
invocationIda hard protocol invariant for every websocket event whoseoriginiscallback.If callback events always carry
invocationId, the client can use exact replacement matching and stop depending oninvocationlessfallback / finalized-fallback fencing for correctness.Acceptance criteria
invocationId.invocation_createdstill replace the original stream bubble instead of creating a duplicate bubble.Related