45972 backend [ templates ] Auto-cleaning of broken links to fields in templates#201
Conversation
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit ba29330. Configure here.
| ...template, | ||
| wfNameTemplate: removeInvalidReferences(template.wfNameTemplate, validKickoffApiNames, WF_NAME_SYSTEM_VARS), | ||
| tasks: cleanedTasks, | ||
| }; |
There was a problem hiding this comment.
Cleanup reorders tasks by number
Medium Severity
cleanTemplateReferences sorts tasks by number and returns them in that order. Because cleanup is now invoked on every tasks/kickoff change in handleChangeTemplateField and in the saga's merge path, any operation that updates tasks while the array is not already strictly sorted by number (e.g., during reorder/move flows before numbers settle) will silently reorder the array stored in Redux, potentially fighting with in-progress reorder logic.
Reviewed by Cursor Bugbot for commit ba29330. Configure here.
| ruleTarget: 'task started', | ||
| }; | ||
| } | ||
| } |
There was a problem hiding this comment.
DueDate fallback may produce invalid preposition combination
Low Severity
When a task's rawDueDate references an invalid field, the cleanup switches ruleTarget to 'task started' but preserves rulePreposition. If the original preposition was 'before' (only valid with ruleTarget: 'field'), the result is the invalid combination 'before task started', which has no corresponding API rule mapping.
Reviewed by Cursor Bugbot for commit ba29330. Configure here.
…972__auto_cleaning_broken_links


1. Description (Problem)
When editing a workflow template, after a user deletes a field from the Kick-off Form or a task's Output Fields, references to that field (
{{field-xxx}}) continued to appear in:Where it manifested: Template editing screen (
/templates/edit/:id). Especially noticeable when steps were expanded — stale{{field-xxx}}references remained visible until a full page reload (F5). Collapsed steps cleaned up correctly.2. Context
cleanTemplateReferenceswas already implemented and called insidemapTemplateRequestto clean data before sending to the backend. However, the cleanup result was not synchronized back to the Redux store or Lexical editors.handleChangeTemplateFieldstored the template without cleanupdefaultValue(one-time initialization), doesn't react to prop updates3. Solution
Two-level fix:
Optimistic cleanup in Redux — call
cleanTemplateReferencesimmediately whenkickoffortaskschange inhandleChangeTemplateField, before writing to the store. The user sees results instantly.Lexical editor synchronization — added
replaceContentmethod toRichEditorthat programmatically updates Lexical content. AddedlastEmittedValuetracking +useEffecttoInputWithVariablesandTaskDescriptionEditor, so whenvaluechanges externally (from Redux cleanup), the editor updates its DOM.4. Implementation Details
Changed Files
utils/template.tscleanTemplateReferencesfunction to strip invalid{{field-xxx}}references from task names, descriptions, conditions, performers, due dates, wfNameTemplateutils/__tests__/template.test.tscleanTemplateReferences— 6 test casesTemplateEdit.tsxcleanTemplateReferences, call onfield === 'kickoff' || field === 'tasks'RichEditor/types.tsreplaceContent(markdown: string): voidtoIRichEditorHandleRichEditor/RichEditor.tsxreplaceContentimplementation viaapplyMarkdownToEditorInputWithVariables.tsxlastEmittedValueref +useEffectfor external sync +handleChangewrapperTaskDescriptionEditor.tsxlastEmittedValue+useEffect+wrappedHandleChangeKey Decisions
cleanTemplateReferencesis idempotent — safe to call twice (called in both UI andmapTemplateRequest)lastEmittedValueref distinguishes external prop changes from user input, preventing cursor jumps during regular editingreplaceContentusesapplyMarkdownToEditorwith'history-merge'tag to avoid polluting the undo stack5. What to Test
5.1 Preconditions
/templates/createor/templates/edit/:id5.2 Positive Scenarios
Scenario 1: Delete kickoff field — collapsed steps
{{field-xxx}}reference disappears from the step name instantly, no reload neededScenario 2: Delete kickoff field — expanded steps
{{field-xxx}}reference disappears from both the Step name field (inside RichEditor) and DescriptionScenario 3: Delete task output field
Scenario 4: System variables are preserved
{{workflow-starter}}in Step name or Description{{workflow-starter}}remains intactScenario 5: Workflow name cleanup
{{date}},{{template-name}}preservedScenario 6: Enable Template after cleanup
5.3 Negative Scenarios and Edge Cases
{{field-xxx}}, after cleanup the name should becomeStep N(fallback)5.4 Verification Points
{{field-xxx}}does not appear after field deletionPATCH /templates/:idrequest body has no invalid{{field-xxx}}intasks[].nameandtasks[].description5.5 API Checks
/api/templates/:id—tasks[].name,tasks[].description,wf_name_templatemust not contain references to deleted fields/api/templates/:id— after saving, response should contain cleaned data5.6 What Was NOT Tested
6. Affected Areas (Dependencies)
RichEditor(replaceContentadded)replaceContentis not called unnecessarily; regular text editing works as beforeInputWithVariables(sync added)TaskDescriptionEditor(sync added)cleanTemplateReferencesmapTemplateRequest(save flow),handleChangeTemplateField7. Refactoring
Minimal refactoring within the task:
handleChangeTemplateField, variablenewWorkflowsplit intoupdatedWorkflow+newWorkflow(cleanup result) for clarityhandleChangeinInputWithVariablesrenamed from directonChangepassthrough to wrappedhandleChange/wrappedHandleChangeAdditional checks:
8. Commits
756ed515—45972 feat(template): auto-cleanup broken field references on save2ae1cb08—45972 fix(template): sync editor UI after field reference cleanup9. Release Notes
Template Editor: Fixed stale field references (
{{field-xxx}}) remaining visible in step names and descriptions after deleting kickoff or output fields. References are now cleaned up instantly in the UI without requiring a page reload.Note
Medium Risk
Updates template persistence and editor syncing logic; incorrect cleanup or editor replacement behavior could unintentionally remove user-entered
{{...}}tokens or affect undo/history in the Lexical editor.Overview
Fixes stale
{{field}}references in the template editor by auto-removing references to deleted kick-off/task fields from workflow name templates, step names/descriptions, conditions, performers, and field-based due dates.cleanTemplateReferencesis now applied optimistically whenkickoff/tasksare changed (both inTemplateEditand thepatchTemplatesaga), andmapTemplateRequestalso cleans before sending to the API; new unit tests cover the cleanup behavior.Adds an imperative
replaceContent(markdown)API toRichEditorand wiresInputWithVariablesandTaskDescriptionEditorto call it when theirvalueprop changes, preventing Lexical editors from getting stuck with old content after Redux-driven cleanup.Reviewed by Cursor Bugbot for commit abbb4d7. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
1. Description (Problem)
When editing a workflow template, after a user deletes a field from the Kick-off Form or a task's Output Fields, references to that field (
{{field-xxx}}) continued to appear in:Where it manifested: Template editing screen (
/templates/edit/:id). Especially noticeable when steps were expanded — stale{{field-xxx}}references remained visible until a full page reload (F5). Collapsed steps cleaned up correctly.2. Context
cleanTemplateReferenceswas already implemented and called insidemapTemplateRequestto clean data before sending to the backend. However, the cleanup result was not synchronized back to the Redux store or Lexical editors.handleChangeTemplateFieldstored the template without cleanupdefaultValue(one-time initialization), doesn't react to prop updates3. Solution
Two-level fix:
Optimistic cleanup in Redux — call
cleanTemplateReferencesimmediately whenkickoffortaskschange inhandleChangeTemplateField, before writing to the store. The user sees results instantly.Lexical editor synchronization — added
replaceContentmethod toRichEditorthat programmatically updates Lexical content. AddedlastEmittedValuetracking +useEffecttoInputWithVariablesandTaskDescriptionEditor, so whenvaluechanges externally (from Redux cleanup), the editor updates its DOM.4. Implementation Details
Changed Files
utils/template.tscleanTemplateReferencesfunction to strip invalid{{field-xxx}}references from task names, descriptions, conditions, performers, due dates, wfNameTemplateutils/__tests__/template.test.tscleanTemplateReferences— 6 test casesTemplateEdit.tsxcleanTemplateReferences, call onfield === 'kickoff' || field === 'tasks'RichEditor/types.tsreplaceContent(markdown: string): voidtoIRichEditorHandleRichEditor/RichEditor.tsxreplaceContentimplementation viaapplyMarkdownToEditorInputWithVariables.tsxlastEmittedValueref +useEffectfor external sync +handleChangewrapperTaskDescriptionEditor.tsxlastEmittedValue+useEffect+wrappedHandleChangeKey Decisions
cleanTemplateReferencesis idempotent — safe to call twice (called in both UI andmapTemplateRequest)lastEmittedValueref distinguishes external prop changes from user input, preventing cursor jumps during regular editingreplaceContentusesapplyMarkdownToEditorwith'history-merge'tag to avoid polluting the undo stack5. What to Test
5.1 Preconditions
/templates/createor/templates/edit/:id5.2 Positive Scenarios
Scenario 1: Delete kickoff field — collapsed steps
{{field-xxx}}reference disappears from the step name instantly, no reload neededScenario 2: Delete kickoff field — expanded steps
{{field-xxx}}reference disappears from both the Step name field (inside RichEditor) and DescriptionScenario 3: Delete task output field
Scenario 4: System variables are preserved
{{workflow-starter}}in Step name or Description{{workflow-starter}}remains intactScenario 5: Workflow name cleanup
{{date}},{{template-name}}preservedScenario 6: Enable Template after cleanup
5.3 Negative Scenarios and Edge Cases
{{field-xxx}}, after cleanup the name should becomeStep N(fallback)5.4 Verification Points
{{field-xxx}}does not appear after field deletionPATCH /templates/:idrequest body has no invalid{{field-xxx}}intasks[].nameandtasks[].description5.5 API Checks
/api/templates/:id—tasks[].name,tasks[].description,wf_name_templatemust not contain references to deleted fields/api/templates/:id— after saving, response should contain cleaned data5.6 What Was NOT Tested
6. Affected Areas (Dependencies)
RichEditor(replaceContentadded)replaceContentis not called unnecessarily; regular text editing works as beforeInputWithVariables(sync added)TaskDescriptionEditor(sync added)cleanTemplateReferencesmapTemplateRequest(save flow),handleChangeTemplateField7. Refactoring
Minimal refactoring within the task:
handleChangeTemplateField, variablenewWorkflowsplit intoupdatedWorkflow+newWorkflow(cleanup result) for clarityhandleChangeinInputWithVariablesrenamed from directonChangepassthrough to wrappedhandleChange/wrappedHandleChangeAdditional checks:
8. Commits
756ed515—45972 feat(template): auto-cleanup broken field references on save2ae1cb08—45972 fix(template): sync editor UI after field reference cleanup9. Release Notes
Template Editor: Fixed stale field references (
{{field-xxx}}) remaining visible in step names and descriptions after deleting kickoff or output fields. References are now cleaned up instantly in the UI without requiring a page reload.Changes since #201 opened
cleanTemplateReferencesutility function to setsourceIdto null and changeruleTargetto 'task started' for tasks with invalid field-based due date references [abbb4d7]patchTemplateSagasaga for task and kickoff field changes [abbb4d7]cleanTemplateReferencesfunction to preserve condition rules with falsyrule.fieldvalues (including empty strings), and rules whererule.fieldTypeis 'task' or 'kickoff', in addition to rules with valid field references [85f7aa2]cleanTemplateReferencesutility to preserve leading and trailing whitespace when removing invalid template variable references and to return empty strings for task names instead of defaulting to 'Step N' format [ba29330]template.test.tsto validate thatcleanTemplateReferencespreserves trailing spaces and allows empty task names [ba29330]InputWithVariablescomponent to track and compare raw prop values instead of escaped values for change detection and editor content updates [42f421d]