Skip to content

feat!: move workflow invocation contracts to transitions#329

Merged
Nek-12 merged 7 commits into
mainfrom
edge-prompt-ownership
Jun 6, 2026
Merged

feat!: move workflow invocation contracts to transitions#329
Nek-12 merged 7 commits into
mainfrom
edge-prompt-ownership

Conversation

@Nek-12

@Nek-12 Nek-12 commented Jun 5, 2026

Copy link
Copy Markdown
Member

Summary

  • Moves workflow invocation contracts from nodes to transition branches: branch prompts, parameters, context source, approval, and routing now define target invocation.
  • Adds previous-target context source, transition-owned runtime snapshots/rendering, model-facing transition + top-level parameter completion contract, and inert legacy node-owned prompt/input/output handling.
  • Updates workflow editor UX for non-disposable transition reconnects, deterministic endpoint handles, branch prompt/parameter editing, join aggregates, prompt-loss delete confirmation, and branch/parameter terminology.

Verification

  • pnpm --dir apps/desktop exec vitest run src/features/workflow-editor/workflowPromptTemplatePlaceholders.test.ts src/features/workflow-editor/workflowEditorDraft.test.ts src/features/workflow-editor/WorkflowEditorRoute.test.tsx src/features/workflow-editor/workflowDeleteConfirmationPolicy.test.ts src/features/workflow-editor/workflowDeleteConfirmationModel.test.ts src/features/workflow-editor/WorkflowDeleteConfirmationWindow.test.tsx src/features/workflow-editor/workflowEditorGraphMutations.test.ts src/features/workflow-editor/workflowGraphLayout.test.ts src/features/workflow-editor/workflowGraphLayoutGroups.test.ts src/features/workflow-editor/WorkflowInspectorPrimitives.test.tsx src/features/workflow/WorkflowValidationIssues.test.tsx src/api/client.test.ts src/features/workflow-editor/WorkflowGraphEdge.test.tsx --reporter=dot
  • pnpm --dir apps/desktop exec tsc -b --pretty false
  • git diff --check origin/main...HEAD
  • ./scripts/build.sh --output ./bin/builder
  • Manual browser QA against isolated branch server: transition prompt/parameter editing, start-source restrictions, join aggregate chips/summary, parameter-only deletion, prompted branch delete confirmation, and clean page errors/console.

Notes

  • Manual reconnect was automated-only because Firefox CLI cannot drag the opacity-0 React Flow endpoint handle; reconnect preservation is covered by tests.
  • Parameter reorder was automated-only because Firefox CLI keyboard events did not activate dnd-kit; reorder is covered by tests.
  • Optional workflow workaround smoke test was not run to avoid unrequested model execution.

Summary by CodeRabbit

  • New Features

    • Branches now support editable prompt templates and typed parameters.
    • Reconnect branches directly on the workflow canvas.
    • New "previous target" context source to reference prior run parameter values.
  • Improvements

    • Editor language switched from “edge/edges” to “branch/branches”.
    • Parameter authoring UI, prompt placeholders (now .Params.*), and validation messaging improved.
    • Delete confirmations show prompt counts; CLI adds --prompt for edge commands.
  • Behavior

    • Completion/export and run contracts use transition-aware parameter schemas.

@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Looking for one thing? Review this PR in Change Stack to search files, summaries, diffs, and code without losing your place.

Review Change Stack

Warning

Review limit reached

@Nek-12, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 46 minutes and 26 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5799a8a8-18ec-43b7-85cd-82a2bdacf1c7

📥 Commits

Reviewing files that changed from the base of the PR and between 4641d5f and 2ba1364.

📒 Files selected for processing (3)
  • apps/desktop/src/features/workflow-editor/workflowGraphLayout.test.ts
  • apps/desktop/src/features/workflow-editor/workflowGraphLayoutGroups.test.ts
  • apps/desktop/src/features/workflow-editor/workflowGraphLayoutTestHelpers.ts
📝 Walkthrough

Walkthrough

Adds transition-branch parameters and prompts to edges across client, editor, services, runtime, and DB; introduces previous-target context source; implements edge reconnection and endpoint handles; updates validation, schemas, tests, i18n, and docs accordingly.

Changes

Transition parameters and reconnection end-to-end

Layer / File(s) Summary
Contracts, types, and DB migrations
server/metadata/*, server/workflow/types.go, shared/serverapi/workflow.go
Parameter and prompt fields added to edges; new previous_target context kind; migrations and queries persist prompt_template and parameters_json.
Client API models and schemas
apps/desktop/src/api/*
Adds WorkflowParameter; edges carry parameters/promptTemplate; client schemas and payload builders updated; client tests assert new shapes.
Editor UI: parameters, prompts, handles
apps/desktop/src/features/workflow-editor/*, apps/desktop/src/vendor/xyflow-react-types.ts, workflow-editor.css
Edge reconnection, endpoint/creation handles, prompt template editor, editable edge parameters with DnD, placeholder chips use .Params.*, and i18n updated to “branch” wording.
Editor state, mutations, deletion confirmation
apps/desktop/src/features/workflow-editor/workflowEditorDraft*, workflowEditorGraph*, workflowDeleteConfirmation*
Reducer/mutations for edit/add/update/delete/reorder of edge parameters, reconnectEdge mutation, graph serialization includes prompts/parameters, deletion confirmation now considers promptCount and text keys.
Graph layout & handle binding
apps/desktop/src/features/workflow-editor/workflowGraphLayout*, workflowGraphEdges.ts, workflowGraphLayoutGeometry.ts
Compute endpoint ports, export port IDs to ELK, bind sourceHandle/targetHandle, mark edges reconnectable, and expose handles in ReactFlow; layout tests updated.
Runtime, store, services, and view updates
server/workflowrunner/*, server/workflowstore/*, server/workflowsvc/*, server/workflowview/*
Completion contract switched to transitions+parameters, prompt rendering uses Params, previous-target context resolution added, snapshots/run metadata include prompt/parameters, store persists parameters JSON and marshals/unmarshals edge parameters.
Validation and derived wiring
server/workflow/*
Validate parameters (key/description constraints, reserved names, duplicates), prompt template placeholder parsing switched to Params/PriorParams, derive wiring/output fields from edge parameters including join aggregates and diagnostics.
Tests, docs, CLI, prompts, i18n
tests across repo, docs/dev/specs/*, prompts/workflow/*, cli/builder/*, apps/desktop/src/i18n/*
Widespread test updates to new shapes and behavior; CLI adds --prompt for edges; docs and prompts updated to transition/parameter model and branch terminology.

Estimated code review effort
🎯 4 (Complex) | ⏱️ ~45 minutes

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch edge-prompt-ownership

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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 (3)
shared/serverapi/workflow.go (1)

1243-1278: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Validate context_mode and context_source together on every edge write path.

Line 1253 still validates context_source in isolation, and Lines 1448-1452 only add a parameters count check for graph drafts. That means new_session + selected_node/previous_target can still be persisted through both edge-update and graph-save flows. The view layer now treats those source kinds as manual-move blockers, so a stale context_source on a non-continue_session edge will make a valid branch disappear after save.

Suggested fix
 func validateWorkflowEdgeFields(workflowID string, edgeID string, transitionGroupID string, key string, targetNodeID string, contextMode string, contextSource WorkflowContextSource, parameters []WorkflowParameter) error {
 	_ = edgeID
 	for _, field := range []struct{ name, value string }{{"workflow_id", workflowID}, {"transition_group_id", transitionGroupID}, {"target_node_id", targetNodeID}, {"context_mode", contextMode}} {
 		if err := validateRequired(field.name, field.value); err != nil {
 			return err
 		}
 	}
 	if err := validateModelKey("key", key); err != nil {
 		return err
 	}
-	if err := validateWorkflowContextSource(contextSource); err != nil {
+	if err := validateWorkflowContextModeAndSource(contextMode, contextSource); err != nil {
 		return err
 	}
 	if len(parameters) > WorkflowGraphDraftMaxFieldsPerEntity {
 		return workflowRequestError(WorkflowRequestErrorTooLong, "parameters", fmt.Sprintf("parameters must be <= %d", WorkflowGraphDraftMaxFieldsPerEntity))
 	}
 	return nil
 }
+
+func validateWorkflowContextModeAndSource(contextMode string, source WorkflowContextSource) error {
+	if err := validateWorkflowContextSource(source); err != nil {
+		return err
+	}
+	if strings.TrimSpace(contextMode) != "continue_session" {
+		switch strings.TrimSpace(source.Kind) {
+		case "", "immediate_source":
+			return nil
+		default:
+			return workflowRequestError(WorkflowRequestErrorInvalidValue, "context_source.kind", "non-immediate context_source requires continue_session")
+		}
+	}
+	return nil
+}
 	for _, edge := range graph.Edges {
+		if err := validateWorkflowContextModeAndSource(edge.ContextMode, edge.ContextSource); err != nil {
+			return err
+		}
 		if len(edge.Parameters) > WorkflowGraphDraftMaxFieldsPerEntity {
 			return workflowRequestError(WorkflowRequestErrorTooLong, "graph.edges.parameters", fmt.Sprintf("parameters must be <= %d", WorkflowGraphDraftMaxFieldsPerEntity))
 		}
 	}

Also applies to: 1448-1452

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@shared/serverapi/workflow.go` around lines 1243 - 1278,
validateWorkflowEdgeFields currently validates context_mode and
validateWorkflowContextSource validates context_source in isolation, which
allows disallowed combos (e.g., context_mode != "continue_session" with
context_source.Kind == "selected_node"/"previous_target") to be persisted;
update validateWorkflowEdgeFields to enforce the combined rule: if contextMode
trimmed != "continue_session" then ensure source.Kind is either "" or
"immediate_source" (return WorkflowRequestErrorInvalidValue for
"context_source.kind" otherwise), and if contextMode == "continue_session" allow
"selected_node"/"previous_target" but still call validateWorkflowContextSource
to check NodeKey rules; apply the same combined validation logic to the
graph-draft/graph-save path where WorkflowGraphDraftMaxFieldsPerEntity is
checked so both edge-update and graph-save flows reject invalid context_mode +
context_source combos.
apps/desktop/src/vendor/xyflow-react-types.ts (1)

34-52: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fix reconnectable typing in xyflow React type shim

  • onReconnect, onReconnectStart/onReconnectEnd, edgesReconnectable, reconnectRadius, and sourceHandle/targetHandle shapes in apps/desktop/src/vendor/xyflow-react-types.ts align with the @xyflow/react 12.10 API.
  • Edge.reconnectable is incomplete: it should accept boolean | "source" | "target" (HandleType), but the shim currently defines reconnectable?: boolean (around lines 34-45). This will incorrectly reject valid "source"/"target" reconnection configurations.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/desktop/src/vendor/xyflow-react-types.ts` around lines 34 - 52, The Edge
type's reconnectable field is typed too narrowly; update the reconnectable
property in the Edge type definition (export type Edge<...>) to accept boolean |
"source" | "target" (the HandleType used by `@xyflow/react`) so that existing
reconnection configs like "source" and "target" are valid; ensure the Connection
type and any related props (sourceHandle/targetHandle) remain unchanged but
verify any code referencing Edge.reconnectable treats the new union values
correctly.
server/workflowstore/transitions.go (1)

198-207: ⚠️ Potential issue | 🟠 Major

Fix context resolution when sourceRun is missing (hasSourceRun == false)

  • In server/workflowstore/transitions.go the resolveContextSourceRun(..., sourceRun.PlacementID, &sourceRun, sourceSnapshot, ...) call is reached even when sourceRun/sourceSnapshot are zero-values. For ContextSourceImmediateSource, resolveContextSourceRun returns empty runID/sessionID (no error) when immediate points to a zero-value record; targetRunMetadata then stores/omits source_run_id/source_session_id, leading to silent incorrect context.
  • For ContextSourcePreviousTarget, passing empty sourcePlacementID disables batch scoping (batchScoped=false), so the “latest completed run” lookup may be wrong across batches.
  • The workflow.Parameter copy concern at lines ~337-338 and ~364-365 doesn’t apply: workflow.Parameter is only { Key string; Description string }, so append([]workflow.Parameter(nil), ...) is sufficient.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/workflowstore/transitions.go` around lines 198 - 207, The code calls
resolveContextSourceRun(...) even when there is no sourceRun
(hasSourceRun==false), which yields empty IDs and wrong batch scoping; fix by
adding a guard after resolvedContextSourceRunFromMetadata: only call
resolveContextSourceRun when hasSourceRun is true (or when sourcePlacementID is
non-zero for ContextSourcePreviousTarget), and for ContextSourceImmediateSource
check the immediate record for a zero-value before calling
resolveContextSourceRun (if immediate is zero, do not call
resolveContextSourceRun and ensure targetRunMetadata does not get empty
source_run_id/source_session_id). Use the symbols
resolvedContextSourceRunFromMetadata, resolveContextSourceRun,
ContextSourceImmediateSource, ContextSourcePreviousTarget and ensure batchScoped
logic remains enabled only when a valid sourcePlacementID is provided.
🧹 Nitpick comments (3)
shared/serverapi/workflow_test.go (1)

44-48: 💤 Low value

Consider initializing the oversized parameter slice elements.

The test creates zero-valued WorkflowParameter structs. If parameter validation were to check individual parameter validity before checking the slice length, this test could fail for the wrong reason. However, this matches the existing pattern used for oversized nodes on line 186, and the tests pass, suggesting validation checks counts first.

♻️ Optional: Initialize parameters for explicit coverage
 oversizedEdge := validEdge
-oversizedEdge.Parameters = make([]WorkflowParameter, WorkflowGraphDraftMaxFieldsPerEntity+1)
+oversizedEdge.Parameters = make([]WorkflowParameter, WorkflowGraphDraftMaxFieldsPerEntity+1)
+for i := range oversizedEdge.Parameters {
+	oversizedEdge.Parameters[i] = WorkflowParameter{Key: "p", Description: "d"}
+}
 if err := oversizedEdge.Validate(); err == nil || !strings.Contains(err.Error(), "parameters") {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@shared/serverapi/workflow_test.go` around lines 44 - 48, The oversizedEdge
test currently constructs Parameters as a slice of zero-valued WorkflowParameter
which could mask individual parameter validation; update the test that creates
oversizedEdge (the variable oversizedEdge and its Parameters slice) to populate
each element with a minimal valid WorkflowParameter so the failure is driven by
exceeding WorkflowGraphDraftMaxFieldsPerEntity rather than per-parameter
validation, leaving the Validate() call and the error assertion unchanged.
server/workflowstore/joins.go (1)

190-211: 💤 Low value

Inconsistent return semantics: ready bool is now redundant.

After the refactor, selectedJoinOutputValues only returns (nil, false, error) on failure or (out, true, nil) on success. There's no valid (_, false, nil) "not ready yet" state, making the ready return value dead information and the caller's if !ready { return ..., nil } check at lines 77-79 unreachable without an accompanying error.

Consider either:

  1. Removing the ready bool return since it's now redundant, or
  2. Restoring a legitimate "not ready" case (return nil, false, nil) if aggregation should silently wait when values are missing
Option 1: Simplify return signature
-func selectedJoinOutputValues(join nodeContractSnapshot, outEdge edgeContractSnapshot, arrivals []joinArrival) (map[string]string, bool, error) {
+func selectedJoinOutputValues(join nodeContractSnapshot, outEdge edgeContractSnapshot, arrivals []joinArrival) (map[string]string, error) {
 	out := map[string]string{}
 	for _, requirement := range outEdge.OutputRequirements {
 		parameterKey := strings.TrimSpace(requirement.FieldName)
 		if parameterKey == "" {
 			continue
 		}
 		value := ""
 		for _, arrival := range arrivals {
 			candidate := arrival.OutputValues[parameterKey]
 			if strings.TrimSpace(candidate) != "" {
 				value = candidate
 				break
 			}
 		}
 		if strings.TrimSpace(value) == "" {
-			return nil, false, fmt.Errorf("join node %q missing aggregate parameter %q", join.ID, parameterKey)
+			return nil, fmt.Errorf("join node %q missing aggregate parameter %q", join.ID, parameterKey)
 		}
 		out[parameterKey] = value
 	}
-	return out, true, nil
+	return out, nil
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/workflowstore/joins.go` around lines 190 - 211, The function
selectedJoinOutputValues currently returns a redundant ready bool; change its
signature to return (map[string]string, error) and remove the ready return value
and logic inside selectedJoinOutputValues (return out, nil on success and nil,
fmt.Errorf(...) on failure), then update all call sites that expect
(map,string,bool,error) — e.g. the caller that does if !ready { return ..., nil
} — to instead check error != nil for failure and nil error for success (remove
the unreachable ready-check branch), and update any related tests and usages to
the new two-value return shape.
server/workflowstore/tasks.go (1)

377-389: ⚡ Quick win

Add a focused regression test for the start-edge metadata path.

This branch now serializes derived transition metadata into the initial run, but the updated suite still doesn't assert the metadata_json/edge-snapshot contents created by StartTask. A small read-back assertion here would lock down the new contract separately from the completion path.

As per coding guidelines, **/*_test.go: All business logic covered by tests. Production code is written to be unit-testable

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@apps/desktop/src/vendor/xyflow-react-types.ts`:
- Around line 34-52: The Edge type's reconnectable field is typed too narrowly;
update the reconnectable property in the Edge type definition (export type
Edge<...>) to accept boolean | "source" | "target" (the HandleType used by
`@xyflow/react`) so that existing reconnection configs like "source" and "target"
are valid; ensure the Connection type and any related props
(sourceHandle/targetHandle) remain unchanged but verify any code referencing
Edge.reconnectable treats the new union values correctly.

In `@server/workflowstore/transitions.go`:
- Around line 198-207: The code calls resolveContextSourceRun(...) even when
there is no sourceRun (hasSourceRun==false), which yields empty IDs and wrong
batch scoping; fix by adding a guard after resolvedContextSourceRunFromMetadata:
only call resolveContextSourceRun when hasSourceRun is true (or when
sourcePlacementID is non-zero for ContextSourcePreviousTarget), and for
ContextSourceImmediateSource check the immediate record for a zero-value before
calling resolveContextSourceRun (if immediate is zero, do not call
resolveContextSourceRun and ensure targetRunMetadata does not get empty
source_run_id/source_session_id). Use the symbols
resolvedContextSourceRunFromMetadata, resolveContextSourceRun,
ContextSourceImmediateSource, ContextSourcePreviousTarget and ensure batchScoped
logic remains enabled only when a valid sourcePlacementID is provided.

In `@shared/serverapi/workflow.go`:
- Around line 1243-1278: validateWorkflowEdgeFields currently validates
context_mode and validateWorkflowContextSource validates context_source in
isolation, which allows disallowed combos (e.g., context_mode !=
"continue_session" with context_source.Kind ==
"selected_node"/"previous_target") to be persisted; update
validateWorkflowEdgeFields to enforce the combined rule: if contextMode trimmed
!= "continue_session" then ensure source.Kind is either "" or "immediate_source"
(return WorkflowRequestErrorInvalidValue for "context_source.kind" otherwise),
and if contextMode == "continue_session" allow "selected_node"/"previous_target"
but still call validateWorkflowContextSource to check NodeKey rules; apply the
same combined validation logic to the graph-draft/graph-save path where
WorkflowGraphDraftMaxFieldsPerEntity is checked so both edge-update and
graph-save flows reject invalid context_mode + context_source combos.

---

Nitpick comments:
In `@server/workflowstore/joins.go`:
- Around line 190-211: The function selectedJoinOutputValues currently returns a
redundant ready bool; change its signature to return (map[string]string, error)
and remove the ready return value and logic inside selectedJoinOutputValues
(return out, nil on success and nil, fmt.Errorf(...) on failure), then update
all call sites that expect (map,string,bool,error) — e.g. the caller that does
if !ready { return ..., nil } — to instead check error != nil for failure and
nil error for success (remove the unreachable ready-check branch), and update
any related tests and usages to the new two-value return shape.

In `@shared/serverapi/workflow_test.go`:
- Around line 44-48: The oversizedEdge test currently constructs Parameters as a
slice of zero-valued WorkflowParameter which could mask individual parameter
validation; update the test that creates oversizedEdge (the variable
oversizedEdge and its Parameters slice) to populate each element with a minimal
valid WorkflowParameter so the failure is driven by exceeding
WorkflowGraphDraftMaxFieldsPerEntity rather than per-parameter validation,
leaving the Validate() call and the error assertion unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c4c916e4-a93f-4a1c-b9d1-98faf42a686f

📥 Commits

Reviewing files that changed from the base of the PR and between 1645a5a and aa77132.

📒 Files selected for processing (88)
  • apps/desktop/src/api/client.test.ts
  • apps/desktop/src/api/client.ts
  • apps/desktop/src/api/index.ts
  • apps/desktop/src/api/models.ts
  • apps/desktop/src/api/schemas/common.ts
  • apps/desktop/src/api/schemas/workflow.ts
  • apps/desktop/src/app/workflowDeleteConfirmRoute.tsx
  • apps/desktop/src/features/workflow-editor/WorkflowDeleteConfirmationWindow.test.tsx
  • apps/desktop/src/features/workflow-editor/WorkflowDeleteConfirmationWindow.tsx
  • apps/desktop/src/features/workflow-editor/WorkflowEditorRoute.test.tsx
  • apps/desktop/src/features/workflow-editor/WorkflowEditorRoute.tsx
  • apps/desktop/src/features/workflow-editor/WorkflowGraphCanvas.tsx
  • apps/desktop/src/features/workflow-editor/WorkflowGraphCanvasEdgeInteraction.test.tsx
  • apps/desktop/src/features/workflow-editor/WorkflowGraphEdge.test.tsx
  • apps/desktop/src/features/workflow-editor/WorkflowGraphNodes.tsx
  • apps/desktop/src/features/workflow-editor/WorkflowInspectorPrimitives.test.tsx
  • apps/desktop/src/features/workflow-editor/WorkflowInspectorSidebar.tsx
  • apps/desktop/src/features/workflow-editor/workflow-editor.css
  • apps/desktop/src/features/workflow-editor/workflowDeleteConfirmationModel.test.ts
  • apps/desktop/src/features/workflow-editor/workflowDeleteConfirmationModel.ts
  • apps/desktop/src/features/workflow-editor/workflowDeleteConfirmationPolicy.test.ts
  • apps/desktop/src/features/workflow-editor/workflowDeleteConfirmationPolicy.ts
  • apps/desktop/src/features/workflow-editor/workflowEditorDraft.test.ts
  • apps/desktop/src/features/workflow-editor/workflowEditorDraft.ts
  • apps/desktop/src/features/workflow-editor/workflowEditorGraphEdgeMutations.ts
  • apps/desktop/src/features/workflow-editor/workflowEditorGraphMutationFixtures.ts
  • apps/desktop/src/features/workflow-editor/workflowEditorGraphMutationHelpers.ts
  • apps/desktop/src/features/workflow-editor/workflowEditorGraphMutationTypes.ts
  • apps/desktop/src/features/workflow-editor/workflowEditorGraphMutations.test.ts
  • apps/desktop/src/features/workflow-editor/workflowEditorGraphMutations.ts
  • apps/desktop/src/features/workflow-editor/workflowGraphCanvasInteractions.test.ts
  • apps/desktop/src/features/workflow-editor/workflowGraphCanvasInteractions.ts
  • apps/desktop/src/features/workflow-editor/workflowGraphEdges.ts
  • apps/desktop/src/features/workflow-editor/workflowGraphLayout.test.ts
  • apps/desktop/src/features/workflow-editor/workflowGraphLayout.ts
  • apps/desktop/src/features/workflow-editor/workflowGraphLayoutEdgeRoutes.ts
  • apps/desktop/src/features/workflow-editor/workflowGraphLayoutGeometry.ts
  • apps/desktop/src/features/workflow-editor/workflowGraphLayoutGroups.test.ts
  • apps/desktop/src/features/workflow-editor/workflowGraphLayoutNodes.ts
  • apps/desktop/src/features/workflow-editor/workflowPromptTemplatePlaceholders.test.ts
  • apps/desktop/src/features/workflow-editor/workflowPromptTemplatePlaceholders.ts
  • apps/desktop/src/features/workflow/WorkflowValidationIssues.test.tsx
  • apps/desktop/src/i18n/workflowEditorEn.ts
  • apps/desktop/src/vendor/xyflow-react-types.ts
  • docs/dev/specs/terminology.md
  • docs/dev/specs/workflow-editor.md
  • prompts/workflow/structured_completion_instructions.md
  • prompts/workflow/tool_completion_instructions.md
  • prompts/workflow/workflow_task_instructions.md
  • server/metadata/migrations/00029_workflow_previous_target_context_source.up.sql
  • server/metadata/migrations/00030_workflow_transition_invocation_contract.up.sql
  • server/metadata/queries.sql
  • server/metadata/sqlitegen/models.go
  • server/metadata/sqlitegen/queries.sql.go
  • server/metadata/workflow_schema_test.go
  • server/runtime/workflow_completion_test.go
  • server/tools/definitions.go
  • server/tools/schemas/complete_node.json
  • server/workflow/derived_wiring.go
  • server/workflow/derived_wiring_test.go
  • server/workflow/prompt_refs.go
  • server/workflow/types.go
  • server/workflow/validation.go
  • server/workflow/validation_test.go
  • server/workflowrunner/starter.go
  • server/workflowrunner/starter_test.go
  • server/workflowruntime/completion.go
  • server/workflowruntime/completion_test.go
  • server/workflowstore/context_sources.go
  • server/workflowstore/graph_records.go
  • server/workflowstore/graph_save.go
  • server/workflowstore/joins.go
  • server/workflowstore/manual_moves.go
  • server/workflowstore/queries.go
  • server/workflowstore/queries/latest_transition_output_values.sql
  • server/workflowstore/queries/update_workflow_edge.sql
  • server/workflowstore/queries/upsert_workflow_edge.sql
  • server/workflowstore/run_snapshot.go
  • server/workflowstore/runs.go
  • server/workflowstore/store.go
  • server/workflowstore/store_test.go
  • server/workflowstore/tasks.go
  • server/workflowstore/transitions.go
  • server/workflowsvc/service.go
  • server/workflowview/service.go
  • server/workflowview/service_test.go
  • shared/serverapi/workflow.go
  • shared/serverapi/workflow_test.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/desktop/src/features/workflow-editor/workflowGraphLayoutEdgeRoutes.ts`:
- Around line 65-67: The aligned-join routing path currently discards endpoint
port offsets by calling branchJoinEdgeRoutePoints(...) and
adjustAlignedJoinEndpointRoutePoints(...) which force center points; update the
routing so it preserves model.sourcePort and model.targetPort offsets when
computing routedPoints and when calling
adjustAlignedJoinEndpointRoutePoints/branchJoinEdgeRoutePoints — pass the
source/target port offsets (from model.sourcePort and model.targetPort or the
computed sourceHandle/targetHandle positions) into those helpers or modify the
helpers to accept/use a portOffset argument so endpoint alignment uses the
actual offset values instead of forcing center points; ensure routedPoints are
adjusted using those offsets wherever sourceAligned or targetAligned is used.

In `@prompts/skills/builder-workflows/SKILL.md`:
- Line 58: The docs claim the `edges` output includes prompt template fields,
but the workflow inspect CLI handler currently omits them; update the "workflow
inspect" command's edge-formatting logic (the function that serializes/prints
edges in the CLI inspect implementation) to include the prompt template fields
from each edge (e.g., add the prompt template and any template variables/IDs
into the printed edge record alongside context mode and approval flags) and
format them consistently with the other edge fields so the CLI output matches
the SKILL.md documentation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 71470f95-5e9d-40c2-a75f-ac6403cdaff2

📥 Commits

Reviewing files that changed from the base of the PR and between aa77132 and 35e7a81.

📒 Files selected for processing (19)
  • apps/desktop/src/features/workflow-editor/WorkflowInspectorSidebar.tsx
  • apps/desktop/src/features/workflow-editor/workflowEditorDraft.ts
  • apps/desktop/src/features/workflow-editor/workflowEditorGraphEdgeMutations.ts
  • apps/desktop/src/features/workflow-editor/workflowGraphLayout.test.ts
  • apps/desktop/src/features/workflow-editor/workflowGraphLayoutEdgeRoutes.ts
  • cli/builder/help/workflow.txt
  • cli/builder/workflow_command.go
  • cli/builder/workflow_command_test.go
  • prompts/skills/builder-workflows/SKILL.md
  • scripts/capture-gui-gap-proof.sh
  • server/core/composition_test.go
  • server/workflow/derived_wiring.go
  • server/workflow/derived_wiring_test.go
  • server/workflowscheduler/service_test.go
  • server/workflowstore/run_snapshot.go
  • server/workflowsvc/service_test.go
  • server/workflowview/service.go
  • server/workflowview/service_test.go
  • server/worktree/task_worktree_test.go
✅ Files skipped from review due to trivial changes (2)
  • cli/builder/help/workflow.txt
  • server/workflowscheduler/service_test.go
🚧 Files skipped from review as they are similar to previous changes (4)
  • server/workflowstore/run_snapshot.go
  • server/workflowview/service.go
  • server/workflow/derived_wiring.go
  • apps/desktop/src/features/workflow-editor/workflowEditorDraft.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/desktop/src/features/workflow-editor/workflowGraphLayoutEdgeRoutes.ts`:
- Around line 65-67: The aligned-join routing path currently discards endpoint
port offsets by calling branchJoinEdgeRoutePoints(...) and
adjustAlignedJoinEndpointRoutePoints(...) which force center points; update the
routing so it preserves model.sourcePort and model.targetPort offsets when
computing routedPoints and when calling
adjustAlignedJoinEndpointRoutePoints/branchJoinEdgeRoutePoints — pass the
source/target port offsets (from model.sourcePort and model.targetPort or the
computed sourceHandle/targetHandle positions) into those helpers or modify the
helpers to accept/use a portOffset argument so endpoint alignment uses the
actual offset values instead of forcing center points; ensure routedPoints are
adjusted using those offsets wherever sourceAligned or targetAligned is used.

In `@prompts/skills/builder-workflows/SKILL.md`:
- Line 58: The docs claim the `edges` output includes prompt template fields,
but the workflow inspect CLI handler currently omits them; update the "workflow
inspect" command's edge-formatting logic (the function that serializes/prints
edges in the CLI inspect implementation) to include the prompt template fields
from each edge (e.g., add the prompt template and any template variables/IDs
into the printed edge record alongside context mode and approval flags) and
format them consistently with the other edge fields so the CLI output matches
the SKILL.md documentation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 71470f95-5e9d-40c2-a75f-ac6403cdaff2

📥 Commits

Reviewing files that changed from the base of the PR and between aa77132 and 35e7a81.

📒 Files selected for processing (19)
  • apps/desktop/src/features/workflow-editor/WorkflowInspectorSidebar.tsx
  • apps/desktop/src/features/workflow-editor/workflowEditorDraft.ts
  • apps/desktop/src/features/workflow-editor/workflowEditorGraphEdgeMutations.ts
  • apps/desktop/src/features/workflow-editor/workflowGraphLayout.test.ts
  • apps/desktop/src/features/workflow-editor/workflowGraphLayoutEdgeRoutes.ts
  • cli/builder/help/workflow.txt
  • cli/builder/workflow_command.go
  • cli/builder/workflow_command_test.go
  • prompts/skills/builder-workflows/SKILL.md
  • scripts/capture-gui-gap-proof.sh
  • server/core/composition_test.go
  • server/workflow/derived_wiring.go
  • server/workflow/derived_wiring_test.go
  • server/workflowscheduler/service_test.go
  • server/workflowstore/run_snapshot.go
  • server/workflowsvc/service_test.go
  • server/workflowview/service.go
  • server/workflowview/service_test.go
  • server/worktree/task_worktree_test.go
✅ Files skipped from review due to trivial changes (2)
  • cli/builder/help/workflow.txt
  • server/workflowscheduler/service_test.go
🚧 Files skipped from review as they are similar to previous changes (4)
  • server/workflowstore/run_snapshot.go
  • server/workflowview/service.go
  • server/workflow/derived_wiring.go
  • apps/desktop/src/features/workflow-editor/workflowEditorDraft.ts
🛑 Comments failed to post (2)
apps/desktop/src/features/workflow-editor/workflowGraphLayoutEdgeRoutes.ts (1)

65-67: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Aligned-join route endpoints ignore endpoint-port offsets

On Line 65 and Line 67, aligned-join routing now goes through helpers that force center points (Line 102 and Line 124) while model.sourcePort/model.targetPort are unused. That can misalign routePoints from sourceHandle/targetHandle when endpoints are intentionally offset.

💡 Proposed fix
 export function workflowGraphEdgeRoutePoints(
   model: Readonly<{
     sourceNodeID: string;
     sourcePort: WorkflowGraphEndpointPort;
     targetNodeID: string;
     targetPort: WorkflowGraphEndpointPort;
   }>,
@@
   if (isBranchToAlignedJoin(source, target, targetAligned)) {
-    return branchJoinEdgeRoutePoints(source, target, options.groupNodeByGroupID);
+    return branchJoinEdgeRoutePoints(source, target, model, options.groupNodeByGroupID);
   }
-  return adjustAlignedJoinEndpointRoutePoints(routedPoints, source, target, { sourceAligned, targetAligned });
+  return adjustAlignedJoinEndpointRoutePoints(routedPoints, source, target, model, {
+    sourceAligned,
+    targetAligned,
+  });
 }
@@
 function branchJoinEdgeRoutePoints(
   source: WorkflowGraphNodeRect,
   target: WorkflowGraphNodeRect,
+  model: Readonly<{ sourcePort: WorkflowGraphEndpointPort; targetPort: WorkflowGraphEndpointPort }>,
   groupNodeByGroupID: ReadonlyMap<string, WorkflowGraphNode>,
 ): readonly WorkflowGraphPoint[] {
-  const start = centeredSourcePoint(source);
-  const end = joinCenterTargetPoint(target);
+  const start = sourceHandlePoint(source, model.sourcePort);
+  const end = targetHandlePoint(target, model.targetPort);
@@
 function adjustAlignedJoinEndpointRoutePoints(
   points: readonly WorkflowGraphPoint[],
   source: WorkflowGraphNodeRect,
   target: WorkflowGraphNodeRect,
+  model: Readonly<{ sourcePort: WorkflowGraphEndpointPort; targetPort: WorkflowGraphEndpointPort }>,
   flags: Readonly<{ sourceAligned: boolean; targetAligned: boolean }>,
 ): readonly WorkflowGraphPoint[] {
@@
   if (flags.sourceAligned) {
-    adjusted[0] = centeredSourcePoint(source);
+    adjusted[0] = sourceHandlePoint(source, model.sourcePort);
   }
   if (flags.targetAligned) {
-    adjusted[adjusted.length - 1] = joinCenterTargetPoint(target);
+    adjusted[adjusted.length - 1] = targetHandlePoint(target, model.targetPort);
   }
   return compactRoutePoints(adjusted);
 }
+
+function sourceHandlePoint(rect: WorkflowGraphNodeRect, port: WorkflowGraphEndpointPort): WorkflowGraphPoint {
+  return { x: rect.x + rect.width, y: rect.y + port.y };
+}
+
+function targetHandlePoint(rect: WorkflowGraphNodeRect, port: WorkflowGraphEndpointPort): WorkflowGraphPoint {
+  return { x: rect.x, y: rect.y + port.y };
+}

Also applies to: 97-135

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/desktop/src/features/workflow-editor/workflowGraphLayoutEdgeRoutes.ts`
around lines 65 - 67, The aligned-join routing path currently discards endpoint
port offsets by calling branchJoinEdgeRoutePoints(...) and
adjustAlignedJoinEndpointRoutePoints(...) which force center points; update the
routing so it preserves model.sourcePort and model.targetPort offsets when
computing routedPoints and when calling
adjustAlignedJoinEndpointRoutePoints/branchJoinEdgeRoutePoints — pass the
source/target port offsets (from model.sourcePort and model.targetPort or the
computed sourceHandle/targetHandle positions) into those helpers or modify the
helpers to accept/use a portOffset argument so endpoint alignment uses the
actual offset values instead of forcing center points; ensure routedPoints are
adjusted using those offsets wherever sourceAligned or targetAligned is used.
prompts/skills/builder-workflows/SKILL.md (1)

58-58: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

workflow inspect prompt visibility is documented incorrectly.

Line 58 says the edges section records prompts, but current CLI inspect output (cli/builder/workflow_command.go, Line 659-670) does not print prompt template fields. This will mislead users trying to audit branch prompts.

📌 Suggested doc fix
-The first section lists node IDs, keys, kinds, display names, and subagent roles. The `transition_groups` section maps source node IDs to transition IDs. The `edges` section maps transition groups to target node IDs and records prompts, context mode, and approval requirements.
+The first section lists node IDs, keys, kinds, display names, and subagent roles. The `transition_groups` section maps source node IDs to transition IDs. The `edges` section maps transition groups to target node IDs and records context mode and approval requirements.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

The first section lists node IDs, keys, kinds, display names, and subagent roles. The `transition_groups` section maps source node IDs to transition IDs. The `edges` section maps transition groups to target node IDs and records context mode and approval requirements.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@prompts/skills/builder-workflows/SKILL.md` at line 58, The docs claim the
`edges` output includes prompt template fields, but the workflow inspect CLI
handler currently omits them; update the "workflow inspect" command's
edge-formatting logic (the function that serializes/prints edges in the CLI
inspect implementation) to include the prompt template fields from each edge
(e.g., add the prompt template and any template variables/IDs into the printed
edge record alongside context mode and approval flags) and format them
consistently with the other edge fields so the CLI output matches the SKILL.md
documentation.

@Nek-12

Nek-12 commented Jun 6, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 6, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
apps/desktop/src/features/workflow-editor/workflowGraphLayout.test.ts (1)

213-217: ⚡ Quick win

Potential runtime error: endpointPort returns undefined for non-string handles.

The endpointPort function returns undefined when handle is not a string (lines 215-217), but assertEndpointHandle (line 236) throws an error if port === undefined. This is correct behavior for assertions, but consider whether the early return at lines 215-217 is necessary—React Flow's handle IDs should always be strings when routing is configured. If null/undefined handles indicate a test or implementation defect, consider throwing an error here instead of silently returning undefined.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/desktop/src/features/workflow-editor/workflowGraphLayout.test.ts` around
lines 213 - 217, The endpointPort function currently returns undefined for
non-string handles which leads to assertEndpointHandle throwing later; instead
make endpointPort validate and fail-fast: replace the early return in
endpointPort so that if handle is null/undefined or not a string it throws a
clear error (e.g., TypeError or Error) with context (include the handle value),
so callers like assertEndpointHandle receive a deterministic failure; update
endpointPort (and any callers relying on undefined behavior) to assume a string
handle after this change.
apps/desktop/src/features/workflow-editor/workflowGraphLayoutGroups.test.ts (1)

102-133: ⚡ Quick win

Code duplication: endpointPort, isEndpointPort, and isRecord helpers are duplicated across test files.

The helpers endpointPort, isEndpointPort, and isRecord appear in both workflowGraphLayout.test.ts and workflowGraphLayoutGroups.test.ts. In workflowGraphLayout.test.ts, endpointPort returns undefined for invalid handles, while here it throws an error. This inconsistency could lead to confusion.

♻️ Proposed consolidation

Consider extracting these shared test helpers into a separate file (e.g., workflowGraphLayoutTestHelpers.ts) and reconciling the error-handling strategy:

// apps/desktop/src/features/workflow-editor/workflowGraphLayoutTestHelpers.ts
export function endpointPort(
  node: WorkflowGraphNode,
  handleID: string | null | undefined,
  side: "source" | "target",
): Readonly<{ id: string; side: "source" | "target"; y: number }> {
  if (typeof handleID !== "string" || node.data.entityKind !== "node") {
    throw new Error(`Endpoint port ${handleID ?? ""} not found for ${node.id}`);
  }
  const ports: unknown = node.data.endpointPorts;
  if (!Array.isArray(ports)) {
    throw new Error(`Endpoint ports not found for ${node.id}`);
  }
  const port = ports.filter(isEndpointPort).find((item) => item.id === handleID && item.side === side);
  if (port === undefined) {
    throw new Error(`Endpoint port ${handleID} not found for ${node.id}`);
  }
  return port;
}

// ... export isEndpointPort, isRecord, etc.

Then import and use in both test files.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/desktop/src/features/workflow-editor/workflowGraphLayoutGroups.test.ts`
around lines 102 - 133, Consolidate the duplicated test helpers by extracting
endpointPort, isEndpointPort, and isRecord into a single shared test helpers
module (e.g., workflowGraphLayoutTestHelpers.ts) and import that module from
both workflowGraphLayout.test.ts and workflowGraphLayoutGroups.test.ts;
reconcile the inconsistent behavior by adopting a single error-handling strategy
(use the throwing variant shown here) so endpointPort always throws on invalid
handles, update endpointPoint to call the imported endpointPort, and replace the
in-file helper copies with imports of isEndpointPort/isRecord to avoid
duplication.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@apps/desktop/src/features/workflow-editor/workflowGraphLayout.test.ts`:
- Around line 213-217: The endpointPort function currently returns undefined for
non-string handles which leads to assertEndpointHandle throwing later; instead
make endpointPort validate and fail-fast: replace the early return in
endpointPort so that if handle is null/undefined or not a string it throws a
clear error (e.g., TypeError or Error) with context (include the handle value),
so callers like assertEndpointHandle receive a deterministic failure; update
endpointPort (and any callers relying on undefined behavior) to assume a string
handle after this change.

In `@apps/desktop/src/features/workflow-editor/workflowGraphLayoutGroups.test.ts`:
- Around line 102-133: Consolidate the duplicated test helpers by extracting
endpointPort, isEndpointPort, and isRecord into a single shared test helpers
module (e.g., workflowGraphLayoutTestHelpers.ts) and import that module from
both workflowGraphLayout.test.ts and workflowGraphLayoutGroups.test.ts;
reconcile the inconsistent behavior by adopting a single error-handling strategy
(use the throwing variant shown here) so endpointPort always throws on invalid
handles, update endpointPoint to call the imported endpointPort, and replace the
in-file helper copies with imports of isEndpointPort/isRecord to avoid
duplication.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 45089c03-00e1-4481-bcf4-800789f05f17

📥 Commits

Reviewing files that changed from the base of the PR and between 35e7a81 and 4641d5f.

📒 Files selected for processing (4)
  • apps/desktop/src/features/workflow-editor/workflowGraphLayout.test.ts
  • apps/desktop/src/features/workflow-editor/workflowGraphLayoutEdgeRoutes.ts
  • apps/desktop/src/features/workflow-editor/workflowGraphLayoutGroups.test.ts
  • prompts/skills/builder-workflows/SKILL.md
✅ Files skipped from review due to trivial changes (1)
  • prompts/skills/builder-workflows/SKILL.md

@Nek-12 Nek-12 merged commit 91bb457 into main Jun 6, 2026
14 of 15 checks passed
@Nek-12 Nek-12 deleted the edge-prompt-ownership branch June 6, 2026 01:07
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