Skip to content

fix(dispatch): reconcile Hub task replay with durable admission evidence #2349

Description

@DeliciousBuding

Summary

Complete the Hub-task replay path beyond the process-local delivery cache. The current durable lookup can bypass original-run capability checks after cache loss, and it can turn an executor admission rejection into a false successful replay. Both failures are reproduced through real HTTP tests on master.

Scope / invariants

  • Separate admission evidence from run execution status. Persist Hub identity and pending admission atomically before starting the executor; record accepted/rejected outcomes using the existing File/SQLite storage machinery, not a second database or event log.
  • In-flight same-Hub-task admissions cannot return 202 before the first executor admission finishes, even with a different delivery ID or no delivery ID. Live contention is retryable 503; a retained pending/legacy-queued record without a current owner is uncertain and cannot trigger automatic restart.
  • Every durable replay validates capability against the actual stored project/thread just like cached delivery replay. Request validation remains fail-closed.
  • A definite pre-execution capacity rejection may retry under the same Hub task; a generic rejected attempt never becomes cached success. Accepted work that later fails must never restart merely because its execution status is terminal.
  • Accepted receipts only acknowledge admission, not process liveness or recovery. Replays can retry persistence of the admission evidence; storage failure must not manufacture a success response.
  • Desktop treats explicit executor-capacity rejection as transient without ACK/FAIL, retaining existing outbox ownership. Existing delivery behavior remains intact.

Files / ownership

  • Main: edge-server/internal/runcontrol/*, internal/api/handlers_run_delivery.go, handlers_runs.go, internal/api/hub_task_replay_test.go, internal/errcode/codes.go and directly affected admission/cleanup tests; api/events.md and api/openapi.yaml.
  • Storage worker: edge-server/internal/store/store_types.go, store_interfaces.go, store_domain.go, file_store.go, sqlite_store.go, and one focused admission persistence test file. One independent worktree; exact methods will be agreed before editing.
  • Desktop worker (if delegated): the existing transient-admission mapper/hook, typed bridge error field and behavior tests, in a separate worktree.

Interfaces / compatibility

  • Add run admission metadata inside existing JSON/SQLite metadata; no destructive migration and no new credential source. Memory/File/SQLite repositories preserve their existing unrelated write behavior.
  • Keep local/MCP no-Hub-task run creation compatible. Known legacy runs with started evidence may replay; ambiguous pre-start records are not silently re-executed.
  • Do not normalize all execution intent fields across Hub transports in this issue; that needs independent contract evidence. Do not promise recovery after retention deletes all identity evidence.

Acceptance

  • Red-to-green HTTP tests for cold-cache original-scope authorization and rejected admission not becoming fake success.
  • Deterministically held concurrent Hub-task admission returns busy across delivery identities; successful replay returns the original run without another start.
  • File and SQLite reopen preserve pending/accepted/rejected admission evidence; storage-error injection proves no successful admission receipt is returned without persistence.
  • Capacity rejection can reattempt safely; accepted then failed execution cannot be re-executed; orphan/legacy ambiguity remains explicit and fail-closed.
  • Desktop transient error behavior, Go race/static/unit gates, API/owner documentation and required CI pass with evidence levels recorded.
  • One-topic PR is merged, final source tree is checked against the tested tree and task worktrees are cleaned.

Negative constraints

No production deployment, actual model/login requests, process restart/cancellation, credentials or destructive data migration. Do not infer absence of external side effects from queued/failed status. Do not add a second retry loop, replica execution engine, new verifier or local progress SSOT. #2304 and #1663 remain independent.

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