Skip to content

feat(deployments): support multi-version flows#12950

Merged
viktoravelino merged 20 commits into
release-1.10.0from
feat/deployment-multi-flow-versions
May 8, 2026
Merged

feat(deployments): support multi-version flows#12950
viktoravelino merged 20 commits into
release-1.10.0from
feat/deployment-multi-flow-versions

Conversation

@viktoravelino
Copy link
Copy Markdown
Collaborator

@viktoravelino viktoravelino commented Apr 30, 2026

Summary

  • support attaching multiple versions of the same flow to one deployment
  • move deployment stepper state to version-scoped attachment keys
  • default tool names with stable scoped suffixes to avoid provider collisions across flows and agents
  • preserve removed attachment state in edit mode so detach/undo/review stay consistent
  • refactor the review step into smaller components and utilities
  • keep the deployment stepper provider mounted during modal loading transitions to avoid context crashes

Behavior Changes

  • the same flow can now be attached more than once in a single agent, using different versions
  • each attached version is tracked independently for connections, tool names, review, detach, and undo
  • create and edit flows both generate stable default tool names when the user does not provide one
  • edit mode now keeps pending removals visible in the left panel and review step until submit

Notes

  • backend/API already supported multiple flow versions per deployment; this PR wires the frontend to that model.
Screen.Recording.2026-04-30.at.3.08.57.PM.mov
Screenshot 2026-04-30 at 2 50 13 PM

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 30, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: fd8976ec-0721-4f6f-a2d2-deda549c2220

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/deployment-multi-flow-versions

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions github-actions Bot added the enhancement New feature or request label Apr 30, 2026
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 30, 2026

Codecov Report

❌ Patch coverage is 94.76219% with 87 lines in your changes missing coverage. Please review.
✅ Project coverage is 54.06%. Comparing base (a556005) to head (00e4831).
⚠️ Report is 7 commits behind head on release-1.10.0.

Files with missing lines Patch % Lines
...s/deploymentsPage/components/step-attach-flows.tsx 78.84% 22 Missing ⚠️
...es/deploymentsPage/components/step-review/types.ts 0.00% 20 Missing ⚠️
...oymentsPage/helpers/deployment-payload-builders.ts 95.45% 16 Missing ⚠️
...age/components/step-attach-flows-version-panel.tsx 89.25% 13 Missing ⚠️
...ymentsPage/contexts/deployment-stepper-context.tsx 95.45% 7 Missing ⚠️
...age/pages/deploymentsPage/helpers/version-scope.ts 89.28% 6 Missing ⚠️
.../src/pages/MainPage/pages/deploymentsPage/types.ts 93.33% 3 Missing ⚠️

❌ Your project check has failed because the head coverage (49.97%) is below the target coverage (60.00%). You can increase the head coverage or adjust the target coverage.

Additional details and impacted files

Impacted file tree graph

@@                Coverage Diff                 @@
##           release-1.10.0   #12950      +/-   ##
==================================================
+ Coverage           53.90%   54.06%   +0.15%     
==================================================
  Files                2051     2060       +9     
  Lines              187497   188372     +875     
  Branches            26653    26827     +174     
==================================================
+ Hits               101072   101842     +770     
- Misses              85306    85411     +105     
  Partials             1119     1119              
Flag Coverage Δ
backend 57.20% <ø> (-0.07%) ⬇️
frontend 54.26% <94.76%> (+0.24%) ⬆️
lfx 49.97% <ø> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...nts/deployment-details-modal/flow-version-item.tsx 90.41% <100.00%> (+0.13%) ⬆️
...ymentsPage/components/deployment-stepper-modal.tsx 85.07% <100.00%> (-1.73%) ⬇️
...e/components/step-attach-flows-flow-list-panel.tsx 93.79% <100.00%> (+<0.01%) ⬆️
...e/pages/deploymentsPage/components/step-review.tsx 98.10% <100.00%> (+0.13%) ⬆️
...Page/components/step-review/editable-tool-name.tsx 96.03% <100.00%> (ø)
...omponents/step-review/review-detaching-section.tsx 93.24% <100.00%> (ø)
...components/step-review/review-flow-config-card.tsx 95.17% <100.00%> (ø)
...age/components/step-review/review-summary-card.tsx 94.39% <100.00%> (ø)
...es/deploymentsPage/components/step-review/utils.ts 100.00% <100.00%> (ø)
...MainPage/pages/deploymentsPage/helpers/wxo-name.ts 100.00% <100.00%> (ø)
... and 8 more

... and 34 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@viktoravelino viktoravelino self-assigned this Apr 30, 2026
@github-actions github-actions Bot added enhancement New feature or request and removed enhancement New feature or request labels Apr 30, 2026
@github-actions github-actions Bot added enhancement New feature or request and removed enhancement New feature or request labels Apr 30, 2026
@github-actions github-actions Bot added enhancement New feature or request and removed enhancement New feature or request labels Apr 30, 2026
@github-actions github-actions Bot added enhancement New feature or request and removed enhancement New feature or request labels Apr 30, 2026
@github-actions github-actions Bot added enhancement New feature or request and removed enhancement New feature or request labels Apr 30, 2026
…ment-multi-flow-versions

# Conflicts:
#	src/frontend/src/pages/MainPage/pages/deploymentsPage/components/step-attach-flows-version-panel.tsx
#	src/frontend/src/pages/MainPage/pages/deploymentsPage/components/step-attach-flows.tsx
Copy link
Copy Markdown
Member

@Cristhianzl Cristhianzl left a comment

Choose a reason for hiding this comment

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

🎯 Executive Summary

Functionally strong PR. Enables attaching multiple versions of the same flow to a deployment, with flowId:versionId scoped keys, undo of detach in edit mode, stable default tool names, and a clean refactor of the Review step into smaller components.

Category Status Notes
🔴 CRITICAL — Security / PII ✅ OK No PII in logs, no secrets in code, no console.*
🔴 CRITICAL — DRY ⚠️ FAIL "Scoped fallback" logic duplicated in 4 places
🔴 CRITICAL — File Structure ⚠️ FAIL deployment-stepper-context.tsx = 814 lines (limit 500–700)
🟠 IMPORTANT — SOLID ⚠️ FAIL Context SRP; overloaded signature in handleSelectVersion
🟠 IMPORTANT — Errors / Typing ✅ OK Strong typing, no any, errors handled
🟡 RECOMMENDED — Observability ✅ OK No logs; modal surfaces errors via toast
🟢 TESTING ✅ OK Happy + adversarial paths covered

🔴 CRITICAL

1. deployment-stepper-context.tsx exceeds the line limit

File: src/frontend/src/pages/MainPage/pages/deploymentsPage/contexts/deployment-stepper-context.tsx (814 lines)
Rule violated: Hard limit: 500 lines (up to 600–700 only if all other rules pass) — REVIEWER_RULE.md §"File Structure Limits"

The Provider mixes very distinct responsibilities in the same file:

Responsibility Functions Prefix Approx. lines
Normalization / key utility normalizeSelectedFlowVersions, getScopedValue, getScopedToolName normalize* / get* 134–183
Payload building (business rule orchestration) buildProviderAccountPayload, buildConnectionPayloads, buildDeploymentPayload, buildDeploymentUpdatePayload build* 419–705
Stepper state / handlers handleNext, handleBack, handleSelectVersion, handleRemoveAttachedFlow, handleUndoRemoveFlow handle* / set* 297–416
React Provider + final useMemo DeploymentStepperProvider 185–804

Why this matters:

  • Mixing prefixes (normalize* + build* + handle* + get*) violates "Mandatory Separation by Responsibility" (REVIEWER_RULE.md §246-258).
  • The build* functions are pure domain logic (state → API payload) — they don't depend on React and could be tested without the Provider.
  • The Provider should orchestrate; today it is the entire module.

How to fix:

contexts/
└── deployment-stepper-context.tsx     # Provider + state (≤300 lines)

helpers/
├── version-scope.ts                    # normalizeSelectedFlowVersions, getScopedValue, getScopedToolName
└── deployment-payload-builders.ts      # build*Payload (pure functions)

The build* functions today close over React state; exposed as pure functions they would receive state as arguments and become trivially testable in isolation (today the tests in deployment-stepper-payload-builders.test.tsx need to mount the Provider and use act() to test them — a clear coupling smell).

💡 Estimate: after the split, the Provider file should drop to ~300 lines and build*Payload gain cheap, isolated test coverage.


2. DRY — "Scoped key fallback" logic duplicated in 4 places

Rule violated: REVIEWER_RULE.md §"DRY Principle" — "5+ lines appearing 2+ times = extract"

The rule "look up by attachmentKey; if missing and only one version exists for that flowId, fall back to flowId" is replicated:

Location Function Lines
contexts/deployment-stepper-context.tsx:155-161 getScopedValue 7
contexts/deployment-stepper-context.tsx:163-183 getScopedToolName 21
components/step-review/utils.ts:9-28 getToolNameForReview 20
components/step-review/utils.ts:30-49 getInitialToolNameForReview 20

getToolNameForReview (utils.ts:9) is essentially identical to getScopedToolName (context.tsx:163) — only the versionMap shape differs (Map vs list). Same business rule, rewritten.

How to fix:

Extract into helpers/version-scope.ts:

export function getScopedValue<T>(
  map: Map<string, T>,
  attachmentKey: string,
  flowId: string,
): T | undefined { ... }

export function getScopedValueWithUniqueVersion<T>(
  map: Map<string, T>,
  attachmentKey: string,
  flowId: string,
  flowVersionCount: number,
): T | undefined { ... }

Reuse in both the context and step-review/utils.ts.


🟠 IMPORTANT

3. SRP — Overloaded signature in handleSelectVersion

File: contexts/deployment-stepper-context.tsx:93-98 and :388-414

handleSelectVersion: (
  flowId: string,
  flowNameOrVersionId: string,    // 👈 meaning shifts
  versionIdOrVersionTag: string,  // 👈 meaning shifts
  versionTag?: string,
) => void;

The semantics of arguments 2 and 3 depend on whether the 4th is provided. This is "do-stuff-with-conditional-meaning" — the same anti-pattern described in REVIEWER_RULE.md §"SRP — How to Spot Violations" (processOrder(), handleEverything()).

Real call sites:

  • step-attach-flows.tsx:127-134: passes 4 arguments (the "complete" path)
  • 3-argument calls also possible (legacy callers without flowName).

How to fix:

Lock down a single object signature:

handleSelectVersion: (params: {
  flowId: string;
  flowName: string;
  versionId: string;
  versionTag: string;
}) => void;

If a legacy caller has no flowName, it explicitly passes "Flow" — visible at the call site.


4. SRP / KISS — step-review/utils.ts mixes prefixes

File: components/step-review/utils.ts (255 lines)

Coexisting prefixes: normalize* (validation/format), build* (formatting), get* (data lookup). Per REVIEWER_RULE.md §246-258, these prefixes belong in separate files.

The mitigation here is reasonable (all are pure and review-specific), but:

  • normalizeWxoName (utils.ts:5-7) is provider-specific ("Wxo" = watsonx Orchestrate). A similar utility likely already exists — verify with grep -rn "wxo\|watsonx" src/frontend --include="*.ts". If it doesn't, move it to helpers/wxo-name.ts to make scope explicit.
  • getToolNameForReview / getInitialToolNameForReview are lookups (item #2 above): move to the shared helper.
  • What remains (buildReviewFlows, buildToolNamesToCheck, buildToolNameErrors) is cohesive — keep.

5. Magic strings / inconsistent fallbacks

Locations:

File:line Value Suggestion
step-review.tsx:84 "Unknown flow" constant UNKNOWN_FLOW_LABEL
step-review/utils.ts:119 "Unknown" same constant
step-attach-flows.tsx:280 "Flow" constant DEFAULT_FLOW_LABEL
deployment-stepper-modal.tsx:108 "Flow" same constant
contexts/deployment-stepper-context.tsx:395, 496, 585 "Flow" same constant
contexts/deployment-stepper-context.tsx:424 "watsonx-orchestrate" already exists? yes, in mockInstance.provider_key (tests) and mappers — confirm whether WXO_PROVIDER_KEY is already defined

Three different fallbacks ("Unknown flow", "Unknown", "Flow") represent the same concept ("could not resolve flow name"). Standardize.


6. Redundant as string cast

File: deployment-stepper-modal.tsx:124-127

const llm =
  typeof deploymentDetail?.provider_data?.llm === "string"
    ? (deploymentDetail.provider_data.llm as string)  // 👈 redundant
    : "";

The typeof === "string" check already narrows the type. The cast is dead weight and hides the real type of provider_data.llm (likely unknown in the schema). Drop the cast and, ideally, type provider_data properly in Deployment (types.ts:95) — today it's Record<string, unknown>, which forces this pattern.


🟡 RECOMMENDED

7. The hydration useEffect in the context could be a useState initializer

File: contexts/deployment-stepper-context.tsx:276-295

The effect exists to populate state when initialState arrives asynchronously (after the attachments fetch resolves). Today:

  1. Provider mounts with empty Maps.
  2. initialState changes when the fetch resolves.
  3. Effect checks hasHydratedEditStateRef and populates state.

But deployment-stepper-modal.tsx:148-149 already uses key={...editingDeployment?.id...} to remount the Provider when the deployment changes. Combined, you can resolve this with useState(() => initialState ?? defaults) in the Provider and drop the effect — simplifies the logic and removes the hasHydratedEditStateRef.

Why it matters: the effect runs on every render until the guards short-circuit; in production it's cheap, but it widens the surface for subtle race bugs (between setState calls).


8. isAttachedVersion has 3 redundant code paths

File: step-attach-flows-version-panel.tsx:81-89

const isAttachedVersion =
  selectedVersionByFlow.has(attachmentKey) ||
  selectedVersionByFlow.get(selectedFlow.id)?.versionId === version.id ||
  Array.from(selectedVersionByFlow.values()).some(
    (entry) =>
      entry.flowId === selectedFlow.id &&
      entry.versionId === version.id,
  );

3 OR-checks for the same question:

  1. Has the canonical key?
  2. Has the legacy key (flowId) and the versionId matches?
  3. Any entry with these ids? (O(n) scan)

After normalizeSelectedFlowVersions, every key should be in canonical form — cases (2) and (3) only matter if non-normalized keys leak from state. Confirm and simplify to selectedVersionByFlow.has(attachmentKey). If the triple check is "defensive" for legacy data, it should be temporary and tracked by a ticket.


9. Naming — getScopedValue is too generic

File: contexts/deployment-stepper-context.tsx:155

getScopedValue doesn't reveal intent (REVIEWER_RULE.md §"Naming"). getValueByAttachmentKeyOrFlowId or getValueByScopedKey makes the fallback explicit.


10. // biome-ignore + // eslint-disable-next-line in step-attach-flows.tsx:202-204

File: step-attach-flows.tsx:202-257

Both ignores are present ("intentionally run only on mount"), but the effect has real dependencies (flows, initialFlowId, selectedVersionByFlow, detectEnvVars, defaultToolNameScopeId, setErrorData, setToolNameByFlow, updateDetectedEnvVars). The guard is handledPreselectedAttachmentRef.current === preSelected.key — valid, but the presence of two suppression comments hints at friction: either deps truly almost never change (and it's fine) or the effect should be refactored into a useMemo + minimal effect. Worth reviewing.


🟢 TESTING

Strengths:

  • edit-mode.test.tsx: covers happy path (multi-step edit) and adversarial ("blocks update flow when existing name does not start with a letter" — line 111).
  • deployment-stepper-payload-builders.test.tsx: covers buildConnectionPayloads, isNew filtering, deduplication.
  • step-attach-flows.test.tsx: tests real panel interaction (clean mocks, no leakage between tests).
  • step-review.test.tsx: covers tool name validation, duplicates, errors.

⚠️ Suggestions:

  • Tests for buildPayload* run inside the Provider via renderHook. After the split suggested in #1, those tests would become pure (describe('buildDeploymentPayload', () => { ... }) calling a pure function). Faster suite, no React lifecycle coupling.
  • I didn't see a regression test for "preserve removed flow state" (commit cbae4ff) — confirm there's a scenario covering "user clicks detach, navigates to another step, comes back — state preserved".

🔒 Security / PII / Integrations

All clean:

  • No console.*, no print() in modified files.
  • No hardcoded secrets — credentials (api_key, url) are collected via form input and sent through a mutation.
  • No eval/exec/dangerouslySetInnerHTML.
  • No : any or : object.
  • No user data in error messages (only generic toast titles).
  • No TODOs without ticket references.
  • No webhook/HMAC integration in the diff — N/A.
  • No AI/LLM logic in the diff — N/A.

📐 Architecture

Good:

  • Clean separation of presentational components (FlowListPanel, VersionPanel, ReviewSummaryCard, ReviewFlowConfigCard, ReviewDetachingSection, EditableToolName) — all memo-ed where appropriate.
  • Custom useConnectionPanelState hook properly extracted.
  • The comment block in deployment-stepper-modal.tsx:84-95 explaining the tool-name caching contract (Langflow ↔ wxO) is a perfect example of a "WHY comment", not a "WHAT comment".

⚠️ Watch:

  • The Provider remains the feature's "god object" — see #1.

📊 Final Score

Category Weight Status Findings
🔴 PII/Security Blocker Pass
🔴 DRY Blocker ⚠️ #2
🔴 File Structure Blocker ⚠️ #1
🟠 SRP/SOLID High ⚠️ #3, #4
🟠 Naming/Magic strings High ⚠️ #5, #9
🟠 Error Handling High Pass
🟠 Strong Typing High Pass (with #6 polish)
🟡 Observability Medium Pass
🟢 Testing Low Pass (with post-split suggestion)

✅ Action checklist for the author

Blockers (resolve before merge):

  • #1 Split deployment-stepper-context.tsx into helpers/version-scope.ts + helpers/deployment-payload-builders.ts + Provider.
  • #2 Extract getScopedToolName / getScopedValue and the equivalents in step-review/utils.ts into a single shared helper.

Important (preferably this PR):

  • #3 Replace the overloaded handleSelectVersion signature with a typed object.
  • #5 Standardize "Unknown flow" / "Unknown" / "Flow" into a single constant.
  • #6 Drop the redundant as string cast; type provider_data.llm.

Recommended (can ship as a follow-up PR):

  • #4 Move normalizeWxoName to a dedicated file.
  • #7 Consider replacing the hydration useEffect with a useState initializer.
  • #8 Simplify the triple isAttachedVersion check (assuming the normalizer guarantees the invariant).
  • #9 Rename getScopedValue to something more descriptive.
  • #10 Consider refactoring the effect at step-attach-flows.tsx:204 to remove the two suppressions.

Test suggestions post-split (#1):

  • After moving build*Payload to a pure module, convert deployment-stepper-payload-builders.test.tsx to pure tests (no renderHook).
  • Add an explicit regression case for "preserve removed flow state across step navigation".

@github-actions github-actions Bot removed the enhancement New feature or request label May 4, 2026
@github-actions github-actions Bot added the enhancement New feature or request label May 4, 2026
@github-actions github-actions Bot added enhancement New feature or request and removed enhancement New feature or request labels May 4, 2026
@github-actions github-actions Bot added enhancement New feature or request and removed enhancement New feature or request labels May 4, 2026
@viktoravelino viktoravelino requested a review from Cristhianzl May 4, 2026 14:16
… tool names

- use-connection-panel-state.test.ts: rename effectiveFlowId → effectiveAttachmentKey
  in baseParams() to match hook's refactored param name
- deployment-create.spec.ts: make SNAPSHOTS_DUPLICATE_MOCK route dynamic — echo back
  requested names as existing tools so duplicate check works with scoped names
  (getDefaultDeploymentToolName now generates "Flow {scope}-{id}" format)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions github-actions Bot added enhancement New feature or request and removed enhancement New feature or request labels May 4, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 4, 2026

Frontend Unit Test Coverage Report

Coverage Summary

Lines Statements Branches Functions
Coverage: 36%
36.65% (42980/117240) 67.69% (5856/8651) 36.71% (991/2699)

Unit Test Results

Tests Skipped Failures Errors Time
4074 0 💤 0 ❌ 0 🔥 7m 11s ⏱️

@github-actions github-actions Bot added enhancement New feature or request and removed enhancement New feature or request labels May 4, 2026
Copy link
Copy Markdown
Member

@Cristhianzl Cristhianzl left a comment

Choose a reason for hiding this comment

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

lgtm

@github-actions github-actions Bot added the lgtm This PR has been approved by a maintainer label May 5, 2026
@viktoravelino viktoravelino added this pull request to the merge queue May 8, 2026
Merged via the queue into release-1.10.0 with commit 1dd95e2 May 8, 2026
106 of 108 checks passed
@viktoravelino viktoravelino deleted the feat/deployment-multi-flow-versions branch May 8, 2026 13:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request lgtm This PR has been approved by a maintainer

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants