Issue #3608367: Rework the join and subprocess coordination into decide-then-apply collaborators (JoinCoordinator, SubprocessCoordinator)
Second of the two follow-ups to #3608236: WorkflowExecutor lands at 1936 lines (from 2422 after !279 (merged), 3290 after !278 (merged), 3662 originally), with the public contract unchanged throughout.
The join machinery and the subprocess coordination both call the loop and are called by it, so they could not move mechanically. They now decide rather than act:
- JoinCoordinator (
orchestra.join_coordinator) evaluates an arrival and returns a JoinOutcome value object: fire or wait, the wait deadline to arm, the siblings to consume, the surplus deadlines to clear, the merge to write, the new parent for the continuing token, and whether an early fire must tear the cohort down. The executor applies that outcome inside the same per-(instance, node) lock and transaction as before. - SubprocessCoordinator (
orchestra.subprocess_coordinator) computes the SubprocessResume payload for a terminal child (mapped result, routable status, or NULL when a retry re-launch was armed or the parent must stay parked) and owns the guarded retry/re-launch claims. The executor keeps only the atomic PARKED to ACTIVE claim and the resume call. - FlowEvaluator (
orchestra.flow_evaluator) hosts liveFlows() and successors(), shared by the outgoing routing and the join's incoming-arc matching, so the OR-join and the splits keep one definition of "live" without duplication. - Two small policy relocations finish the job: the dead-letter retry policy moved onto IncidentStore, and the execution-mode config read onto DefinitionResolver. The executor no longer depends on the join, split, flow-condition or config-factory services at all.
Also included:
_PHPUNIT_CONCURRENT: '1'in .gitlab-ci.yml: the serial phpunit job had passed 17 minutes; this runs it through core's run-tests.sh with concurrent threads.- A test-robustness fix in OrchestraCmFeatureAuthoringTest: the post-save assertion now waits for the destination page (it raced the non-AJAX navigation under CPU load; diagnosed while verifying this MR, unrelated to the rework).
Verified locally: the dedicated join and subprocess matrix (JoinTest, JoinMergeTest, LoopJoinTest, ForkCohortTest, the three discriminator suites, JoinCancellationTest, JoinStallRecoveryTest, SignalIdempotencyTest, SubprocessTest: 30 tests), root kernel (183), unit plus all submodule kernel (328), all Functional in Docker (37), FunctionalJavascript (6), phpcs, cspell, phpstan parity.