Skip to content

feat(automations): group-aware tag actions (GroupID + canonical/all-members apply modes) #2256

Description

@nitrobass24

Problem

Group awareness is unevenly distributed across automation actions:

  • Delete has full group semantics — member expansion via MembersForHash, once-per-group dedupe (processedGroupKeys), and verify_overlap ambiguity safety (internal/services/automations/service.go).
  • Category has GroupID and IncludeCrossSeeds (models/automation.go CategoryAction).
  • Tags have neither: TagAction (models/automation.go:943) carries no GroupID, and tag changes are computed strictly per-hash (tagChanges[hash], service.go tag block).

Consequence for cross-seed libraries: any tag rule whose condition qualifies a release ends up tagging every copy of that release — e.g. a rule meant to mark releases for review queues N identical entries, one per tracker copy. There's no way to say "tag this release once" or "keep tags consistent across a release's copies."

Proposal

Extend TagAction with the same group vocabulary the other actions use:

type TagAction struct {
    ...
    GroupID   string `json:"groupId,omitempty"`   // resolve members like Category/Delete
    GroupMode string `json:"groupMode,omitempty"` // "", "canonical", "members"
}
  • canonical — apply the tag change to exactly one member per group per run. Deterministic pick: first hash in the group's sorted member list (the ordering buildGroupIndex already guarantees), with a once-per-group guard mirroring delete's processedGroupKeys. Use case: queue/marker tags that must exist once per release regardless of copy count.
  • members — expand the tag change to all group members when the trigger torrent matches. Use case: propagating state tags (e.g. archived) so every current copy of a release reflects it, keeping group state visible on each torrent in the UI.
  • Empty/absent GroupMode (or GroupID) keeps today's per-torrent behavior — fully backward compatible.

Safety

  • Reuse the ambiguity handling from group-aware delete: for contentPath-keyed groups with ContentPath == SavePath, honor the group definition's AmbiguousPolicy (verify_overlap with MinFileOverlapPercent, or skip) before expanding/deduping, so flat layouts can't smear tags across unrelated torrents.
  • DeleteFromClient/managed-tag reset semantics (collectManagedTagsForClientReset) should treat group-applied tags identically to per-torrent ones.

Notes

Complements GROUP_HAS_TAG (#2255): that issue makes group tag state readable in conditions; this one makes it writable by actions. Together they let tag state converge at the release-group level instead of per copy, but each stands alone.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions