Issue #3608215: Consolidate recovery/transition behind StatusTransitions and fix the RC-audit concurrency bugs
Decomposes the recovery/transition layer and fixes the concurrency bugs the release-candidate audit found. Closes #3608219 (the behavioral bugs) and delivers the core of #3608215 (the shared guarded transition primitive); the further class splits (a core execution service, InstanceRecovery/IncidentManager/JoinCoordinator, the low-risk TimeoutScheduler/VariableStore extractions) are tracked in #3608236.
Each commit is self-contained; refactor and behavioral fixes are separated.
Refactor
- Extract
StatusTransitions(a6f893d): the guarded conditional-write status flip becomes a sharedorchestra.status_transitionsservice, injected into the engine, the timeout sweep and the incident actions, so every contended flip goes through one implementation. Behavior-preserving.
Behavioral fixes (all with the guard routed through the shared primitive)
- RC-H2 (
e6bdcb5, HIGH):TimeoutSweep::fireInnerdouble-fired a timeout whose deadline was already cleared (a concurrent fire); it now returns instead of falling through. Regression test. - M1 (
02e286a):fireTimerInnerdid unguarded load-then-save flips; add a fire claim, route the consume through the primitive, current-read the task status, guard the recurring re-arm. - RC-H1 + M3 + M6 (
059485b, HIGH): the incident actions had no concurrency claim (two operators double-executed a branch); claim the incident open-to-resolved through the primitive, make retry/resume atomic, resolve open incidents on teardown. Regression test. - M2 + M4 (
e4aac5b): migration re-derives live tokens (a token created after planning was stranded); the stalled-recovery sweep gains a persisted cursor so permanently-stuck instances do not starve the batch. - M5 (
595e461): areconcileTerminalTokenscron backstop cancels live tokens orphaned on a terminal instance by an advance that finished after teardown.
Testing
Full orchestra kernel suite green (183 tests); two new regression tests (RC-H1, RC-H2) fail on the old code and pass with the fix; phpcs and phpstan (level 1) clean. Found by a release-candidate audit of the changes since 1.0.0-alpha8.