rayhpeng e876cfac3a fix(schedule): retain the active slot across post-launch failures
Port the #4504 retention semantics (#4452 duplicate-execution fix) into
the hexagonal dispatch path. Once launch() returns -- or raises without
being able to say whether a run started -- a live run may exist, so
bookkeeping failures must not release the task's single active slot:

- The two post-launch writes are best-effort: failures are logged and
  surfaced on DispatchResult.error while the outcome stays LAUNCHED.
- New LaunchIndeterminateError expresses main's launch_succeeded-before-
  unpack semantics at the port boundary: the adapter raises it when the
  side effect may have happened but the identity is unknown, and the
  service retains the slot with run_id=None.
- LaunchFailedError is narrowed to "the adapter is CERTAIN no run
  started", since that path releases the slot.

Regression tests ported from tests/test_scheduled_task_service.py.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 16:44:10 +08:00
..