Refuse workflows that move cards in a loop
Build package / xml-lint (push) Successful in 1m4s
Build package / php-lint (8.5) (push) Successful in 4m0s
Build package / unit-tests (push) Successful in 4m13s
Build package / php-lint (8.4) (push) Successful in 4m34s
Build package / package (push) Successful in 1m38s

Every enabled workflow is an edge "source stack -> target stack";
LoopDetector finds circles in that graph, a workflow whose source is its
own target being the shortest. Without a check, two workflows A -> B and
B -> A move the same cards back and forth on every run, even within a
single run, with a notice for each move. The trigger date is ignored on
purpose: by start date one way and by due date the other, a card with
both dates passed still circles. Chains stay allowed.

The controller rejects saving an enabled workflow that would close a loop
and names the workflows involved. Workflows stored before this check are
skipped by the runner (logged, not disabled) and flagged in the settings
list via a new `loop` field from index().
This commit is contained in:
Patrick Niebeling committed 2026-10-01 13:47:32 +02:00
1 parent 8268f1068d
commit 9e1a15bab0
8 files changed
+251 -5

No files matched your search

+14
View File
@@ -67,11 +67,25 @@ class WorkflowRunner {
return;
}
// The controller refuses to store a loop, but workflows saved before
// that check — including ones whose source is their own target — can
// still form one. They would move the same cards on every run, with a
// notice each time, so they are left out until the user fixes them;
// the settings page marks them.
$looping = LoopDetector::findLoopingWorkflows(LoopDetector::edgesOf($workflows));
$this->pendingNotices = [];
$byUser = [];
$owners = [];
foreach ($workflows as $workflow) {
$this->followBoardOwner($workflow, $owners);
if (in_array((int)$workflow->getId(), $looping, true)) {
$this->logger->warning('Skipping workflow {id}: it moves cards in a loop together with other workflows', [
'app' => 'workflow_deck_automation',
'id' => $workflow->getId(),
]);
continue;
}
$byUser[$workflow->getUserId()][] = $workflow;
}