feat(kiloclaw) onboarding calendar connect step#3083
Open
Conversation
Contributor
Code Review SummaryStatus: No Issues Found | Recommendation: Merge Files Reviewed (3 files)
Resolved Findings
Reviewed by gpt-5.5-20260423 · 367,152 tokens |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Add a Connect a calendar step to the KiloClaw onboarding wizard. New users see this between identity capture and channel selection. The step wraps the existing Google Calendar OAuth flow at /api/integrations/google/connect (read access only, no writes), with a Skip for now option that is always available.
To make the OAuth round trip land users back in the wizard rather than stranding them on Settings, this PR adds returnTo support to the Google OAuth state, connect, and callback routes. The signed state token validates returnTo against a strict relative path regex, blocks URI fragments, blocks path traversal segments (
./..), and caps length at 2048 characters. The callback honors returnTo on both success and error paths.The Connect button gates on the kiloclaw instance row existing in the DB (a fast write that completes shortly after provisioning kicks off, distinct from the slower Fly machine readiness). While the row is missing, the button reads "Setting up your instance…" and is disabled. Skip for now stays enabled throughout, so the user is never blocked.
After the OAuth round trip, the wizard hydrates local bot identity state from the persisted instance row so the user resumes at the calendar step instead of restarting from identity. A watchdog effect cleans the URL and surfaces a soft warning if hydration cannot complete within 5 seconds.
PostHog tracking covers the full funnel:
claw_setup_calendar_viewed(fires on render, matching the identity step pattern)claw_setup_calendar_connect_clickedclaw_setup_calendar_oauth_completedclaw_setup_calendar_oauth_failed(reason allowlisted; unknown codes bucketed asunknown)claw_setup_calendar_resumed(outcome: connected / error / unknown)claw_setup_calendar_completed(withskippedandconnectedflags)Verification
/claw/new. Identity step works as before. Calendar step renders with correct copy and the Recommended pill./claw/new?step=calendar&success=google_connected. Wizard restored bot identity, set the step to calendar, showed a "Calendar connected" toast, and stripped the query params from the URL./claw/new?step=calendar&error=access_denied. Wizard showed an error toast, firedclaw_setup_calendar_oauth_failedwith reason'access_denied', and cleaned the URL./claw/settings(returnTo unset, fallback path).//evil.example.com,https://evil.example.com/foo,/claw/new#frag,/claw/../adminall fall through to the no-returnTo fallback.Visual Changes
Reviewer Notes
flowState.instanceStatus !== null(the DB row written by the provision API), not on Fly machine readiness. The OAuth state itself just needs to know which instance to associate the connection with; the gateway being up is only required later when calendar data is actually read.instanceReadyis a required (non optional) prop onCalendarConnectStepViewso any future caller that forgets to pass it fails typecheck. Same defense in depth pattern used forassistantEmojipreviously.botIdentityhydration cannot complete fromflowState.instanceStatus, the watchdog cleans the URL and shows a soft warning rather than leaving stale?step=calendarparams lingering on the identity step.buildGoogleRedirectPathin the callback route requires a pre-encodedkey=valuefragment;appendQueryParamtakes raw key plus value and encodes both. JSDoc on each function states the contract; the parameter name ispreEncodedQueryFragmentfor emphasis.