This is an automated email from the ASF dual-hosted git repository. github-merge-queue[bot] pushed a commit to branch gh-readonly-queue/main/pr-7349-a09260a46cbfa844537b8865ff0f79754bf3daa5 in repository https://gitbox.apache.org/repos/asf/texera.git
commit 12169c2cfe5f92d0c492ae12d8eb2b53a6fec098 Author: Martin Vu <[email protected]> AuthorDate: Thu Aug 13 21:30:34 2026 +0000 fix(agent-service): delete links targeting removed input ports when shrinking input ports (#7349) ### What changes were proposed in this PR? This PR fixes a bug in `WorkflowState.updateOperatorInputPorts` where shrinking an operator's input port count left links targeting removed input ports. The method now removes any links whose target port no longer exists by calling `deleteLink`, preventing dangling links from remaining in the workflow state. A regression test was also added to verify that links targeting deleted input ports are removed when the input port count is reduced. ### Any related issues, documentation, discussions? Closes #7169 ### How was this PR tested? Added a regression test covering the case where an operator's input ports are reduced while a link targets one of the removed ports. Verified with: ```bash bun test --test-name-pattern="removes links targeting input ports that are dropped" bun test ``` Before: <img width="1716" height="1160" alt="image" src="https://github.com/user-attachments/assets/bcfd23cb-aa4b-4f8e-87c1-46f0eda3e3e4" /> After: <img width="1716" height="984" alt="image" src="https://github.com/user-attachments/assets/f9937312-80f8-423b-8ec7-d7c522319b90" /> ### Was this PR authored or co-authored using generative AI tooling? Generated-by: ChatGPT (5.5 mini) --- agent-service/src/agent/workflow-state.spec.ts | 24 ++++++++++++++++++++++++ agent-service/src/agent/workflow-state.ts | 9 +++++++++ 2 files changed, 33 insertions(+) diff --git a/agent-service/src/agent/workflow-state.spec.ts b/agent-service/src/agent/workflow-state.spec.ts index 2fd27af652..3c17b352de 100644 --- a/agent-service/src/agent/workflow-state.spec.ts +++ b/agent-service/src/agent/workflow-state.spec.ts @@ -341,6 +341,30 @@ describe("WorkflowState - updateOperatorInputPorts", () => { test("returns false for a missing operator", () => { expect(new WorkflowState().updateOperatorInputPorts("missing", 2)).toBe(false); }); + + test("removes links targeting input ports that are dropped", () => { + const state = new WorkflowState(); + + state.addOperator(makeOperator("src")); + state.addOperator( + makeOperator("op1", { + inputPorts: [ + { portID: "input-0", displayName: "Input 0" }, + { portID: "input-1", displayName: "Input 1" }, + ], + }) + ); + + state.addLink({ + linkID: "l1", + source: { operatorID: "src", portID: "output-0" }, + target: { operatorID: "op1", portID: "input-1" }, + }); + + state.updateOperatorInputPorts("op1", 1); + + expect(state.getAllLinks().map(l => l.linkID)).toEqual([]); + }); }); describe("WorkflowState - workflow content round-trip", () => { diff --git a/agent-service/src/agent/workflow-state.ts b/agent-service/src/agent/workflow-state.ts index d53692f33c..ef408eee24 100644 --- a/agent-service/src/agent/workflow-state.ts +++ b/agent-service/src/agent/workflow-state.ts @@ -189,6 +189,15 @@ export class WorkflowState { inputPorts: newInputPorts, }; this.operators.set(operatorId, updatedOperator); + + const validInputPorts = new Set(newInputPorts.map(port => port.portID)); + + for (const link of this.getAllLinks()) { + if (link.target.operatorID === operatorId && !validInputPorts.has(link.target.portID)) { + this.deleteLink(link.linkID); + } + } + this.operatorPropertyChangeSubject.next({ operator: updatedOperator }); return true; }
