fix(backend): apply auto-tag rule to existing files on creation

CreateRule accepted apply_to_existing but ignored it, so enabling the
checkbox while creating a rule never retroactively tagged files already
carrying the when-tag — only activating an existing rule did. Extract the
retroactive expansion into TagRuleRepo.ApplyToExisting (reused by SetActive)
and call it from CreateRule when the rule is active, inside one transaction
so a file is never left half-tagged. Mirrors SetRuleActive semantics,
including following only active downstream rules.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-15 18:09:31 +03:00
parent 9937984a5a
commit 57192a49f9
4 changed files with 126 additions and 13 deletions
+9 -4
View File
@@ -604,10 +604,14 @@ WHERE when_tag_id = $1 AND then_tag_id = $2`
if !active || !applyToExisting {
return nil
}
return r.ApplyToExisting(ctx, whenTagID, thenTagID)
}
// Retroactively apply the full transitive expansion of thenTagID to all
// files that already carry whenTagID. The recursive CTE walks active rules
// starting from thenTagID (mirrors the Go expandTagSet BFS).
// ApplyToExisting retroactively applies the full transitive expansion of
// thenTagID to all files that already carry whenTagID. The recursive CTE walks
// active rules starting from thenTagID (mirrors the Go expandTagSet BFS), so
// inactive downstream rules are not followed. Idempotent via ON CONFLICT.
func (r *TagRuleRepo) ApplyToExisting(ctx context.Context, whenTagID, thenTagID uuid.UUID) error {
const retroQuery = `
WITH RECURSIVE expansion(tag_id) AS (
SELECT $2::uuid
@@ -624,8 +628,9 @@ CROSS JOIN expansion e
WHERE ft.tag_id = $1
ON CONFLICT DO NOTHING`
q := connOrTx(ctx, r.pool)
if _, err := q.Exec(ctx, retroQuery, whenTagID, thenTagID); err != nil {
return fmt.Errorf("TagRuleRepo.SetActive retroactive apply: %w", err)
return fmt.Errorf("TagRuleRepo.ApplyToExisting: %w", err)
}
return nil
}