Skip to content

fix(web): remove @ts-nocheck from workflow-editor and enforce strict checks#9

Merged
matheusgalvao1 merged 3 commits intomainfrom
codex/fix-workflow-editor-strict-mode
Feb 25, 2026
Merged

fix(web): remove @ts-nocheck from workflow-editor and enforce strict checks#9
matheusgalvao1 merged 3 commits intomainfrom
codex/fix-workflow-editor-strict-mode

Conversation

@matheusgalvao1
Copy link
Collaborator

Summary

  • remove @ts-nocheck from apps/web/src/app/workflow-editor.ts
  • add explicit editor state/types and class field declarations so the file compiles under TypeScript strict mode
  • add null-safety guards for key DOM/runtime references (canvas, buttons, layers, input, temp connections)
  • improve error handling for unknown exceptions in async run/resume paths

Why

tsconfig.base.json has strict: true, but @ts-nocheck disabled TypeScript checking for the largest UI module in the web app. This change restores compile-time safety for that module without changing user-facing behavior.

Validation

  • npm --workspace apps/web run typecheck
  • npm run lint
  • npm run build

Scope

  • no functional feature changes intended
  • no server/package changes; web editor typing and safety improvements only

@coderabbitai
Copy link

coderabbitai bot commented Feb 25, 2026

📝 Walkthrough

Walkthrough

The PR introduces extensive TypeScript typings and explicit method signatures across the workflow editor, adding interfaces for workflow state, nodes, ports, conditions, UI instances, and run results. It replaces many loose inline typings with stronger types (with some provisional any use), adds null-guards for DOM access, centralizes error-message handling, adjusts typed imports/dynamic loads, and hardens public APIs and internal helpers for node management, port/connection logic, condition handling, chat/log rendering, and run/resume flows. Runtime behavior is preserved.

Possibly related PRs

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely describes the main objective: removing @ts-nocheck and enforcing TypeScript strict mode checks in workflow-editor.ts, which matches the core purpose of the changeset.
Description check ✅ Passed The description comprehensively explains the changes (removing @ts-nocheck, adding type safety), the rationale (restore compile-time safety under strict mode), validation steps, and scope (no functional changes), all of which directly relate to the changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/web/src/app/workflow-editor.ts (1)

317-327: ⚠️ Potential issue | 🟠 Major

Replace broad any annotations on core editor APIs.

This change compiles, but these any signatures erase most of the strict-mode guarantees you're trying to restore. The file defines type aliases (WorkflowState, Point, Viewport, IfCondition, WorkflowNodeData) but many core methods still use untyped any parameters (state, node, connection, event, and coordinate flows are unchecked).

🔧 Tightening example
+type DropdownItem = { value: string; label: string };

-async setupDropdown(container: any, items: any, selectedValue: any, placeholder: any, onSelect: any) {
+async setupDropdown(
+  container: HTMLElement,
+  items: DropdownItem[],
+  selectedValue: string,
+  placeholder: string,
+  onSelect: (value: string) => void
+) {

-setWorkflowState(state: any) {
+setWorkflowState(state: WorkflowState) {

-screenToWorld(clientX: any, clientY: any) {
+screenToWorld(clientX: number, clientY: number): Point {

-addNode(type: any, x: any, y: any) {
+addNode(type: string, x: number, y: number) {

Per coding guidelines, "Use TypeScript strict mode everywhere" and "Use TypeScript with strict mode enabled across all apps and packages".

Also applies to: 341-348, 452-455, 875-883, 1024-1034, 1290-1296, 1558-1567, 1972-1978

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/app/workflow-editor.ts` around lines 317 - 327, The method
setupDropdown and several other core editor APIs use broad any types which
undermines strict-mode—replace those anys with concrete types: change
setupDropdown(container: any, items: any, selectedValue: any, placeholder: any,
onSelect: any) to a typed signature (e.g., container: HTMLElement | string,
items: DropdownItem[] or the editor's DropdownItem type, selectedValue: string |
number | null, placeholder: string, onSelect: (value: string | number) => void)
and use the actual Dropdown constructor type returned by getDropdownCtor() for
DropdownCtor; similarly audit and replace anys in the other affected methods
(the ones interacting with WorkflowState, Point, Viewport, IfCondition,
WorkflowNodeData, node, connection, and event flows) to use those declared
aliases (WorkflowState, Point, Viewport, IfCondition, WorkflowNodeData) or
explicit DOM/event types so the compiler enforces strict types across
setupDropdown, getDropdownCtor, and the listed API methods.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/web/src/app/workflow-editor.ts`:
- Around line 1359-1364: The code assumes effortOptions has at least one element
and dereferences effortOptions[0].value which can throw; update the logic around
effortOptions/selectedEffort in the block that builds effortOptions (using
this.modelEfforts and this.modelOptions) to first check if effortOptions.length
> 0 and only then use effortOptions[0].value, otherwise pick a safe fallback
(e.g., keep node.data.reasoningEffort unchanged, set to null/empty string, or a
defined default) so node.data.reasoningEffort is not assigned from an undefined
index; ensure references to effortOptions, selectedEffort and
node.data.reasoningEffort are guarded accordingly.

---

Outside diff comments:
In `@apps/web/src/app/workflow-editor.ts`:
- Around line 317-327: The method setupDropdown and several other core editor
APIs use broad any types which undermines strict-mode—replace those anys with
concrete types: change setupDropdown(container: any, items: any, selectedValue:
any, placeholder: any, onSelect: any) to a typed signature (e.g., container:
HTMLElement | string, items: DropdownItem[] or the editor's DropdownItem type,
selectedValue: string | number | null, placeholder: string, onSelect: (value:
string | number) => void) and use the actual Dropdown constructor type returned
by getDropdownCtor() for DropdownCtor; similarly audit and replace anys in the
other affected methods (the ones interacting with WorkflowState, Point,
Viewport, IfCondition, WorkflowNodeData, node, connection, and event flows) to
use those declared aliases (WorkflowState, Point, Viewport, IfCondition,
WorkflowNodeData) or explicit DOM/event types so the compiler enforces strict
types across setupDropdown, getDropdownCtor, and the listed API methods.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between aa979e1 and 895a278.

📒 Files selected for processing (1)
  • apps/web/src/app/workflow-editor.ts

@matheusgalvao1
Copy link
Collaborator Author

Addressed CodeRabbit feedback in commit ee04610.

What was fixed:

  • Replaced broad any signatures in the core APIs called out by review (including setupDropdown, screenToWorld, setWorkflowState, addNode, loadWorkflow, createPort, handleRunResult).
  • Added concrete constructor/config types for dynamic DS components (dropdown/split-panel/modal).
  • Added a safe fallback for empty effort options so we no longer dereference effortOptions[0] when the array is empty.

Validation rerun:

  • npm --workspace apps/web run typecheck
  • npm run lint
  • npm run build

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
apps/web/src/app/workflow-editor.ts (2)

872-877: ⚠️ Potential issue | 🟠 Major

this.approveBtn and this.rejectBtn are always null at initButtons time — these listeners are dead code.

this.approveBtn and this.rejectBtn are initialized to null (lines 265–266) and are only populated inside showApprovalMessage at lines 1854–1859, where click handlers are already wired directly. The if (this.approveBtn) guards in initButtons will never pass on the first (and only) call.

🗑️ Suggested cleanup
-        if (this.approveBtn) {
-            this.approveBtn.addEventListener('click', () => this.submitApprovalDecision('approve'));
-        }
-        if (this.rejectBtn) {
-            this.rejectBtn.addEventListener('click', () => this.submitApprovalDecision('reject'));
-        }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/app/workflow-editor.ts` around lines 872 - 877, initButtons
currently adds click listeners guarded by checks on this.approveBtn and
this.rejectBtn but those properties are null at initButtons time and are later
assigned (and already wired) in showApprovalMessage, so the listeners are dead
code; remove the redundant conditional blocks in initButtons that reference
this.approveBtn and this.rejectBtn (and their addEventListener calls) to avoid
dead branches, keeping the click wiring that exists in showApprovalMessage and
ensuring submitApprovalDecision remains called only from the handlers defined
where the buttons are actually created.

1731-1736: ⚠️ Potential issue | 🟠 Major

connection.sourceHandle (type string | undefined) assigned to ConnectionStart.handle (type string).

WorkflowConnection.sourceHandle is optional (sourceHandle?: string in @agentic/types), making it string | undefined. The ConnectionStart.handle field requires string with no undefined allowed. This assignment violates TypeScript strict mode's type safety and should be guarded with a fallback value.

Fix:

-            handle: connection.sourceHandle,
+            handle: connection.sourceHandle ?? '',
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/app/workflow-editor.ts` around lines 1731 - 1736, The assignment
to this.connectionStart sets ConnectionStart.handle (type string) from
connection.sourceHandle which is WorkflowConnection.sourceHandle (type string |
undefined); ensure you guard against undefined by providing a fallback before
assigning. Update the logic around this.connectionStart (the block that assigns
nodeId/handle/x/y) to coerce connection.sourceHandle to a definite string (e.g.,
use a default like "" or a sensible handle constant) or perform a conditional
check and set handle accordingly so ConnectionStart.handle always receives a
string value.
♻️ Duplicate comments (1)
apps/web/src/app/workflow-editor.ts (1)

1430-1452: The previous effortOptions[0] crash is now correctly guarded.

effortOptions[0]?.value ?? '' and the wrapping if (selectedEffort) guard prevent the node rendering crash when no effort options are configured.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/app/workflow-editor.ts` around lines 1430 - 1452, The code
previously crashed when no effort options existed; ensure you compute
modelEfforts and effortSource (using modelEfforts ||
this.modelEfforts[this.modelOptions[0]] || []) then build effortOptions, pick
selectedEffort via effortOptions.find(...)? .value ?? effortOptions[0]?.value ??
'' and only call this.setupDropdown (with effortDropdown, effortOptions,
selectedEffort, 'Select effort', ...) and assign data.reasoningEffort when
selectedEffort is truthy to avoid rendering with empty values; update references
to modelEfforts, effortSource, effortOptions, selectedEffort, effortDropdown,
data.reasoningEffort and this.setupDropdown accordingly.
🧹 Nitpick comments (11)
apps/web/src/app/workflow-editor.ts (11)

2082-2087: result.status !== 'paused' in the final else branch is always true.

By the time this else branch is reached, 'paused' and 'completed' and 'failed' have all been handled above. The inner check is unreachable as written.

🧹 Suggested cleanup
         } else {
             this.clearApprovalMessage();
-            if (result.status !== 'paused') {
-                this.hideAgentSpinner();
-                this.setWorkflowState('idle');
-            }
+            this.hideAgentSpinner();
+            this.setWorkflowState('idle');
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/app/workflow-editor.ts` around lines 2082 - 2087, The inner
conditional check "result.status !== 'paused'" inside the final else branch is
unreachable because 'paused', 'completed', and 'failed' are already handled
earlier; remove the redundant check and always run the cleanup logic (call
clearApprovalMessage(), hideAgentSpinner(), and setWorkflowState('idle')) in
that branch, or if a specific status should be excluded, replace the condition
with the correct status check on result.status to reflect the intended behavior.

256-256: Redundant inline comment — WorkflowState type makes it obsolete.

🧹 Suggested cleanup
-        this.workflowState = 'idle'; // 'idle' | 'running' | 'paused'
+        this.workflowState = 'idle';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/app/workflow-editor.ts` at line 256, The inline comment " 'idle'
| 'running' | 'paused' " after the assignment to this.workflowState is redundant
because the WorkflowState type already documents the allowed values; remove that
inline comment from the assignment to this.workflowState in workflow-editor.ts
(the line initializing this.workflowState = 'idle') so the code relies on the
WorkflowState type for documentation and avoids duplication.

2127-2127: pollForRun parameters should be (runId: string, knownLogCount: number) instead of any.

Both types are known at all call sites — runId from WorkflowRunResult.runId and knownLogCount from result.logs.length.

🛡️ Suggested fix
-    pollForRun(runId: any, knownLogCount: any) {
+    pollForRun(runId: string, knownLogCount: number) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/app/workflow-editor.ts` at line 2127, Change the pollForRun
parameter types from any to concrete types: update the method signature
pollForRun(runId: any, knownLogCount: any) to pollForRun(runId: string,
knownLogCount: number), and adjust any local variable annotations or JSDoc in
the same function if present; use WorkflowRunResult.runId as the source for
runId and result.logs.length as the source for knownLogCount at call sites to
ensure callers match the new types.

839-843: getElementById('btn-run') duplicates this.runButton — use the existing field.

this.runButton is already populated in the constructor (line 253). Querying the DOM again in initButtons is redundant and creates a local shadow runBtn that could diverge if the DOM is manipulated between construction and init.

♻️ Suggested fix
-        const runBtn = document.getElementById('btn-run');
-        if (runBtn) {
-            runBtn.addEventListener('click', () => this.runWorkflow());
-        }
+        if (this.runButton) {
+            this.runButton.addEventListener('click', () => this.runWorkflow());
+        }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/app/workflow-editor.ts` around lines 839 - 843, initButtons
currently queries getElementById('btn-run') again and creates a local runBtn
instead of using the existing this.runButton set in the constructor; remove the
redundant DOM query in initButtons and attach the click listener to
this.runButton (or guard that this.runButton exists) so initButtons uses the
already-populated field; update initButtons to call
this.runButton.addEventListener('click', () => this.runWorkflow()) only when
this.runButton is non-null.

1159-1163: deleteNode parameter should be typed as string, not any.

Node IDs are always string (constructed as `node_${this.nextNodeId++}`). Using any silently accepts undefined or non-string IDs, which would corrupt the filter.

🛡️ Suggested fix
-    deleteNode(id: any) {
-        this.nodes = this.nodes.filter((n: any) => n.id !== id);
-        this.connections = this.connections.filter((c: any) => c.source !== id && c.target !== id);
+    deleteNode(id: string) {
+        this.nodes = this.nodes.filter((n) => n.id !== id);
+        this.connections = this.connections.filter((c) => c.source !== id && c.target !== id);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/app/workflow-editor.ts` around lines 1159 - 1163, The deleteNode
method currently accepts id: any which allows non-string/undefined values to
pass into the nodes and connections filters; change the parameter signature of
deleteNode to id: string and update any callers that pass non-string values to
supply a string (IDs are constructed as `node_${this.nextNodeId++}`) so the
filters on this.nodes and this.connections remain correct; after changing the
signature, ensure the existing calls still compile and keep the existing logic
that calls this.render() and this.updateRunButton().

1172-1172: (n: any) annotation on EditorNode[] forEach is unnecessary.

this.nodes is EditorNode[]; the callback parameter is already inferred. This pattern repeats across many forEach/filter/find/reduce calls in the file (lines 558, 566, 576, 591, 607–616, 962, 989, 1106, 1160, 1172, and others). A single pass to remove the inline : any annotations on typed-array callbacks would reduce noise and keep strict mode meaningful.

♻️ Representative fix (apply consistently throughout the file)
-        this.nodes.forEach((n: any) => this.renderNode(n));
+        this.nodes.forEach((n) => this.renderNode(n));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/app/workflow-editor.ts` at line 1172, Remove unnecessary inline
": any" annotations from callbacks where the container is already strongly typed
(e.g., change this.nodes.forEach((n: any) => this.renderNode(n)) to
this.nodes.forEach(n => this.renderNode(n))). Search for occurrences across the
file (examples: this.nodes.forEach, array.filter/find/reduce callbacks around
the noted spots) and delete the ": any" on the parameter so TypeScript will use
the existing EditorNode[] (or other array) element type; keep the callback logic
and function names like renderNode intact.

1047-1049: saveRunId parameter should be string, not any.

runId is always a string (sourced from WorkflowRunResult.runId); any lets accidental non-string values pass through to localStorage.setItem.

🛡️ Suggested fix
-    saveRunId(runId: any) {
+    saveRunId(runId: string) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/app/workflow-editor.ts` around lines 1047 - 1049, The saveRunId
method currently accepts runId: any which can allow non-string values to be
stored; change the signature of saveRunId to accept runId: string and update any
callsites to pass a string (or call String(runId) before passing) so
localStorage.setItem(WorkflowEditor.RUN_KEY, runId) always receives a string;
keep the existing try/catch around localStorage access. Reference: saveRunId and
WorkflowEditor.RUN_KEY.

1063-1091: loadWorkflow trusts raw localStorage/server JSON without runtime shape validation.

Both callers pass unvalidated JSON.parse or res.json() output as WorkflowGraphInput. TypeScript types are erased at runtime, so malformed stored state can still produce unexpected undefined accesses deeper in the editor.

Consider adding a lightweight guard (or Zod schema) that checks Array.isArray(graph.nodes) and Array.isArray(graph.connections) before loadWorkflow processes the data. The existing null-coalescing (?? []) already helps, but typed node fields (e.g., node.type, node.data) have no runtime guarantees.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/app/workflow-editor.ts` around lines 1063 - 1091, Add runtime
shape validation before passing parsed JSON to loadWorkflow: in
loadInitialWorkflow and loadDefaultWorkflow (after JSON.parse / await
res.json()), verify that graph is an object and Array.isArray(graph.nodes) and
Array.isArray(graph.connections), and that each node has required fields like
typeof node.id === 'string' and typeof node.type === 'string' (or use a small
Zod/schema validator). If validation fails, do not call loadWorkflow; instead
fall back to loadDefaultWorkflow (from loadInitialWorkflow) or keep the default
start node (from loadDefaultWorkflow). Reference WorkflowEditor.STORAGE_KEY,
loadInitialWorkflow, loadDefaultWorkflow, loadWorkflow, graph.nodes and
graph.connections when making the changes.

906-906: Promise resolve callback typed as any — loses the boolean constraint.

confirmed is always boolean; the generic should be explicit so TypeScript tracks the resolved value.

🛡️ Suggested fix
-            return await new Promise((resolve: any) => {
+            return await new Promise<boolean>((resolve) => {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/app/workflow-editor.ts` at line 906, The Promise created with
"new Promise((resolve: any) =>" loses the boolean type; change it to an
explicitly typed Promise<boolean> and type the resolve callback accordingly
(e.g., "new Promise<boolean>((resolve: (value: boolean) => void) =>" or "new
Promise<boolean>((resolve) =>") so TypeScript tracks that the resolved value is
boolean; update the occurrence inside workflow-editor.ts where "new
Promise((resolve: any) =>" is used.

294-294: catch parameter typed as any — use unknown to stay consistent with strict mode.

🛡️ Suggested fix
-        }).catch((err: any) => {
+        }).catch((err: unknown) => {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/app/workflow-editor.ts` at line 294, The catch handler currently
types the error parameter as (err: any); change it to (err: unknown) and then
narrow it before use (e.g., const error = err instanceof Error ? err : new
Error(String(err))) so you don't rely on any typings; update the catch callback
that currently reads (err: any) => { ... } to (err: unknown) => { const error =
err instanceof Error ? err : new Error(String(err)); /* use error.message /
error.stack */ ... } to keep strict-mode safety when logging or rethrowing.

1785-1788: getPathD numeric parameters all typed as any — use number.

All four parameters are always numbers; any defeats arithmetic type checking.

🛡️ Suggested fix
-    getPathD(startX: any, startY: any, endX: any, endY: any) {
+    getPathD(startX: number, startY: number, endX: number, endY: number): string {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/app/workflow-editor.ts` around lines 1785 - 1788, Change the
getPathD signature to use explicit numeric types (startX: number, startY:
number, endX: number, endY: number) and a string return type, replacing the
current any types so arithmetic is type-checked; update any internal local vars
if needed (e.g., controlPointOffset inferred as number) and adjust callers or
exported type declarations referencing getPathD to match the new signature.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/web/src/app/workflow-editor.ts`:
- Line 959: The upgradeLegacyNodes method's parameter shouldRender is declared
as any; change its type to boolean (upgradeLegacyNodes(shouldRender: boolean =
false)) to tighten typing and satisfy strict mode, update any callers that pass
non-boolean values to pass a proper boolean or coerce appropriately (e.g.,
!!value), and run type checks to ensure no other places rely on non-boolean
behavior of shouldRender.
- Around line 703-721: Remove the dead method renderEffortSelect from the file
(it's not used; renderNodeForm now builds the effort dropdown using
setupDropdown), and delete its entire function body to avoid dead code. If you
choose to ever reintroduce similar logic, ensure you guard against an empty
options array before dereferencing options[0] (e.g., check options.length > 0)
and preserve the same behavior of setting node.data.reasoningEffort and wiring
the change handler.

---

Outside diff comments:
In `@apps/web/src/app/workflow-editor.ts`:
- Around line 872-877: initButtons currently adds click listeners guarded by
checks on this.approveBtn and this.rejectBtn but those properties are null at
initButtons time and are later assigned (and already wired) in
showApprovalMessage, so the listeners are dead code; remove the redundant
conditional blocks in initButtons that reference this.approveBtn and
this.rejectBtn (and their addEventListener calls) to avoid dead branches,
keeping the click wiring that exists in showApprovalMessage and ensuring
submitApprovalDecision remains called only from the handlers defined where the
buttons are actually created.
- Around line 1731-1736: The assignment to this.connectionStart sets
ConnectionStart.handle (type string) from connection.sourceHandle which is
WorkflowConnection.sourceHandle (type string | undefined); ensure you guard
against undefined by providing a fallback before assigning. Update the logic
around this.connectionStart (the block that assigns nodeId/handle/x/y) to coerce
connection.sourceHandle to a definite string (e.g., use a default like "" or a
sensible handle constant) or perform a conditional check and set handle
accordingly so ConnectionStart.handle always receives a string value.

---

Duplicate comments:
In `@apps/web/src/app/workflow-editor.ts`:
- Around line 1430-1452: The code previously crashed when no effort options
existed; ensure you compute modelEfforts and effortSource (using modelEfforts ||
this.modelEfforts[this.modelOptions[0]] || []) then build effortOptions, pick
selectedEffort via effortOptions.find(...)? .value ?? effortOptions[0]?.value ??
'' and only call this.setupDropdown (with effortDropdown, effortOptions,
selectedEffort, 'Select effort', ...) and assign data.reasoningEffort when
selectedEffort is truthy to avoid rendering with empty values; update references
to modelEfforts, effortSource, effortOptions, selectedEffort, effortDropdown,
data.reasoningEffort and this.setupDropdown accordingly.

---

Nitpick comments:
In `@apps/web/src/app/workflow-editor.ts`:
- Around line 2082-2087: The inner conditional check "result.status !==
'paused'" inside the final else branch is unreachable because 'paused',
'completed', and 'failed' are already handled earlier; remove the redundant
check and always run the cleanup logic (call clearApprovalMessage(),
hideAgentSpinner(), and setWorkflowState('idle')) in that branch, or if a
specific status should be excluded, replace the condition with the correct
status check on result.status to reflect the intended behavior.
- Line 256: The inline comment " 'idle' | 'running' | 'paused' " after the
assignment to this.workflowState is redundant because the WorkflowState type
already documents the allowed values; remove that inline comment from the
assignment to this.workflowState in workflow-editor.ts (the line initializing
this.workflowState = 'idle') so the code relies on the WorkflowState type for
documentation and avoids duplication.
- Line 2127: Change the pollForRun parameter types from any to concrete types:
update the method signature pollForRun(runId: any, knownLogCount: any) to
pollForRun(runId: string, knownLogCount: number), and adjust any local variable
annotations or JSDoc in the same function if present; use
WorkflowRunResult.runId as the source for runId and result.logs.length as the
source for knownLogCount at call sites to ensure callers match the new types.
- Around line 839-843: initButtons currently queries getElementById('btn-run')
again and creates a local runBtn instead of using the existing this.runButton
set in the constructor; remove the redundant DOM query in initButtons and attach
the click listener to this.runButton (or guard that this.runButton exists) so
initButtons uses the already-populated field; update initButtons to call
this.runButton.addEventListener('click', () => this.runWorkflow()) only when
this.runButton is non-null.
- Around line 1159-1163: The deleteNode method currently accepts id: any which
allows non-string/undefined values to pass into the nodes and connections
filters; change the parameter signature of deleteNode to id: string and update
any callers that pass non-string values to supply a string (IDs are constructed
as `node_${this.nextNodeId++}`) so the filters on this.nodes and
this.connections remain correct; after changing the signature, ensure the
existing calls still compile and keep the existing logic that calls
this.render() and this.updateRunButton().
- Line 1172: Remove unnecessary inline ": any" annotations from callbacks where
the container is already strongly typed (e.g., change this.nodes.forEach((n:
any) => this.renderNode(n)) to this.nodes.forEach(n => this.renderNode(n))).
Search for occurrences across the file (examples: this.nodes.forEach,
array.filter/find/reduce callbacks around the noted spots) and delete the ":
any" on the parameter so TypeScript will use the existing EditorNode[] (or other
array) element type; keep the callback logic and function names like renderNode
intact.
- Around line 1047-1049: The saveRunId method currently accepts runId: any which
can allow non-string values to be stored; change the signature of saveRunId to
accept runId: string and update any callsites to pass a string (or call
String(runId) before passing) so localStorage.setItem(WorkflowEditor.RUN_KEY,
runId) always receives a string; keep the existing try/catch around localStorage
access. Reference: saveRunId and WorkflowEditor.RUN_KEY.
- Around line 1063-1091: Add runtime shape validation before passing parsed JSON
to loadWorkflow: in loadInitialWorkflow and loadDefaultWorkflow (after
JSON.parse / await res.json()), verify that graph is an object and
Array.isArray(graph.nodes) and Array.isArray(graph.connections), and that each
node has required fields like typeof node.id === 'string' and typeof node.type
=== 'string' (or use a small Zod/schema validator). If validation fails, do not
call loadWorkflow; instead fall back to loadDefaultWorkflow (from
loadInitialWorkflow) or keep the default start node (from loadDefaultWorkflow).
Reference WorkflowEditor.STORAGE_KEY, loadInitialWorkflow, loadDefaultWorkflow,
loadWorkflow, graph.nodes and graph.connections when making the changes.
- Line 906: The Promise created with "new Promise((resolve: any) =>" loses the
boolean type; change it to an explicitly typed Promise<boolean> and type the
resolve callback accordingly (e.g., "new Promise<boolean>((resolve: (value:
boolean) => void) =>" or "new Promise<boolean>((resolve) =>") so TypeScript
tracks that the resolved value is boolean; update the occurrence inside
workflow-editor.ts where "new Promise((resolve: any) =>" is used.
- Line 294: The catch handler currently types the error parameter as (err: any);
change it to (err: unknown) and then narrow it before use (e.g., const error =
err instanceof Error ? err : new Error(String(err))) so you don't rely on any
typings; update the catch callback that currently reads (err: any) => { ... } to
(err: unknown) => { const error = err instanceof Error ? err : new
Error(String(err)); /* use error.message / error.stack */ ... } to keep
strict-mode safety when logging or rethrowing.
- Around line 1785-1788: Change the getPathD signature to use explicit numeric
types (startX: number, startY: number, endX: number, endY: number) and a string
return type, replacing the current any types so arithmetic is type-checked;
update any internal local vars if needed (e.g., controlPointOffset inferred as
number) and adjust callers or exported type declarations referencing getPathD to
match the new signature.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 895a278 and ee04610.

📒 Files selected for processing (1)
  • apps/web/src/app/workflow-editor.ts

@matheusgalvao1
Copy link
Collaborator Author

Addressed the newest CodeRabbit review in commit 3141b92.

Updates made:

  • Removed dead / listener wiring from (handlers remain where buttons are created in ).
  • Fixed assignment during reconnect to always use a definite string ().
  • Removed unused method.
  • Tightened additional types requested in review: , , , with number params, , and constructor catch arg to .
  • Replaced redundant run button DOM query in with .
  • Added runtime graph-shape validation before loading parsed workflow JSON from storage/server.
  • Simplified unreachable branch in final else cleanup.

Validation rerun:

  • npm --workspace apps/web run typecheck
  • npm run lint
  • npm run build

@matheusgalvao1
Copy link
Collaborator Author

Correction: previous summary comment had shell-formatting noise. Clean summary for commit 3141b92:

  • Removed dead approval-button listener wiring from initButtons. Approval buttons are still wired where they are created in showApprovalMessage.
  • Fixed reconnect assignment so ConnectionStart.handle always receives a string (sourceHandle fallback).
  • Removed unused renderEffortSelect method.
  • Tightened signatures: upgradeLegacyNodes(boolean), saveRunId(string), pollForRun(string, number), getPathD(number, number, number, number), deleteNode(string), and constructor catch argument typed as unknown.
  • Replaced redundant run-button DOM query in initButtons with the existing this.runButton field.
  • Added runtime validation for workflow payload shape before loading parsed JSON from local storage or default-workflow response.
  • Simplified unreachable cleanup branch in handleRunResult.

Validation rerun:

  • npm --workspace apps/web run typecheck
  • npm run lint
  • npm run build

@matheusgalvao1 matheusgalvao1 merged commit ec0eea8 into main Feb 25, 2026
1 of 2 checks passed
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/web/src/app/workflow-editor.ts (1)

1294-1304: ⚠️ Potential issue | 🟠 Major

Escape node.type before injecting into HTML.

The fallback label path interpolates raw node.type into innerHTML (Line 1303). Since graph data can come from storage/network, this creates an avoidable XSS surface.

🔒 Suggested fix
-        return `<span class="icon icon-primary"></span>${node.type}`;
+        return `<span class="icon icon-primary"></span>${escapeHtml(String(node.type || ''))}`;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/app/workflow-editor.ts` around lines 1294 - 1304, The fallback
branch of getNodeLabel currently injects raw node.type into HTML, creating an
XSS risk; update that return to escape/sanitize node.type before concatenation
(e.g. use the existing escapeHtml helper) so the fallback becomes something like
building the label with escapeHtml(node.type) instead of node.type; change the
fallback return in getNodeLabel to use escapeHtml(node.type) (and keep the
existing icon markup) to prevent unsafe HTML injection.
🧹 Nitpick comments (1)
apps/web/src/app/workflow-editor.ts (1)

567-627: Remove explicit any type annotations in graph-critical validation methods to maintain strict mode compliance.

These methods (getRunDisableReason, onPortMouseDown, onPortMouseUp) already work with properly typed class properties (nodes: EditorNode[], connections: WorkflowConnection[]), but explicit any annotations on callback parameters weaken compile-time guarantees in validation logic. TypeScript can infer correct types automatically.

Type cleanup suggestions
-        const startNodes = this.nodes.filter((node: any) => node.type === 'start');
+        const startNodes = this.nodes.filter((node) => node.type === 'start');

-        const nodeIdSet = new Set(this.nodes.map((node: any) => node.id));
+        const nodeIdSet = new Set(this.nodes.map((node) => node.id));

-        this.connections.forEach((conn: any) => {
+        this.connections.forEach((conn) => {

-    onPortMouseDown(e: any, nodeId: any, handle: any) {
+    onPortMouseDown(e: MouseEvent, nodeId: string, handle: string) {

-    onPortMouseUp(e: any, nodeId: any, handle: any) {
+    onPortMouseUp(e: MouseEvent, nodeId: string, handle: string) {

Also applies to: 1639-1660

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/app/workflow-editor.ts` around lines 567 - 627, Remove the
explicit any annotations in the graph-validation callbacks: change parameter
types in getRunDisableReason callbacks and the similar callbacks onPortMouseDown
and onPortMouseUp to use the actual domain types (e.g., node => EditorNode, conn
=> WorkflowConnection) or rely on type inference instead of (node: any) and
(conn: any); update uses of conn.sourceHandle/conn.source/conn.target and
node.id/node.type to match those types so TypeScript type-checks correctly
(references: getRunDisableReason, onPortMouseDown, onPortMouseUp,
IF_FALLBACK_HANDLE, getIfConditionIndexFromHandle).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@apps/web/src/app/workflow-editor.ts`:
- Around line 1294-1304: The fallback branch of getNodeLabel currently injects
raw node.type into HTML, creating an XSS risk; update that return to
escape/sanitize node.type before concatenation (e.g. use the existing escapeHtml
helper) so the fallback becomes something like building the label with
escapeHtml(node.type) instead of node.type; change the fallback return in
getNodeLabel to use escapeHtml(node.type) (and keep the existing icon markup) to
prevent unsafe HTML injection.

---

Nitpick comments:
In `@apps/web/src/app/workflow-editor.ts`:
- Around line 567-627: Remove the explicit any annotations in the
graph-validation callbacks: change parameter types in getRunDisableReason
callbacks and the similar callbacks onPortMouseDown and onPortMouseUp to use the
actual domain types (e.g., node => EditorNode, conn => WorkflowConnection) or
rely on type inference instead of (node: any) and (conn: any); update uses of
conn.sourceHandle/conn.source/conn.target and node.id/node.type to match those
types so TypeScript type-checks correctly (references: getRunDisableReason,
onPortMouseDown, onPortMouseUp, IF_FALLBACK_HANDLE,
getIfConditionIndexFromHandle).

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ee04610 and 3141b92.

📒 Files selected for processing (1)
  • apps/web/src/app/workflow-editor.ts

@matheusgalvao1 matheusgalvao1 deleted the codex/fix-workflow-editor-strict-mode branch February 25, 2026 16:59
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