You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Make the existing Hub → Desktop/Edge task-admission path retry-safe and consumable end to end. The user-facing requirement is that a busy/rejected delivery can be retried, duplicate channels cannot launch the same work twice, and a successful replay identifies the original run instead of making the bridge report a failure. This is a product execution-loop slice, not another storage micro-optimization or a new orchestration framework.
Current source evidence
Base: a3f044b19189ff7932b7a5b67c0a13f9baeeaf81.
app/desktop/src/hooks/hubIntegrationMappers.ts builds Edge requests without carrying the Hub delivery_id.
edge-server/internal/api/handlers_runs.go calls delivery dedup before capability validation and records the ID before target validation/admission can fail. A rejected request can therefore become a cached 202 on retry.
The cached 202 contains only deduplicated/deliveryId, while Desktop extractCreatedRunId requires a run ID and Hub's HTTP response planner likewise consumes the run ID.
The current Seen→Record calls are separate operations, not an atomic admission claim.
Scope / file ownership
One integration PR for this end-to-end contract. Independent writers use separate task worktrees.
Main: edge-server/internal/deliverydedup/deliverydedup.go and its unit tests; edge-server/internal/api/handlers_runs.go; a focused run-delivery handler helper if needed; edge-server/internal/errcode/codes.go; api/events.md and directly affected API contract documentation.
Go regression worker: a new focused edge-server/internal/api/*delivery*test.go plus only the directly affected existing delivery assertions.
Desktop worker: app/desktop/src/hooks/hubIntegrationMappers.ts, useHubIntegration.ts, and their existing/focused behavior tests.
Browser regression: main integrator owns app/desktop/src/__e2e__/hub-delivery-admission.spec.ts; only HTTP/WS fixtures are added, with no production test seam or runtime credentials.
Contract / architectural decisions
Dedup tracks admission, not mere observation. Atomically reserve an ID, commit a receipt only after successful run admission, and release a failed attempt so a subsequent delivery can retry. Claims and accepted receipts remain bounded; pending claims are not evicted or expired into duplicate execution.
Every incoming request still passes its current authentication/capability policy before receiving a duplicate-success response. For Hub deliveries, the stable hubTaskId is the business binding: the legacy direct HTTP path uses the local thread while Desktop uses its conversation thread, so equal Hub task IDs must still converge on the original run. For legacy deliveries without hubTaskId, bind project/thread. A different Hub task or legacy binding is a conflict. Cached replays also revalidate capability against the actual stored run scope before returning its data.
Accepted replays return the original run ID in the normal accepted run envelope, with additive dedup metadata; they never create a second run, timeline or executor start. A removed run is not silently recreated.
In-flight/bounded-capacity contention is an explicit temporary 503 delivery_busy with Retry-After, not a false 202. A binding conflict is 409 delivery_conflict, not another task's receipt.
Desktop preserves snake/camel delivery-ID aliases when building the Edge request. For 503 delivery_busy and 409 active_run_exists, it keeps the task queued, does not acknowledge or fail it, and leaves retry ownership with the existing Hub outbox. It must not invent a second retry loop or downgrade a known running/terminal task on duplicate dispatch. Other failures retain their existing handling.
Legacy payloads without delivery IDs retain their existing path. No new runtime/credential source, no relaxation of workspace/capability gates, no schema migration or rollout in this issue.
Acceptance
HTTP regression: reject before admission, fix/free the target, retry the same delivery ID, and observe one real accepted run rather than a fake cached success.
Concurrent duplicate while first admission is held returns retryable busy; after completion replay returns the same run ID with no duplicate execution or timeline.
A cached receipt cannot bypass capability validation (including the stored run scope), cross another Hub task, or cross a legacy project/thread binding. Both channel-specific thread representations of the same Hub task replay the original run.
Desktop forwards delivery IDs, preserves prior task progress, and neither acknowledges nor fails a temporary admission rejection; actual accepted/replayed responses remain consumable.
Existing bounded cache/TTL, Go/API behavior, Desktop behavior/typecheck and applicable contract/CI gates pass; failures and unrun evidence levels are recorded.
Source/contract documentation and issue evidence are synchronized, then the one-topic PR is merged and task worktrees cleaned.
Negative constraints / follow-on boundary
Do not turn an old queued run into an automatic restart: the actual subprocess start occurs before the durable started transition, so queued alone does not prove no side effect ran. Durable pre-start acceptance versus uncertain execution recovery needs a separate explicit design/acceptance slice. This issue does not claim post-crash process resume, real-model E2E, live rollout or packaged Desktop readiness. Keep #2304 and #1663 independent.
Scope clarification from source review
dispatch.BuildEdgeRunRequest currently targets the local project/thread; Desktop derives its Edge thread from dispatch conversation data. This slice must not introduce a false conflict between those two transport representations of the same Hub task. The receipt is anchored to Hub task identity, returns the actual original run scope, and does not grant access outside the incoming capability. Canonicalizing all model/workspace/context fields across transports and persistent process restart reconciliation are separate follow-on slices, not claims made by this admission change.
Summary
Make the existing Hub → Desktop/Edge task-admission path retry-safe and consumable end to end. The user-facing requirement is that a busy/rejected delivery can be retried, duplicate channels cannot launch the same work twice, and a successful replay identifies the original run instead of making the bridge report a failure. This is a product execution-loop slice, not another storage micro-optimization or a new orchestration framework.
Current source evidence
Base:
a3f044b19189ff7932b7a5b67c0a13f9baeeaf81.app/desktop/src/hooks/hubIntegrationMappers.tsbuilds Edge requests without carrying the Hubdelivery_id.edge-server/internal/api/handlers_runs.gocalls delivery dedup before capability validation and records the ID before target validation/admission can fail. A rejected request can therefore become a cached 202 on retry.deduplicated/deliveryId, while DesktopextractCreatedRunIdrequires a run ID and Hub's HTTP response planner likewise consumes the run ID.Scope / file ownership
One integration PR for this end-to-end contract. Independent writers use separate task worktrees.
edge-server/internal/deliverydedup/deliverydedup.goand its unit tests;edge-server/internal/api/handlers_runs.go; a focused run-delivery handler helper if needed;edge-server/internal/errcode/codes.go;api/events.mdand directly affected API contract documentation.edge-server/internal/api/*delivery*test.goplus only the directly affected existing delivery assertions.app/desktop/src/hooks/hubIntegrationMappers.ts,useHubIntegration.ts, and their existing/focused behavior tests.app/desktop/src/__e2e__/hub-delivery-admission.spec.ts; only HTTP/WS fixtures are added, with no production test seam or runtime credentials.Contract / architectural decisions
503 delivery_busywithRetry-After, not a false 202. A binding conflict is409 delivery_conflict, not another task's receipt.503 delivery_busyand409 active_run_exists, it keeps the task queued, does not acknowledge or fail it, and leaves retry ownership with the existing Hub outbox. It must not invent a second retry loop or downgrade a known running/terminal task on duplicate dispatch. Other failures retain their existing handling.Acceptance
Negative constraints / follow-on boundary
Do not turn an old
queuedrun into an automatic restart: the actual subprocess start occurs before the durablestartedtransition, so queued alone does not prove no side effect ran. Durable pre-start acceptance versus uncertain execution recovery needs a separate explicit design/acceptance slice. This issue does not claim post-crash process resume, real-model E2E, live rollout or packaged Desktop readiness. Keep #2304 and #1663 independent.Scope clarification from source review
dispatch.BuildEdgeRunRequestcurrently targets the local project/thread; Desktop derives its Edge thread from dispatch conversation data. This slice must not introduce a false conflict between those two transport representations of the same Hub task. The receipt is anchored to Hub task identity, returns the actual original run scope, and does not grant access outside the incoming capability. Canonicalizing all model/workspace/context fields across transports and persistent process restart reconciliation are separate follow-on slices, not claims made by this admission change.