Skip to content

fix(dispatch): preserve execution intent and callback ownership across transports #2350

Description

@DeliciousBuding

Summary

Preserve the same execution intent across Hub direct HTTP/outbox and Desktop WS/relay, and make callback ownership explicit before enabling a route. This is the next end-to-end dispatch slice after #2349, not a storage micro-optimization.

Verified source gaps

  1. hub-server/internal/service/dispatch/edge_request.go projects no workDir/model-parameter/tool configuration and hardcodes model to claude. dispatchsvc/agent_dispatch_edge_http.go passes no modelParams/toolWhitelist into it. Edge runcontrol requires a non-empty allowed workspace; therefore fresh direct admissions constructed by this path fail workdir_required rather than honoring the user-selected execution intent. Local project/thread differences remain an intentional transport compatibility boundary, not a standalone defect.
  2. app/desktop/src/hooks/hubIntegrationMappers.ts does not forward Hub messages/pinned_messages, and its string-only schema accessor drops the top-level JSON object supplied as structured_output_schema. The direct Go projection forwards messages/pins and explicitly serializes the schema. This changes runtime context, not merely display metadata.
  3. Callback ownership needs a route gate before repairing direct dispatch. If a configured direct Edge is a Desktop-managed sidecar without Hub callback configuration, a successful direct admission would not establish Desktop run/task mapping and would have no output callback owner. This is a conditional source-level risk, not a claim that production is currently in this configuration.

Scope / Files / Ownership

  • One integration PR with disjoint Hub projection and Desktop projection workers; main owns shared fixture contract, route/callback decision and acceptance.
  • Hub: hub-server/internal/service/dispatch/edge_request.go, edge_http_prep.go, affected dispatchsvc call sites and their tests.
  • Desktop: app/desktop/src/hooks/hubIntegrationMappers.ts and focused unit/browser contract tests.
  • Contract: existing API owner documents and tests/fixtures/dispatch/execution-intent.json, consumed by both Go and TypeScript tests. Fixture-only edits must select both test lanes.
  • Main Edge: HTTP callback ownership validation/capability advertisement, persisted run callback owner and executor callback reporting guard; associated API/store/lifecycle tests. Main Hub: ownership-aware route preflight, real-device reservation/receipt handling and PostgreSQL integration acceptance. Edge callback worker: typed tool/permission/result events on the existing reporter FIFO.

Invariants / Interfaces

  • Same logical task means the same model, reasoning/thinking, permission mode, workDir, system/append prompt, tool restrictions, messages/pins, structured schema, ephemeral/config values and delivery/Hub task identity across channels.
  • Preserve intentional target/project/thread representations and capability constraints; do not weaken workspace allowlists to make direct dispatch succeed.
  • An accepted route has a defined callback owner. Do not inject a Hub token into Desktop or silently enable two output reporters; make route eligibility explicit.

Callback ownership protocol

  • Modern Hub HTTP requests choose callbackOwner=edge; Desktop chooses callbackOwner=desktop. Edge persists the selected owner with pending Hub admission and returns the actual original owner on every receipt, including cross-channel replays. Requesting a different owner on replay does not reassign it.
  • /v1/health advertises capabilities.runCallbackOwnership and capabilities.directHubCallbacks (configuration presence only, never a credential or a live-reachability claim). Hub direct execution requires both; Desktop requires ownership support but may use a sidecar without direct callbacks. Missing capability is not optimistic compatibility: no run POST is issued to an old/unsupported Edge. Direct health identity must match the configured registered device, and the device must belong to the task Agent inviter.
  • Edge-owned runs use the Edge reporter; Desktop preserves local run state but does not emit a second set of task lifecycle/output callbacks. Relay delivery ACK remains the receiver Desktop responsibility. Unknown receipt ownership is explicit uncertainty, not a guessed owner.
  • Hub reserves the actual device before a run POST. An ambiguous HTTP response retains that reservation across outbox retries/restarts; it must not fall through to another executor. A Desktop-owned replay restores only the original device bridge. A late HTTP receipt preserves advanced callback status and any existing run/device binding.
  • Edge-owned delivery must preserve typed tool, permission, route and result events as well as text/final callbacks. Control events must not silently disappear when the stream queue is busy. Team-context execution stays on the Desktop control path: typed streams are observations, not authority to execute a Team route decision.

Acceptance

  • A common realistic Hub dispatch fixture produces equivalent runtime-intent fields in Go HTTP and Desktop projections, including absent/empty/default semantics.
  • A pure/fixture direct admission reaches Edge with a valid configured workspace and the selected runtime/model, instead of the hardcoded model or missing-workDir rejection.
  • Desktop delivers conversation/pinned context and object/string structured schemas to the existing Edge request contract.
  • Route tests prove callback-capability requirements fail closed without real tokens or production calls; accepted work cannot lose its output owner.
  • Real PostgreSQL tests prove UUID-safe device binding and late-receipt preservation through authorized ACK/done endpoints.
  • Lost/unknown HTTP receipts never trigger another device, including a fresh dispatcher retry; Desktop-owned replay restores only the original callback device.
  • Cross-layer behavior, API contract, applicable browser/Visual QA and required CI pass; documentation and one-topic PR are closed out.

Negative constraints

No live deployment, real-model/login request, credential distribution, automatic execution recovery, default-workspace bypass or new second retry loop. Do not call a future/conditional callback gap a verified production incident. Persistent admission work belongs to #2349.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions