Deleting a board or stack left the workflow row pointing at nothing: Deck
keeps the orphaned cards readable until its DeleteCron purges them, so the
run kept failing on the move every few minutes, silently and forever.
- The controller now rejects boards and stacks that do not exist (or are not
the user's) on create and update, so no new broken row can be stored.
- The settings list marks affected workflows in red instead of showing a row
that looks healthy.
- The background job disables such a workflow and mails its owner once. The
mail ignores the notifyEmail flag: that one is about moved cards, this is a
notice that the automation stopped. It stays a one-off because a disabled
workflow is no longer picked up.
The check deliberately goes through StackService::findAll() rather than
BoardService::getUserBoards(): Deck injects the current user into BoardService
as a string frozen at construction, so in the job -- one process, many users,
a cached BoardService -- it would answer for the wrong user or for none, and
every workflow on the instance would have been disabled. findStackIds()
returns null only for a genuinely missing or forbidden board and throws for
anything else, so a Deck outage skips the workflow instead of killing it.
Ports the standalone due-date cron script into a proper Nextcloud 34
app with a personal-settings UI, per-user workflow configuration
(source/target stack, assigned-user and label filters, email
notification), a TimedJob background runner, and Gitea CI pipelines.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>