6 Commits
Author SHA1 Message Date
Lauri Ojansivu f735eba194 Rules: assignee actions, an optional title, and remove-all members that removes them.
#4294 asked for four things. Several triggers and actions, rule variables,
renaming in place and rule import/export were built earlier; two parts were
still missing.

- "I want the assignee to be the creator of the card by default": rules had
  member actions only. Add and Remove assignee actions take a username, a
  {creator}/{assignees}/{members} token, the user who triggered the rule, or
  "*" for every assignee, and write card.assignees. They are durable through
  Sync like the member actions, with the joinAssignee/unjoinAssignee
  activities the assignees hook writes.
- "give a Rule a default name": Add Rule with an empty title showed an
  error, and rules.createRule stored the literal "Rule". The title is now
  optional; the rule is named after its trigger and action ("When ...,
  then ..."), and the rules list renames it in place.

Fixing this found that "Remove all members from the card" iterated the
card's assignees while removing from its members: an earlier fix believed
assignMember wrote assignees, but both definitions in models/cards.js write
members. It removed only members who were also assignees. Remove-all now
iterates card.members, in the ordinary and the durable path, and the guards
that pinned the wrong collection now read models/cards.js to decide.

New text is added in English, pending Transifex.

Thanks to xeruf and xet7 !

Fixes #4294.
2026-10-08 11:31:12 +03:00
Lauri Ojansivu 12e98cdbfe Let a rule's later actions follow its card to another board, so a cross-board move need not be last.
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 !
2026-10-03 00:22:57 +03:00
Lauri Ojansivu ab611bcd79 Make rule links to another board durable when both boards opted into Sync effects.
As decided on 2026-10-02, a linkCard rule that links onto another board
keeps durable Sync only when that board has Sync effects enabled too and
the actor may write there. Eligibility now follows every board such a
rule reaches, because the link's creation activity runs that board's own
rules through the same stored stages; any action there without a durable
adapter keeps the source board on direct Sync. The runner checks the
destination again before each write.

A card-field rule acting on a linked card now writes the card it links
to, as the ordinary setters do through getRealId. Before, the durable
command refused a linked card, so a durable link followed by such a rule
would have failed its Sync run on every replay.

Thanks to xet7 !
2026-10-02 18:04:14 +03:00
Lauri Ojansivu 0395989d01 Make the member rule actions durable in Sync.
addMember and removeMember join the durable card-field actions. The people
are resolved at capture exactly as performAction resolves them - the acting
user for the #2522 option, username tokens through buildRuleVars (#4294),
and the card's assignees for "remove every member" (#2674) - by a new
RulesHelper.resolveMemberTargets beside it, and saved with their usernames,
so a replay acts on the same people. The ordinary action's one $addToSet or
$pull per person becomes one planned change of `members`, with a joinMember
or unjoinMember activity for each person who really joined or left, as the
members hook writes it, and one members History row.

The new helper phrases its conditions so the source guards that locate
performAction's member branch still find it, and the recording-isolation
test now uses assignees as its example of a field that is never deferred.
Unit tests cover resolution being saved, joins and leaves, nobody resolved
and tampered targets; the full-app durable Sync test adds a member to every
created card through a rule - one joinMember activity and one members
History row. 565 server tests pass.

Thanks to xet7 !
2026-09-30 12:09:24 +03:00
Lauri Ojansivu ad4ccd5c38 Make the date rule actions durable in Sync.
setDate, updateDate, setDateRelative and removeDate join the durable
card-field actions. Their semantics follow the ordinary actions exactly:
setDate only fills a date that is unset, updateDate always sets it,
setDateRelative offsets from "now", removeDate unsets - with "now" fixed at
the command's capture time, so a replay makes the same change.

A full-app probe of the ordinary writes showed what they record: a dates
History row (added, edited or removed) and the timing hook's a-<field>
activity with username, card title, time key and the old value ('' when
there was none) - and no timeValue when the date is removed. The command
plans exactly that. The timing hook in server/models/cards.js gains a
'timing' deferral kind so the saved activity is written instead of the
hook's own, never both; the runner can now unset a field as well as set it.

Unit tests cover all four actions, setDate on a filled date, the timing
activity's shape for set and unset, and a replay-stable relative date; the
full-app durable Sync test gives every created card a due date through a
rule - one a-dueAt activity and one dates History row. 565 server tests pass.

Thanks to xet7 !
2026-09-30 12:05:03 +03:00
Lauri Ojansivu ff86170f35 Make colour, label and completion rule actions durable in Sync.
Boards with any rule action other than email and archive stayed on direct
Sync. Six more now take the durable path: setColor, addLabel, removeLabel,
removeAllLabels, markCardComplete and markCardIncomplete.

Each rule invocation saves one command (server/lib/syncRuleCardCommand.js)
with its card field's value before and after the action - with the ordinary
action's semantics: $addToSet/$pull for single labels, a missing field stays
missing on removeLabel, 'white' is stored as null - and the effects the
ordinary hooks would write: one History row for the field and, for
addLabel/removeLabel, the addedLabel/removedLabel activity only when the
label really changed (removeAllLabels writes none, as ordinarily). The runner
applies the saved change conditionally with ordinary History deferred for
that one write, then writes the planned History rows and activities, each
activity delivered durably with its own rules and notifications, and returns
the invocation only when all of it is confirmed. Replays use the saved
command, never the rule's current configuration.

The History planner is parameterized by allowed fields (Sync's own, or these
three), and the recording deferral covers labelIds, color and dueComplete.
Unit tests cover every action, the effects, absent-versus-null selectors and
tampering; the full-app durable Sync test labels every created card through
a rule - one History row, one activity, nothing more on replay. The rule
selection test's example of an action without an adapter is now
moveCardToTop. 565 server tests pass.

Thanks to xet7 !
2026-09-30 12:00:47 +03:00