Skip to content

Fix progress dots mapping, persist variant, clamp restored step#1363

Merged
alex-nork merged 1 commit into
mainfrom
swarm/task-10
Feb 13, 2026
Merged

Fix progress dots mapping, persist variant, clamp restored step#1363
alex-nork merged 1 commit into
mainfrom
swarm/task-10

Conversation

@alex-nork

@alex-nork alex-nork commented Feb 13, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add totalSteps parameter to OnboardingProgressDots (default 7) with dynamic step-to-dot mapping, and pass totalSteps: 5 from FirstMeetingFlowView so all 5 dots are correctly used across the 5-step flow
  • Persist CLI-selected onboarding variant (--onboarding-variant first_meeting) to UserDefaults immediately in OnboardingWindow.show() so it survives app restarts
  • Clamp restored step to variant-specific max (4 for firstMeeting, 6 for default) in OnboardingState.init() to prevent out-of-range rendering when the saved step exceeds the variant's step count

Addresses review feedback from #1350.

Test plan

  • Launch with --onboarding-variant first_meeting and verify all 5 progress dots light up sequentially across steps 0-4
  • Launch default onboarding and verify dots still map correctly across steps 0-6
  • Kill app mid-onboarding with --onboarding-variant first_meeting, relaunch, and verify variant persists
  • Manually set onboarding.step to 6 in UserDefaults, launch with first-meeting variant, and verify step is clamped to 4

🤖 Generated with Claude Code


Open with Devin

- Add totalSteps parameter to OnboardingProgressDots (default 7) and compute
  step-to-dot mapping dynamically instead of hardcoded switch. Pass totalSteps: 5
  from FirstMeetingFlowView so all 5 dots are used across 5 steps.
- Persist CLI-selected onboarding variant to UserDefaults immediately in
  OnboardingWindow.show() so it survives app restarts.
- Clamp restored step to variant-specific max (4 for firstMeeting, 6 for
  default) in OnboardingState.init() to prevent out-of-range rendering.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@alex-nork alex-nork merged commit e584734 into main Feb 13, 2026
@alex-nork alex-nork deleted the swarm/task-10 branch February 13, 2026 02:15

@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: 3f8b72737a

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 23 to +24
state.onboardingVariant = .firstMeeting
UserDefaults.standard.set(OnboardingVariant.firstMeeting.rawValue, forKey: "onboarding.variant")

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 Re-clamp restored step after CLI variant override

When --onboarding-variant first_meeting is passed, show() switches state.onboardingVariant after OnboardingState.init() has already clamped currentStep using the previously persisted variant. If a user has onboarding.step saved as 5 or 6 from the default flow, this path leaves currentStep out of range for first-meeting (0...4), and FirstMeetingFlowView falls into its default case (EmptyView) instead of rendering a valid step. This makes the new clamp ineffective for the CLI-switch scenario the commit is targeting.

Useful? React with 👍 / 👎.

@devin-ai-integration devin-ai-integration 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.

Devin Review found 2 potential issues.

View 4 additional findings in Devin Review.

Open in Devin Review

Comment on lines +22 to +24
guard totalSteps > 1 else { return 0 }
let fraction = Double(currentStep) / Double(totalSteps - 1)
return min(Int(fraction * Double(totalDots - 1) + 0.5), totalDots - 1)

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.

🟡 Default flow progress dots regression: step 5 no longer lights the last dot

The new generic formula for activeDotIndex changes the default flow's step-to-dot mapping in a way that breaks the previous behavior.

Detailed mapping comparison and impact

The old hand-coded mapping was:

  • Steps 0,1 → dot 0
  • Step 2 → dot 1
  • Step 3 → dot 2
  • Step 4 → dot 3
  • Steps 5,6 (default) → dot 4

The new formula Int(fraction * Double(totalDots - 1) + 0.5) with totalSteps=7 produces:

  • Step 0 → dot 0
  • Steps 1,2 → dot 1
  • Step 3 → dot 2
  • Steps 4,5 → dot 3
  • Step 6 → dot 4

The critical difference is step 5 (ScreenPermissionStepView): it previously mapped to dot 4 (all 5 dots lit), but now maps to dot 3 (only 4 dots lit). This means users on the Screen Permission step see only 4/5 dots filled instead of 5/5, which is a visual regression.

Additionally, step 1 (NamingStepView) now advances the dot from 0 to 1, whereas before steps 0 and 1 shared dot 0. This changes the perceived progress pacing for the default onboarding flow.

Open in Devin Review

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

Comment on lines 23 to +24
state.onboardingVariant = .firstMeeting
UserDefaults.standard.set(OnboardingVariant.firstMeeting.rawValue, forKey: "onboarding.variant")

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.

🔴 Step clamping in init() runs before CLI variant override, leaving step out of range

When launching with --onboarding-variant first_meeting after a previous default-flow session saved a step > 4, the step is not properly clamped because the variant override happens after init() has already clamped.

Root cause and reproduction

OnboardingState is initialized at OnboardingWindow declaration time (clients/macos/vellum-assistant/Features/Onboarding/OnboardingWindow.swift:7). The init() at OnboardingState.swift:69-93 reads the variant from UserDefaults and clamps the step accordingly. However, in OnboardingWindow.show() at OnboardingWindow.swift:20-25, the variant is overridden from the command line argument after init() has already completed.

Reproduction:

  1. Run default onboarding, reach step 5, kill the app (saves onboarding.step=5, onboarding.variant=default)
  2. Relaunch with --onboarding-variant first_meeting
  3. OnboardingState.init() reads variant as .default from UserDefaults → clamps with maxStep=6currentStep stays at 5
  4. show() sets state.onboardingVariant = .firstMeeting
  5. Now currentStep=5 with onboardingVariant=.firstMeeting — step 5 hits the default: EmptyView() case in FirstMeetingFlowView.swift:82-83, rendering a blank screen

This only occurs on the first launch after switching variants via CLI when a higher step was previously saved. Subsequent launches work correctly because the variant is now persisted.

Suggested change
state.onboardingVariant = .firstMeeting
UserDefaults.standard.set(OnboardingVariant.firstMeeting.rawValue, forKey: "onboarding.variant")
state.onboardingVariant = .firstMeeting
UserDefaults.standard.set(OnboardingVariant.firstMeeting.rawValue, forKey: "onboarding.variant")
let maxStep = 4
if state.currentStep > maxStep {
state.currentStep = maxStep
}
Open in Devin Review

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

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