Since #1759 a card copied or moved to another board brings its labels: a
label that board lacks is created there when the actor is its admin. The
stored (durable) rule copyCard, move to another board and move-all onto
another board still matched by name only and dropped the rest, so the
durable copy or move was no longer the ordinary one - the parity three
server tests check - and the card lost a label a direct copy would keep.
The durable plans now use the same planCrossBoardLabels with the rule's
actor. The labels to create are saved in the command (copy) or move
(single move, and each move-all unit), so a replay creates nothing twice:
each is pushed only when no label with its id is on that board yet, before
the card that names it is written. In a move-all, a label created for one
card is that board's label for the next, as when Card.move moves them one
by one. Commands saved before this, without the labels, still validate.
The server tests now expect the created label, once across a replay, and
the ordinary copy or move finding it; the card-move test gains the negative
of a mover who is not the new board's admin, where the label is dropped and
its activity removed. The command tests cover the new field, its validation
and an older command without it.
Thanks to xet7 !
The ordinary engine takes a move-all's list from the board the rule's card
is on now (RulesHelper.ruleBoards); the durable move-all onto another board
took it from the plan's board, so it could not follow a move to another
board. It now takes the list from where the card is, saves that board as
`fromBoard` - only when it is not the plan's, so saved commands still
validate - and each unit leaves from it. A move-all onto the board the card
is already on is refused. Only an email now keeps such a plan on direct Sync.
A server test moves the card from A to B, then every card of B's list to C,
durably and with the ordinary engine, with the same result.
Thanks to xet7 !
A rule whose card had been moved to another board could not move it on to
yet another one through durable Sync: the cross-board move command took
the card from the plan's board only. It now takes it from where it is -
the plan's board, or where an earlier move of the same plan took it
(ruleCardNow) - and saves that board as `fromBoard`, written only when it
is not the plan's, so every command saved before still validates. The
board the card leaves, its dependencies and the deferred hook records
follow the saved move's own before-place. A move onto the board the card
is already on is refused.
Such a further move may now follow a move to another board; every board
the chain reaches must have opted in, as the first. A move-all onto
another board still may not follow, since it moves a list of the rule's
board. A server test runs A to B to C and a later colour, durably and with
the ordinary engine on a twin card, and refuses the chain while C has not
opted in.
Thanks to xet7 !
The ordinary rule engine reads the card by id for every action, so the
actions after a move to another board act on the card there. The
durable commands that can do the same now find the card where a saved
move of the same plan put it, and record their writes, History rows and
activities on that board: card fields, checklist toggles, adding and
removing checklists, linking, and the title variables of a new card or
swimlane. A copy refuses, as the ordinary copy does. A move to another
board is no longer required to be the plan's last action. It is lifted
when every action that can follow it in a plan, in its own rule or in a
rule of the same trigger type, is one of these. Moves, sorting and
moving all cards are resolved against the board the card left, and
archiving cascades to child cards there, so with any of those the board
keeps direct Sync.
Thanks to xet7 !
Durable delivery identified every activity by a card and a list, so a
rule's createSwimlane activity, which names neither, and a cross-board
move's moveCardBoard activity, which names no list, were written once
but delivered the ordinary way: their notifications and webhooks were
not replayed after a crash. Durable delivery now takes activities
without a list, and without a card. Recipients and webhook senders are
checked against the board, plus the card when one is named. A card or
list the activity does not name is refused. An assigned-only member
never receives a board-level event, since its scope cannot be checked
against their assignments. The rule stage has nothing to run for these
types, because no trigger exists for them, and it refuses a list-less
activity whose type has one.
Thanks to xet7 !
As decided on 2026-10-02, a card or swimlane copied or moved to another
board now takes that board's sprint and release of the same name when
exactly one matches: a planned or active sprint, or a release that is
not cancelled. Otherwise they are dropped, as before. Past sprints and
backlog rank always go. This applies to card and swimlane copies,
subtasks copied with their parent, the server hooks behind every move,
and the durable rule copy and move. A move that sets the Scrum revision
itself, as the durable rule move does, is left alone by the hook.
Thanks to xet7 !
A card or swimlane moved to another board kept the sprint, past
sprints, release and backlog rank of the board it left, pointing at
that board's records. A copy there already dropped them. Moves now do
the same, keeping the issue type, acceptance criteria and swimlane
purpose. Server hooks do it, so client, REST and rule moves are all
covered. Scrum fields are not the client's to write, so the
client-callable move does not write them itself. The durable
cross-board rule move writes the same result.
Thanks to xet7 !
A moveAllCardsInList rule onto another board now saves one unit per
card of the list. Each unit is a whole move to another board, with the
same mappings and records as a single rule move there. As in the
ordinary action, each card keeps its sort and lands in that board's
default swimlane. Like a single move to another board, it must end its
rule plan, because the rule's own card may be among those moved. The
rule guard follows a card to where its unit put it. The single-move and
move-all runners share one apply step.
Thanks to xet7 !
The saved command for a rule move onto another board is written
(server/lib/syncRuleMoveBoardCommand.js). It plans what Card.move and
its hooks write there: labels by name, the card number, mapped custom
fields, filtered members and watchers, cleared dependencies, the
History rows, the moveCardBoard and custom-field activities, the
re-pointed label activities and the legacy undo row. It accepts only
the last action of its rule plan. The History planner gains those
fields, and the recording scope can defer the dependencies row and the
label-activity hook, which runs while the card is still on its old
board. Nothing uses the command yet: such a rule still keeps its board
on direct Sync until its runner, guard and eligibility are added.
Thanks to xet7 !