Skip to content

Add observation mode views for First Meeting onboarding (step 4)#1356

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

Add observation mode views for First Meeting onboarding (step 4)#1356
alex-nork merged 1 commit into
mainfrom
swarm/task-6

Conversation

@alex-nork

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

Copy link
Copy Markdown
Contributor

Summary

  • Create three new observation mode views (ObservationModeView, ObservationSessionView, ObservationSummaryView) as step 4 of the First Meeting onboarding flow
  • Wire the observation sub-flow into FirstMeetingFlowView with pitch -> session -> summary phase transitions
  • Add observationDurationMinutes and observationInsights properties to OnboardingState

Closes #1346

Test plan

  • Verify ObservationModeView displays the pitch message with the user's first task candidate
  • Verify duration selector chips (3/5/10 min) toggle correctly
  • Verify "Start observing" transitions to ObservationSessionView
  • Verify "Skip for now" completes onboarding without observation
  • Verify ObservationSessionView shows timer countdown and narration bubbles
  • Verify "Stop early" transitions to ObservationSummaryView
  • Verify ObservationSummaryView displays insights and proposal
  • Verify both "Let's try it" and "Maybe later" set observationCompleted and complete onboarding
  • Verify all Previews render correctly

🤖 Generated with Claude Code


Open with Devin

Create three new views for the observation mode flow that appears as
the final step in FirstMeetingFlowView:

- ObservationModeView: Pitch message referencing the user's first task
  candidate, duration selector (3/5/10 min), and start/skip buttons
- ObservationSessionView: Timer countdown with progress indicator,
  running narration bubbles (stubbed), and stop-early button
- ObservationSummaryView: Post-observation insights with sparkle icons,
  proposed first autonomous action, and let's-try-it/maybe-later buttons

Wire the three views into FirstMeetingFlowView step 4 using an
ObservationPhase enum (pitch -> session -> summary) for sub-step
transitions. Add observationDurationMinutes and observationInsights
properties to OnboardingState. Both completion paths (accept/decline)
set observationCompleted = true and call onComplete.

Closes #1346

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

@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 1 potential issue.

View 5 additional findings in Devin Review.

Open in Devin Review

Comment on lines +186 to +190
for i in 1..<Self.stubNarrations.count {
DispatchQueue.main.asyncAfter(deadline: .now() + narrationInterval * Double(i) + 2.0) {
addNextNarration()
}
}

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.

🔴 Scheduled narration callbacks continue to mutate shared state after observation stops

When the user taps "Stop early" or the timer naturally completes, stopObservation() only invalidates the Timer but does not cancel the pre-scheduled DispatchQueue.main.asyncAfter callbacks at lines 170 and 187. These callbacks call addNextNarration() which appends to state.observationInsights (line 205) even after the view has transitioned to ObservationSummaryView.

Root Cause and Impact

In startObservation(), narration messages are pre-scheduled using DispatchQueue.main.asyncAfter at fixed future times:

// Line 170: initial narration at +2s
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
    addNextNarration()
}
// Lines 186-190: remaining narrations at intervals
for i in 1..<Self.stubNarrations.count {
    DispatchQueue.main.asyncAfter(deadline: .now() + narrationInterval * Double(i) + 2.0) {
        addNextNarration()
    }
}

These dispatched blocks cannot be cancelled. When the user stops early, stopObservation() (line 208) only invalidates the Timer, but the asyncAfter blocks remain queued.

When they fire, addNextNarration() (line 193) appends to state.observationInsights (line 205). Since ObservationSummaryView reads state.observationInsights via its displayInsights computed property (ObservationSummaryView.swift:19-29), the insights list can grow while the summary is already being displayed, potentially causing the displayed insights to change mid-view or after the user has already read them.

For example, with a 5-minute observation (default), narrations are scheduled at ~62s intervals. If the user stops after 10 seconds, up to 4 remaining narration callbacks will fire over the next ~4 minutes, each appending to state.observationInsights.

Impact: Shared onboarding state is mutated after the observation phase has ended, which could cause the summary view's insight list to change unexpectedly.

Prompt for agents
Replace the DispatchQueue.main.asyncAfter scheduling approach with a cancellable mechanism. One approach: add a @State private var isStopped: Bool = false flag, set it to true in stopObservation(), and add a guard !isStopped else { return } check at the top of addNextNarration() (before the existing guard). Alternatively, use DispatchWorkItem instances stored in a @State array that can be cancelled in stopObservation(). The fix should be applied in ObservationSessionView.swift. Both the initial narration dispatch at line 170 and the loop dispatches at lines 186-190 need to be covered.
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: 8eab534b2d

ℹ️ 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 +208 to +210
private func stopObservation() {
timer?.invalidate()
timer = nil

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Cancel queued narration callbacks when stopping observation

stopObservation() only invalidates the repeating timer, but startObservation() also enqueues multiple DispatchQueue.main.asyncAfter callbacks that still call addNextNarration() after the session is stopped. When a user taps “Stop early” (or leaves this screen quickly), those delayed callbacks can continue appending to state.observationInsights, so the summary can change after observation has ended and onboarding state is mutated out of phase with the UI.

Useful? React with 👍 / 👎.

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.

M6: Observation mode for first task (Step 6)

1 participant