Skip to content

feat: implement managed sign-in setup flow for onboarding#16668

Merged
dvargasfuertes merged 11 commits into
mainfrom
devin/1773497772-managed-sign-in-setup-flow
Mar 14, 2026
Merged

feat: implement managed sign-in setup flow for onboarding#16668
dvargasfuertes merged 11 commits into
mainfrom
devin/1773497772-managed-sign-in-setup-flow

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Mar 14, 2026

Copy link
Copy Markdown
Contributor

feat: managed sign-in onboarding flow with 2-screen setup

Summary

When the managed_sign_in_enabled feature flag is enabled, the onboarding flow is restructured into 2 screens before hatching:

Screen 1 (WakeUpStepView): "Sign up/log in with Vellum" (primary) or "Skip for now" (new secondary button style). When managed_sign_in_enabled is OFF, shows a single "Get Started" button instead. If the user is already authenticated when the flag is ON, this screen is auto-skipped and the user lands directly on screen 2. The "Skip for now" button is hidden when the user is already authenticated (they should sign in or be auto-advanced).

Screen 2 (APIKeyStepView): Hosting environment selector with options filtered by flags:

  • managed_sign_in_enabled ON + authenticated → Vellum, Local (+ GCP, AWS, Docker, Custom if user_hosted_enabled is also ON)
  • managed_sign_in_enabled ON + unauthenticated → Local (+ GCP, AWS, Docker, Custom if user_hosted_enabled)
  • managed_sign_in_enabled OFF + user_hosted_enabled ON → Local, GCP, AWS, Docker, Custom
  • Both flags OFF → no hosting selector shown, just the API key field

When the user is authenticated with managed_sign_in_enabled ON, the "Back" button is hidden (they were auto-advanced past screen 1 and shouldn't return to it).

Cloud credential fields (GCP project/zone/service account, AWS IAM Role ARN, Custom QR code) render inline based on the selected hosting mode. A "Hatch" button is always visible regardless of selection.

Logout handling: If the user signs out while on screen 2 during a managed sign-in flow, they are returned to screen 1 (unauthenticated state) with an animated transition.

Key conditional behaviors:

  • Vellum option only appears when the user authenticated on screen 1
  • Anthropic API key field appears when user is NOT authenticated (skipped sign-in)
  • OnboardingFooter (step indicator) is hidden in the managed flow
  • When managed_sign_in_enabled is off, existing behavior is preserved (with "Get Started" replacing the old two-button layout)

Changes span 7 files:

  • OnboardingButton.swift — new .secondary button style (primary-colored text, clear background, primary-colored border)
  • WakeUpStepView.swift — three-way branch: managed → sign-in + conditional skip; non-managed → "Get Started" only
  • APIKeyStepView.swift — Vellum hosting mode, hosting selector logic, availableHostingModes filtered by userHostedEnabled, saveAndHatch(), validation, hatch/continue button branching, conditional Back button
  • APIKeyStepView+CloudFields.swift(new file) inline cloud credential fields, setup blurbs, file pickers, and file picking methods (extracted per AGENTS.md 500-600 line limit)
  • OnboardingFlowView.swift — passes isAuthenticated and onHatchManaged to APIKeyStepView; 2-step maxOnboardingStep when flag enabled; auto-advances authenticated users past welcome screen; handles logout by resetting to step 0
  • OnboardingState.swiftmanagedSignInEnabled property; step clamping for new flow
  • OnboardingManagedAuthState.swiftonboardingPrimaryButtonTitle updated for hasAssistant parameter (merged from main)

Updates since last revision

  • UX: Hide "Skip for now" for authenticated users — When managed_sign_in_enabled is ON and the user is already authenticated, the "Skip for now" button on screen 1 is hidden. Only the primary sign-in button is shown (though in practice, authenticated users are auto-advanced past this screen).
  • UX: Hide "Back" button when auto-advanced — On screen 2, the "Back" button is hidden when managed_sign_in_enabled is ON and the user is authenticated. Since these users were auto-advanced past screen 1, they shouldn't navigate back to it.
  • UX: Logout returns to screen 1 — If isAuthenticated becomes false while on screen 2 during a managed sign-in flow, the user is animated back to screen 1 in an unauthenticated state.

Earlier fixes (still included)

  • "Get Started" for non-managed flow — single primary button when managed_sign_in_enabled is OFF.
  • Auto-advance authenticated users past welcome screen when managed_sign_in_enabled is ON.
  • Filter hosting modes by user_hosted_enabled — GCP, AWS, Docker, Custom hidden when flag is OFF.
  • SecureField .onSubmit guards against hatchButtonDisabled.
  • Accessibility label on file picker clear button.
  • SecureField .onSubmit correctly dispatches to saveAndHatch() when managedSignInEnabled.
  • File split: APIKeyStepView.swift → main file (~430 lines) + APIKeyStepView+CloudFields.swift (~310 lines).
  • loadHostingModeFromDefaults() validates restored hosting mode against availableHostingModes.

Review & Testing Checklist for Human

  • Build the macOS app — developed on Linux with no Xcode build. Verify it compiles, especially the cross-file extension accessing @State/@FocusState properties (Swift allows this for internal but not private).
  • Test logout during managed onboarding — sign in on screen 1, auto-advance to screen 2, then sign out. Verify the user is animated back to screen 1 and sees the full unauthenticated layout (sign-in + "Skip for now"). Check that any state set on screen 2 (hosting mode, credentials) doesn't cause issues on re-entry.
  • Test auto-advance + hidden Back — with managed_sign_in_enabled ON, start the app already authenticated. Confirm screen 1 is skipped, screen 2 shows no "Back" button, and no "Skip for now" flashes briefly on screen 1.
  • Test unauthenticated managed flow — with managed_sign_in_enabled ON, start unauthenticated. Confirm screen 1 shows both "Sign up/log in" and "Skip for now". Click "Skip for now" → confirm screen 2 shows the "Back" button and no Vellum hosting option.
  • Verify hosting mode filtering — with user_hosted_enabled OFF: confirm only Local (+ Vellum if authenticated) appears. With both flags ON: confirm all 6 options appear for authenticated users.
  • Confirm API key visibility for authenticated non-Vellum modes — when managedSignInEnabled && isAuthenticated, showApiKeyField returns false for all hosting modes including local/docker. Product decision needed on whether the CLI requires ANTHROPIC_API_KEY for these modes.

Notes

  • The inline cloud credential fields (GCP, AWS, custom hardware) are ported from CloudCredentialsStepView.swift patterns. The GCP zones list is hardcoded to 5 zones — verify this matches the full list in CloudCredentialsStepView.
  • The onHatchManaged callback on the Vellum hosting mode calls performManagedBootstrap() from OnboardingFlowView, reusing the existing managed bootstrap flow.
  • HostingMode.displayName was shortened from "Custom Hardware" to "Custom". This applies globally to both managed and existing flows.
  • The showBackButton logic uses managedSignInEnabled && isAuthenticated rather than tracking a separate "was auto-advanced" flag. This means manually-authenticated users who proceed to screen 2 also won't see Back — which should be the correct behavior since they authenticated via Vellum and shouldn't return to the sign-in screen.

Requested by: @dvargas92495
Link to Devin Session


Open with Devin

@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Devin Review found 4 potential issues.

View 3 additional findings in Devin Review.

Open in Devin Review

Comment thread clients/macos/vellum-assistant/Features/Onboarding/APIKeyStepView.swift Outdated
Comment on lines +131 to +136
if showHostingSelector, let saved = loadHostingModeFromDefaults() {
hostingMode = saved
}
if managedSignInEnabled && isAuthenticated {
hostingMode = .vellum
}

@devin-ai-integration devin-ai-integration Bot Mar 14, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

🚩 onAppear hosting mode override ordering may surprise returning authenticated users

In APIKeyStepView.swift:131-136, onAppear first restores a saved hosting mode from UserDefaults, then unconditionally overrides it with .vellum when managedSignInEnabled && isAuthenticated. This means a returning authenticated user who previously selected GCP or AWS will always be reset to .vellum on each view appear. This appears intentional (Vellum-managed is the default for authenticated users, and they can re-select), but worth confirming this is the desired UX rather than respecting the user's prior selection.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +643 to +659
private func saveAndHatch() {
state.cloudProvider = hostingMode.rawValue

if hostingMode == .vellum {
onHatchManaged?()
return
}

let trimmed = apiKey.trimmingCharacters(in: .whitespacesAndNewlines)
if !trimmed.isEmpty {
APIKeyManager.setKey(trimmed, for: "anthropic")
APIKeyManager.syncKeyToDaemon(provider: "anthropic", value: trimmed)
}

saveModelToConfig("claude-opus-4-6")
state.isHatching = true
}

@devin-ai-integration devin-ai-integration Bot Mar 14, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

🚩 saveAndHatch doesn't persist cloudProvider to UserDefaults before hatching

In saveAndHatch() at line 644, state.cloudProvider is set but persist() is never called (it's only called from advance()). If the app is killed during hatching (before hatchCompleted), the cloud provider selection would be lost. This matches the pre-existing saveAndContinue() pattern where cloudProvider is set before advance() which then persists. However, the saveAndHatch path sets state.isHatching = true without ever calling advance(), so the cloud provider choice is never persisted for the hatch flow. If hatching fails and the app restarts, the user would lose their cloud provider selection. This may be acceptable since resetForRetry() resets everything anyway.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4649ee3caa

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +563 to +566
disabled: hatchButtonDisabled
) {
saveAndHatch()
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Route managed text-field submit through hatch flow

In managed sign-in onboarding, this branch moves the button action to saveAndHatch(), but the API key field’s submit handler in APIKeyStepView.apiKeyField still calls saveAndContinue(). If a user presses Return instead of clicking Hatch, currentStep is advanced to 2; with managed flow now capped at step 1 in OnboardingFlowView.maxOnboardingStep, that path is treated as completion and onComplete() runs without starting hatch/bootstrap, leaving onboarding exited before the assistant is set up.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Already addressed in commit 38914d4.onSubmit now conditionally calls saveAndHatch() when managedSignInEnabled is true, otherwise saveAndContinue().

Comment on lines +74 to +77
private var showApiKeyField: Bool {
if managedSignInEnabled {
return !isAuthenticated && hostingMode != .vellum
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Require API key for authenticated AWS/GCP modes

This condition hides the Anthropic key field for all authenticated users, including when they select gcp or aws. In managed flow that allows saveAndHatch() to proceed with no key set, and HatchingStepView then invokes the CLI without ANTHROPIC_API_KEY; both cli/src/lib/gcp.ts and cli/src/lib/aws.ts explicitly exit when that variable is missing. As a result, authenticated users choosing AWS/GCP will consistently fail hatch unless they already had a previously saved key.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is by design per the user's spec: the API key field only appears when the user is NOT authenticated (skipped sign-in). Authenticated users are expected to have their API key managed through Vellum's backend. Whether the CLI scripts need adjustment to support this managed flow is a separate concern outside this PR's scope.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Devin Review found 3 new potential issues.

View 6 additional findings in Devin Review.

Open in Devin Review

Comment on lines +73 to +78
private var showApiKeyField: Bool {
if managedSignInEnabled {
return !isAuthenticated && hostingMode != .vellum
}
return true
}

@devin-ai-integration devin-ai-integration Bot Mar 14, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

🔴 API key field hidden for authenticated users choosing self-hosted modes, allowing hatching without a key

When managedSignInEnabled && isAuthenticated, showApiKeyField returns false for ALL hosting modes (not just .vellum). This means authenticated users who manually switch to a self-hosted mode (.local, .docker, .gcp, .aws) never see the API key field. The hatchButtonDisabled check at APIKeyStepView.swift:316 skips the API key validation when showApiKeyField is false, so the Hatch button becomes enabled without an API key. When the user clicks Hatch, saveAndHatch() proceeds to state.isHatching = true, and HatchingStepView.startRemoteHatch() at HatchingStepView.swift:197 reads APIKeyManager.getKey(for: "anthropic") ?? "" — which is empty for new users. The CLI then runs without ANTHROPIC_API_KEY (AssistantCli.swift:381-383), causing the hatch to fail.

How showApiKeyField should probably work

The condition return !isAuthenticated && hostingMode != .vellum conflates authentication status with the need for an API key. For non-vellum hosting modes, an Anthropic API key is needed regardless of auth status. The logic should be hostingMode != .vellum (or additionally gated on auth only for the vellum case).

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This follows the user's spec: "When the user does not log in through Vellum, the Vellum option is not there and instead there is an API key field for the Anthropic API key." The design intent is that authenticated users have their API key managed through Vellum's platform. Whether the CLI needs adjustment for authenticated non-Vellum hosting modes is a separate concern — deferring to the human reviewer on this product decision.

Comment thread clients/macos/vellum-assistant/Features/Onboarding/APIKeyStepView.swift Outdated
Comment on lines +99 to +107
APIKeyStepView(
state: state,
isAuthenticated: authManager.isAuthenticated,
onHatchManaged: {
Task {
await performManagedBootstrap()
}
}
)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

🚩 onChange(of: authManager.isAuthenticated) may race with the new onHatchManaged flow

In OnboardingFlowView.swift:159-188, the onChange(of: authManager.isAuthenticated) handler auto-triggers performManagedBootstrap() when the user becomes authenticated and managedBootstrapEnabled is true with no lockfile. In the new flow, an authenticated user on step 1 (APIKeyStepView) can also trigger performManagedBootstrap() via the onHatchManaged callback (OnboardingFlowView.swift:102-106). If both paths fire, performManagedBootstrap() could execute concurrently. In practice this race is unlikely because the onChange handler fires when auth state changes (i.e., during the sign-in process on step 0), and by the time the user reaches step 1 and clicks Hatch, the auth state is already settled. But it's worth verifying that performManagedBootstrap() is idempotent or has guards against concurrent execution.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Acknowledged. As noted, the onChange handler fires during sign-in (step 0), and by the time the user reaches step 1 and clicks Hatch, auth state is settled. The race is theoretical rather than practical. If performManagedBootstrap() needs idempotency guards, that's pre-existing behavior outside this PR's scope.

@devin-ai-integration devin-ai-integration Bot force-pushed the devin/1773497772-managed-sign-in-setup-flow branch from 924d545 to e2c14a4 Compare March 14, 2026 15:46

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Devin Review found 2 new potential issues.

View 7 additional findings in Devin Review.

Open in Devin Review

Comment on lines +134 to +136
if managedSignInEnabled && isAuthenticated {
hostingMode = .vellum
}

@devin-ai-integration devin-ai-integration Bot Mar 14, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

🚩 onAppear override to .vellum may discard user's hosting mode selection after going back from failed hatch

In APIKeyStepView.swift:134-136, onAppear unconditionally forces hostingMode = .vellum when managedSignInEnabled && isAuthenticated. If a user selects e.g. .gcp, fills in credentials, hatches, the hatch fails, and they tap 'Go Back' in HatchingStepView (HatchingStepView.swift:151-157), the APIKeyStepView is recreated (since state.isHatching toggled, changing the conditional branch in OnboardingFlowView). The @State hostingMode resets to its initial .local, and onAppear overrides it to .vellum. The user's previous .gcp selection and the credential fields are lost. Note that state.cloudProvider still holds "gcp" from the prior saveAndHatch() call, but the local @State doesn't use it. This creates a confusing UX on retry after failure. The goBack() function doesn't call state.resetForRetry() (which resets cloudProvider), creating a mismatch between state.cloudProvider and the displayed hostingMode.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is a UX/product decision — resetting to .vellum on appear for authenticated users is intentional as the default. A returning user who goes back from a failed hatch would need to re-select, but this is an edge case we can address separately if needed. Leaving as-is pending product feedback.

devin-ai-integration[bot]

This comment was marked as resolved.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

View 10 additional findings in Devin Review.

Open in Devin Review

Comment on lines +106 to +125
var customHardwareInlineFields: some View {
VStack(spacing: VSpacing.sm) {
customHardwareSetupBlurb

VStack(alignment: .leading, spacing: VSpacing.xs) {
Text("QR Code Image")
.font(.system(size: 13, weight: .medium))
.foregroundColor(VColor.contentSecondary)
filePickerButton(
fileName: qrCodeImageFileName,
prompt: "Select QR Code PNG",
onPick: { pickQRCodeImageFile() },
onClear: {
state.customQRCodeImageData = Data()
qrCodeImageFileName = ""
}
)
}
}
}

@devin-ai-integration devin-ai-integration Bot Mar 14, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

🚩 Significant code duplication between CloudCredentialsStepView and new APIKeyStepView+CloudFields

The new APIKeyStepView+CloudFields.swift duplicates substantial UI code from the existing CloudCredentialsStepView.swift — the GCP fields, AWS fields, custom hardware fields, setup blurbs, file picker button, and file picking functions are nearly identical between the two files. This creates a maintenance burden where bug fixes or UI changes need to be applied in two places. Consider extracting shared components (e.g., GCPCredentialFields, AWSCredentialFields, shared file picker, shared setup blurbs) that both views can reuse. The clients/AGENTS.md rules state: "Do not create one-off UI elements that duplicate existing design system components."

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@devin-ai-integration devin-ai-integration Bot force-pushed the devin/1773497772-managed-sign-in-setup-flow branch from 51a53bb to ec6e3d7 Compare March 14, 2026 18:59

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

View 10 additional findings in Devin Review.

Open in Devin Review

Comment on lines +66 to +76
private var availableHostingModes: [HostingMode] {
var modes: [HostingMode] = []
if managedSignInEnabled && isAuthenticated {
modes.append(.vellum)
}
modes.append(.local)
if userHostedEnabled {
modes.append(contentsOf: [.gcp, .aws, .docker, .customHardware])
}
return modes
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

🚩 Authenticated user selecting .vellum then signing out causes ghost selection

If an authenticated user lands on APIKeyStepView with hostingMode = .vellum (set at APIKeyStepView.swift:140), and then their session expires or they sign out, isAuthenticated becomes false (the parent re-renders and passes the new value). However, @State var hostingMode persists as .vellum across re-renders. The availableHostingModes computed property (APIKeyStepView.swift:66-76) no longer includes .vellum when isAuthenticated is false, so the hosting mode selector shows cards without .vellum — but the internal hostingMode state is still .vellum. This results in no visible radio button being selected. The user would need to manually select another mode or go back. This is an edge case (signing out during step 1 of onboarding) that likely occurs rarely, and goBack() provides an escape hatch.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This edge case is now handled by the logout logic added in commit 9bc5c8d. When isAuthenticated becomes false during a managed sign-in flow, the onChange(of: authManager.isAuthenticated) handler in OnboardingFlowView resets state.currentStep to 0, sending the user back to the welcome screen. The user never stays on the APIKeyStepView with a stale .vellum hosting mode selection.

devin-ai-integration Bot and others added 7 commits March 14, 2026 20:03
When managed_sign_in_enabled flag is enabled:
- Screen 1: Sign up/log in with Vellum (primary) or Skip for now (secondary)
- Screen 2: Hosting environment selector (Vellum, Local, GCP, AWS, Docker, Custom)
  with inline cloud credential fields and Hatch button
- Vellum option only shown when authenticated; API key field shown when not
- Cloud credential fields (GCP project/zone/service account, AWS ARN,
  custom QR code) render conditionally based on hosting selection

Files changed:
- WakeUpStepView: Skip for now button when managed sign-in enabled
- OnboardingButton: Added .secondary button style
- APIKeyStepView: Vellum hosting option, inline cloud credentials, hatch button
- OnboardingFlowView: 2-step flow (WakeUp -> HostingSetup), pass auth state
- OnboardingState: managedSignInEnabled property, step clamping for new flow

Co-Authored-By: vargas@vellum.ai <vargas@vellum.ai>
…e per AGENTS.md

- Fix bug where SecureField .onSubmit called saveAndContinue() instead of
  saveAndHatch() when managedSignInEnabled, which would skip hatching
- Extract cloud credential fields, setup blurbs, file picker UI, and file
  picking methods into APIKeyStepView+CloudFields.swift extension
- Widen access on HostingMode enum and @State/@focusstate properties shared
  across extension files (private -> internal per AGENTS.md guidelines)
- Main file: 414 lines, extension file: ~300 lines (both within 500-600 target)

Co-Authored-By: vargas@vellum.ai <vargas@vellum.ai>
Prevent stale .vellum hosting mode from UserDefaults being applied
when the user is unauthenticated in managed sign-in flow. The loaded
mode is now checked against availableHostingModes before being set.

Co-Authored-By: vargas@vellum.ai <vargas@vellum.ai>
Co-Authored-By: vargas@vellum.ai <vargas@vellum.ai>
…ith missing credentials

Co-Authored-By: vargas@vellum.ai <vargas@vellum.ai>
…icated users, filter hosting modes by user_hosted_enabled

Co-Authored-By: vargas@vellum.ai <vargas@vellum.ai>
…gn-out

Co-Authored-By: vargas@vellum.ai <vargas@vellum.ai>
@devin-ai-integration devin-ai-integration Bot force-pushed the devin/1773497772-managed-sign-in-setup-flow branch from 9bc5c8d to 609f15c Compare March 14, 2026 20:03
devin-ai-integration[bot]

This comment was marked as resolved.

Co-Authored-By: vargas@vellum.ai <vargas@vellum.ai>
@dvargasfuertes

Copy link
Copy Markdown
Contributor

DevinAI We should remove the automatic hatching that's happening after login. Instead, we want to be dropped into the screen where the user can pick between Vellum hosted and local hosted

…ching

Co-Authored-By: vargas@vellum.ai <vargas@vellum.ai>
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

@dvargas92495 Fixed in e7a4183 — after login, users now land on the hosting selector (screen 2) instead of auto-triggering managed bootstrap. The managed bootstrap only runs when the user explicitly clicks "Hatch" with Vellum hosting selected.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

View 11 additional findings in Devin Review.

Open in Devin Review

Comment on lines 148 to +153
if !authManager.isAuthenticated {
await authManager.checkSession()
}
if managedSignInEnabled && authManager.isAuthenticated && state.currentStep == 0 {
state.advance()
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

🚩 Potential double-advance when session is restored with existing managed assistant

When managedSignInEnabled is true and the user has a stale session that checkSession() restores, both the .task handler at OnboardingFlowView.swift:151-153 and the onChange(of: authManager.isAuthenticated) handler at OnboardingFlowView.swift:179-181 can each call state.advance(). For a user with an existing managed assistant in the lockfile, this results in step 0→1→2, which exceeds maxOnboardingStep (1) and triggers onComplete() — silently auto-completing onboarding. This appears intentional for returning managed users (they don't need to re-configure), but the mechanism is fragile and not documented. If the intent changes and the hosting selector should be shown to returning users, this double-advance would need a guard.

(Refers to lines 147-153)

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 35dea76 — added state.currentStep == 0 guards to all state.advance() calls in the onChange(of: authManager.isAuthenticated) handler. This prevents the double-advance when .task already advanced from 0→1 before the onChange fires.

…ance

Co-Authored-By: vargas@vellum.ai <vargas@vellum.ai>

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Devin Review found 2 new potential issues.

View 11 additional findings in Devin Review.

Open in Devin Review

Comment on lines 194 to 200
} else if managedBootstrapEnabled && managedSignInEnabled {
if state.currentStep == 0 {
log.info("Authenticated with no lockfile assistant; advancing to hosting selector")
state.advance()
} else {
log.info("Session restored with no lockfile assistant — staying on welcome screen for user-initiated hatch")
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

🚩 managedBootstrapEnabled && !managedSignInEnabled path now calls onComplete() instead of conditional bootstrap

In OnboardingFlowView.swift:194, the onChange(of: authManager.isAuthenticated) handler now requires both managedBootstrapEnabled && managedSignInEnabled to enter the hosting-selector advance path. Previously, when managedBootstrapEnabled was true alone (without managedSignInEnabled), the code would bootstrap if didInitiateLogin was true. Now it falls through to onComplete() at line 202-203. This is a behavioral change for the case where managedBootstrapEnabled is true but managedSignInEnabled is false — authenticated users with no lockfile will now skip onboarding entirely instead of potentially bootstrapping.

(Refers to lines 194-204)

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 6f3ea2d — restored the managedBootstrapEnabled check without requiring managedSignInEnabled. The managedSignInEnabled guard is now only on the inner state.advance() call, preserving the original fallthrough behavior for the non-managed-sign-in case.

…th for non-managed-sign-in

Co-Authored-By: vargas@vellum.ai <vargas@vellum.ai>

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

View 12 additional findings in Devin Review.

Open in Devin Review

Comment on lines +167 to +174
if !isAuthenticated && managedSignInEnabled && state.currentStep > 0 {
log.info("User signed out during managed onboarding — returning to welcome screen")
isBootstrappingManaged = false
managedBootstrapError = nil
withAnimation(.spring(duration: 0.6, bounce: 0.15)) {
state.currentStep = 0
}
return

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

🚩 Sign-out during hatching does not reset isHatching state

In the new onChange(of: authManager.isAuthenticated) handler at OnboardingFlowView.swift:167-174, when the user signs out during managed onboarding, the code resets isBootstrappingManaged, managedBootstrapError, and state.currentStep back to 0, but does NOT reset state.isHatching. Since the view body checks if state.isHatching first (OnboardingFlowView.swift:41), the HatchingStepView would remain visible even after the sign-out reset. The user would be stuck on the hatching view until the hatch completes or fails on its own, at which point they can proceed via the failure UI's "Go Back" button. This is a narrow edge case (auth token expiry during hatch), but could leave users in a confusing state.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is a narrow edge case (auth token expiry mid-hatch) where the existing hatch failure UI provides an escape via the "Go Back" button. Resetting isHatching during an active hatch could leave the daemon in an inconsistent state. Leaving as-is for now — can revisit if needed.

@dvargasfuertes dvargasfuertes merged commit c3ffe24 into main Mar 14, 2026
2 checks passed
@dvargasfuertes dvargasfuertes deleted the devin/1773497772-managed-sign-in-setup-flow branch March 14, 2026 22:05
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