Skip to content

fix(api,ws): always propagate callback invocationId to avoid late-callback duplicate bubbles #454

@mindfn

Description

@mindfn

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    acceptedMaintainer accepted: ready for implementation/mergebugSomething isn't workingtriagedMaintainer reviewed, replied, and made an initial triage decision

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions