Skip to content

0392e8fe: a renamed Template definition adopts the Task it previously named - #323

Merged
alexeygrigorev merged 1 commit into
mainfrom
fix/template-update-dedup
Nov 4, 2026
Merged

alexeygrigorev merged 1 commit into
mainfrom
fix/template-update-dedup

Conversation

@alexeygrigorev

Copy link
Copy Markdown
Member

Task: 0392e8fe (template-update duplicates manual tasks — sponsor-stats doubling on newsletter cards)

Problem

Applying a Card Template update whose task definition ref changed planned add for the new ref and skip-removed for the old one (backend/src/templates/cardTemplateUpdates.ts, the !current branch + the retirement loop). The Card kept a retired-skipped copy of the original Task beside a fresh open copy — the sponsor-stats tasks doubled on the newsletter cards. The newsletter Card sync (syncIssueSlotTasks) applies Template updates without a human-reviewed preview, so every slot-booking change silently re-ran it.

The existing manual-task adoption matched only Tasks with no Template provenance (!(task.templateId && task.templateTaskRef)), so a Task the Template itself had named under the old ref never matched.

Verified against origin/main with a reproduction: renaming sponsor-stats → sponsor-stats-weekly and applying yields two Collect sponsor stats tasks (one skipped, one new id).

Fix

The plan now adopts the retiring Task whose normalized description (description.trim().toLowerCase() — the same matching key the manual adoption uses) names the renamed definition, when the description is unique on both sides:

  • an open Task takes the renamed definition's fields under its old id (update with adoptedRef),
  • a resolved one keeps its resolution (adopt),
  • a Task the machinery itself skipped for the removed ref is restored (restore),
  • a Task an earlier update retired yields to the live Task (so already-affected cards converge on the next rename instead of accumulating a third copy),
  • definitions that renamed the work to different wording still add and retire as before; repeated instances beyond a count are never adopted.

Dependency resolution, the acyclicity guard, and the apply transaction register the adopted Task's id under the new ref. Genuinely distinct tasks (different descriptions, ambiguous duplicates) are preserved.

Tests

Six new cases in backend/tests/card-template-updates.test.ts (renamed definition adoption): adopt + no-op re-apply (one task, same id), in-place update when content changed too, completed Task keeps its resolution, distinct wording still adds and retires, manual adoption then rename keeps one Task, and the retired-original vs live-copy preference. Four of them fail on origin/main.

NODE_ENV=test FRONTEND_ROOT=../frontend SKIP_AUTH=true node --import tsx --test --test-force-exit tests/card-template-updates.test.ts → 29 pass. Related suites (api-card-template-updates, template-task-dependencies, template-runtime-edits, newsletter-issue-cards, authored-templates) → 103 pass. Full backend tsc --noEmit (src, tests, scripts) clean.

Operator cleanup (informational — NOT executed here)

No prod data was touched. Cleanup commands for the already-duplicated sponsor-stats tasks are collected in the task report; identification of affected cards needs prod data.

… named

Applying a Template update whose definition ref changed planned add for
the new ref and skip-removed for the old one: the Card kept a retired
copy of the original Task beside a fresh one, doubling the work item
(the sponsor-stats tasks doubled on the newsletter Cards). The manual
adoption matched only Tasks with no Template provenance, so a Task the
Template itself had named never matched.

The plan now adopts the retiring Task whose normalized description names
the renamed definition - the same matching key the manual adoption uses
- when the description is unique on both sides: an open Task takes the
renamed definition's fields under its old id, a resolved one keeps its
resolution, and a Task the machinery itself skipped for the removed ref
is restored. A Task an earlier update retired yields to the live Task,
and definitions that renamed the work to different wording still add and
retire as before. Repeated instances are never adopted.

Dependency resolution, the acyclicity guard, and the apply transaction
register the adopted Task's id under the new ref.
@alexeygrigorev
alexeygrigorev merged commit 8f1e98b into main Nov 4, 2026
4 checks passed
@alexeygrigorev
alexeygrigorev deleted the fix/template-update-dedup branch November 5, 2026 15:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant