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
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.
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
Files / ownership
Interfaces / compatibility
Acceptance
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.