feat(onboarding): replace welcome-agent bot with react-joyride walkthrough#1180
Conversation
…rough Replace the post-wizard welcome-agent chat lockdown with a deterministic Joyride product tour that highlights real app surfaces (Home, Chat, Skills, Automation, Settings tabs). - Add react-joyride v3 with custom tooltip matching design system - Set walkthrough pending flag on wizard completion instead of creating welcome thread / firing synthetic chatSend trigger - Comment out (not delete) all welcome-lock logic with [tinyhumansai#1123] markers: isWelcomeLocked, welcomeThreadId, WelcomeThinkingTypewriter, ONBOARDING_WELCOME_THREAD_LABEL, thread filtering, nav hiding - Flip chat_onboarding_completed=true on onboarding_completed false→true transition in config/ops.rs so routing goes to orchestrator immediately - Walkthrough state via localStorage (openhuman:walkthrough_pending/completed) - Add data-walkthrough attributes to Home.tsx and BottomTabBar.tsx - Tests: 10 walkthrough tests, updated OnboardingLayout + coreState tests Closes tinyhumansai#1123
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughReplaces the conversational welcome-agent onboarding with a frontend React Joyride walkthrough: adds react-joyride and AppWalkthrough (with localStorage helpers), mounts the walkthrough UI and step targets, removes welcome-thread creation/locking/gating across frontend code, and normalizes onboarding flags in backend config load/save. ChangesOnboarding Flow Replacement
Sequence Diagram(s)sequenceDiagram
participant User
participant OnboardingWizard as Onboarding\nWizard
participant Home
participant AppWalkthrough as AppWalkthrough\n(Joyride)
participant LocalStorage as LocalStorage
User->>OnboardingWizard: Complete wizard
OnboardingWizard->>LocalStorage: setWalkthroughPending() (pending='true')
OnboardingWizard->>Home: Navigate to /home (replace)
Home->>AppWalkthrough: Mount (if not onboarding route)
AppWalkthrough->>LocalStorage: isWalkthroughPending()
LocalStorage-->>AppWalkthrough: pending=true
AppWalkthrough->>User: Show step (targets via data-walkthrough)
User->>AppWalkthrough: Next / Back / Skip / Finish
AppWalkthrough->>LocalStorage: markWalkthroughComplete() (on finish/skip)
LocalStorage-->>AppWalkthrough: pending cleared
AppWalkthrough->>Home: Unmount (run=false)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. 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.
Built for teams:
One agent for your entire SDLC. Right inside Slack. Comment |
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (3)
app/src/pages/__tests__/Conversations.welcomeLock.test.tsx (1)
12-17: ⚡ Quick winReplace the skipped placeholder with one unlocked-flow assertion.
it.skip(...)drops coverage for the migrated Conversations path entirely. Since this PR changes the post-onboarding behavior, keep at least one behavior test here that the normal Conversations affordances remain available whenchatOnboardingCompletedis false.As per coding guidelines "Prefer testing behavior over implementation details; use existing helpers from
app/src/test/before adding new harness code."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/pages/__tests__/Conversations.welcomeLock.test.tsx` around lines 12 - 17, Replace the skipped placeholder test with a real behavior test that mounts the Conversations UI and asserts the normal conversation affordances are available when chatOnboardingCompleted is false: use existing test helpers from app/src/test/ (e.g., the app render/store helpers) to render the Conversations component with initial state setting chatOnboardingCompleted: false, then assert a user-facing element such as the conversation list or compose input is present (reference the Conversations component and the chatOnboardingCompleted flag) so the test covers the unlocked flow instead of skipping it.app/src/pages/onboarding/OnboardingLayout.tsx (1)
10-10: 🏗️ Heavy liftKeep walkthrough state in the persisted app state layer.
This introduces new onboarding UI state through a component-owned
localStoragehelper instead of the configured persistence path. That makes the walkthrough flags drift independently from the rest of onboarding state and bypasses store rehydration/migration/reset behavior. As per coding guidelines, "Use Redux and persist where configured for app state; prefer Redux over ad hoc localStorage for state management."Also applies to: 132-133
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/pages/onboarding/OnboardingLayout.tsx` at line 10, The component currently sets walkthrough flags via the localStorage helper (see setWalkthroughPending / AppWalkthrough import) which bypasses the app's persisted Redux state; replace this ad‑hoc localStorage usage with the app's persisted store by dispatching the appropriate Redux action (or adding one) to update the onboarding/walkthrough slice so flags are stored via the configured persistence path, remove the direct localStorage reads/writes in OnboardingLayout (and any related usage at the other occurrence around lines noted), and ensure reducers/selectors are updated so rehydration/migration/reset behavior applies to the walkthrough state.app/src/components/walkthrough/WalkthroughTooltip.tsx (1)
26-37: ⚡ Quick winRender Joyride content as nodes, not strings.
step.titleandstep.contentare node-like values here, so theas stringcasts will stringify any richer tooltip content to[object Object]. The<p>wrapper also makes future non-text content awkward to render.♻️ Suggested change
{step.title && ( <h3 className="text-sm font-semibold text-stone-900 leading-snug flex-1"> - {step.title as string} + {step.title} </h3> )} @@ - <p className="text-sm text-stone-600 leading-relaxed mb-4">{step.content as string}</p> + <div className="text-sm text-stone-600 leading-relaxed mb-4">{step.content}</div>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/components/walkthrough/WalkthroughTooltip.tsx` around lines 26 - 37, The component is currently casting step.title and step.content to strings which will stringify node-like content; in WalkthroughTooltip.tsx remove the "as string" casts and render {step.title} and {step.content} directly so richer React nodes display properly, and replace the <p> wrapper around step.content with a more generic container (e.g., a <div> with the same classes) to avoid forcing plain text semantics for non-text children.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/src/components/walkthrough/__tests__/AppWalkthrough.test.tsx`:
- Around line 86-116: Add a test that simulates Joyride completing or being
skipped and asserts that AppWalkthrough writes the completion flag to
localStorage; locate the AppWalkthrough component import used in these tests and
after rendering call the Joyride mock's completion callback (the handler wired
to the mock with testId 'joyride-mock' / onCallback) with an event indicating
action: 'finish' or 'skip', then assert WALKTHROUGH_KEY (and optionally
WALKTHROUGH_PENDING_KEY cleared) is set to 'true' to ensure the component
persists completion state.
In `@app/src/components/walkthrough/AppWalkthrough.tsx`:
- Around line 16-40: Wrap all localStorage accesses in isWalkthroughPending,
setWalkthroughPending, and markWalkthroughComplete with try/catch to prevent
SecurityError/quota exceptions from bubbling up; for isWalkthroughPending return
a safe default (false) on error, and for
setWalkthroughPending/markWalkthroughComplete swallow the error (or log via
console.warn/console.debug) so onboarding flow continues without thrown
exceptions; reference the constants WALKTHROUGH_PENDING_KEY and WALKTHROUGH_KEY
and ensure you still perform the same logical operations
(getItem/setItem/removeItem) inside the guarded blocks.
In `@app/src/pages/Home.tsx`:
- Around line 189-190: The AppWalkthrough component should be mounted outside of
the Home component so it does not unmount when navigation controls inside Home
(e.g., the element with target "home-cta" and the tab buttons used by
BottomTabBar) are clicked; move the AppWalkthrough render out of Home and into
the parent layout/component that remains mounted across route changes (so
AppWalkthrough stays mounted alongside Home), ensuring the Joyride instance
inside AppWalkthrough can emit TOUR_END and the existing target selectors
(home-cta and the BottomTabBar tab selectors) continue to be used.
In `@app/src/pages/onboarding/__tests__/OnboardingLayout.test.tsx`:
- Around line 28-31: Tests mock chatSend but never assert it remains unused; add
explicit assertions that chatSend was not called in the tests that exercise
completeAndExit/walkthrough replacement (e.g., in OnboardingLayout.test.tsx
around the tests covering completeAndExit behavior and the block covering lines
~144-175). After invoking the code path that previously might trigger
welcome-agent synthetic sends (call sites in the test that simulate finishing
onboarding), assert vi.mocked(chatSend).not.toHaveBeenCalled() (or equivalent)
to ensure regressions reintroducing chatSend will fail the test. Ensure the
assertion is added to each relevant test case where completeAndExit is executed.
In `@app/src/pages/onboarding/OnboardingLayout.tsx`:
- Around line 132-136: The call to setWalkthroughPending() can throw
synchronously and must not block the subsequent navigate('/home', { replace:
true }) call; make the write best-effort by invoking setWalkthroughPending()
inside a try/catch (or otherwise swallow promise rejections) and log a warning
on error, then always perform navigate('/home', { replace: true }) from the
OnboardingLayout redirect path so navigation occurs even if storage is
unavailable.
In `@app/src/store/threadSlice.ts`:
- Around line 282-287: The reducer setWelcomeThreadId is supposed to be a
compatibility no-op but still assigns state.welcomeThreadId; remove that
assignment so the reducer performs no state changes (preserve the reducer
signature and accepted payload type for type-checking). Locate the
setWelcomeThreadId reducer and delete or disable the line that sets
state.welcomeThreadId = action.payload while keeping the reducer exported and
its payload type intact.
In `@src/openhuman/config/ops.rs`:
- Around line 599-619: The code only flips chat_onboarding_completed on a
false→true transition in set_onboarding_completed, leaving legacy configs with
onboarding_completed=true but chat_onboarding_completed=false unchanged; fix by
normalizing at load or startup: in load_config_with_timeout (or immediately in
set_onboarding_completed after loading config) detect if
config.onboarding_completed == true && config.chat_onboarding_completed ==
false, set config.chat_onboarding_completed = true and persist the config (using
the existing save/write function), so legacy users are migrated and future logic
relying on chat_onboarding_completed is correct.
---
Nitpick comments:
In `@app/src/components/walkthrough/WalkthroughTooltip.tsx`:
- Around line 26-37: The component is currently casting step.title and
step.content to strings which will stringify node-like content; in
WalkthroughTooltip.tsx remove the "as string" casts and render {step.title} and
{step.content} directly so richer React nodes display properly, and replace the
<p> wrapper around step.content with a more generic container (e.g., a <div>
with the same classes) to avoid forcing plain text semantics for non-text
children.
In `@app/src/pages/__tests__/Conversations.welcomeLock.test.tsx`:
- Around line 12-17: Replace the skipped placeholder test with a real behavior
test that mounts the Conversations UI and asserts the normal conversation
affordances are available when chatOnboardingCompleted is false: use existing
test helpers from app/src/test/ (e.g., the app render/store helpers) to render
the Conversations component with initial state setting chatOnboardingCompleted:
false, then assert a user-facing element such as the conversation list or
compose input is present (reference the Conversations component and the
chatOnboardingCompleted flag) so the test covers the unlocked flow instead of
skipping it.
In `@app/src/pages/onboarding/OnboardingLayout.tsx`:
- Line 10: The component currently sets walkthrough flags via the localStorage
helper (see setWalkthroughPending / AppWalkthrough import) which bypasses the
app's persisted Redux state; replace this ad‑hoc localStorage usage with the
app's persisted store by dispatching the appropriate Redux action (or adding
one) to update the onboarding/walkthrough slice so flags are stored via the
configured persistence path, remove the direct localStorage reads/writes in
OnboardingLayout (and any related usage at the other occurrence around lines
noted), and ensure reducers/selectors are updated so rehydration/migration/reset
behavior applies to the walkthrough state.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 4119c6e1-925f-4df0-821f-777eaf7be356
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (18)
app/package.jsonapp/src/App.tsxapp/src/components/BottomTabBar.tsxapp/src/components/walkthrough/AppWalkthrough.tsxapp/src/components/walkthrough/WalkthroughTooltip.tsxapp/src/components/walkthrough/__tests__/AppWalkthrough.test.tsxapp/src/components/walkthrough/walkthroughSteps.tsapp/src/constants/onboardingChat.tsapp/src/lib/coreState/__tests__/store.test.tsapp/src/lib/coreState/store.tsapp/src/pages/Accounts.tsxapp/src/pages/Conversations.tsxapp/src/pages/Home.tsxapp/src/pages/__tests__/Conversations.welcomeLock.test.tsxapp/src/pages/onboarding/OnboardingLayout.tsxapp/src/pages/onboarding/__tests__/OnboardingLayout.test.tsxapp/src/store/threadSlice.tssrc/openhuman/config/ops.rs
- Wrap localStorage helpers in try/catch for SecurityError resilience - Move AppWalkthrough from Home.tsx to AppShell (persists across routes) - Remove `as string` casts in WalkthroughTooltip, use <div> for content - Make setWelcomeThreadId a true no-op reducer - Add legacy config normalization for users with stale onboarding state - try/catch setWalkthroughPending so navigation always proceeds - Add 12 new tests: tour completion/skip, WalkthroughTooltip rendering, chatSend-not-called assertion, unlocked Conversations flow
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/src/App.tsx`:
- Around line 178-181: The AppWalkthrough is mounted unconditionally on every
non-onboarding route and reads global localStorage flags; change the guard so
the component only mounts for an authenticated session and when onboarding is
complete, and move the pending/completed flags into user-scoped persisted Redux
state. Concretely: update the mount condition at the App.tsx site that uses
onOnboardingRoute to also check isAuthenticated and onboardingComplete (from
your auth/selectors) and a Redux selector like
walkthroughCompletedForUser(userId) instead of any global localStorage flag;
then refactor AppWalkthrough to read and write its pending/completed state via
Redux actions/selectors (store flags keyed by userId) rather than ad-hoc
localStorage.
In `@app/src/pages/__tests__/Conversations.welcomeLock.test.tsx`:
- Around line 46-58: The test named "resolves thread display title to thread
title (no \"Onboarding\" override)" is misleading because it duplicates the
composer-gate assertion already covered earlier; update the test to actually
exercise a distinct gate branch or rename it to reflect the current assertion.
Specifically, modify the invocation of isComposerInteractionBlocked in this test
to use a different branch (for example, call isComposerInteractionBlocked({
activeThreadId: null, welcomePending: false, rustChat: false }) to assert the
alternate path) or change the test title to something like "composer gate does
not block when rustChat is true" if you intend to keep the same inputs; ensure
the test content and its description match and reuse existing test helpers from
app/src/test/ where applicable.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 153d0cae-44fe-4170-83c4-75345cd6440e
📒 Files selected for processing (10)
app/src/App.tsxapp/src/components/walkthrough/AppWalkthrough.tsxapp/src/components/walkthrough/WalkthroughTooltip.tsxapp/src/components/walkthrough/__tests__/AppWalkthrough.test.tsxapp/src/pages/Home.tsxapp/src/pages/__tests__/Conversations.welcomeLock.test.tsxapp/src/pages/onboarding/OnboardingLayout.tsxapp/src/pages/onboarding/__tests__/OnboardingLayout.test.tsxapp/src/store/threadSlice.tssrc/openhuman/config/ops.rs
✅ Files skipped from review due to trivial changes (3)
- app/src/pages/Home.tsx
- app/src/components/walkthrough/WalkthroughTooltip.tsx
- app/src/pages/onboarding/tests/OnboardingLayout.test.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
- app/src/components/walkthrough/AppWalkthrough.tsx
- src/openhuman/config/ops.rs
- app/src/components/walkthrough/tests/AppWalkthrough.test.tsx
| {/* Post-onboarding Joyride walkthrough — mounted here (outside routes) so | ||
| it persists across tab navigations. Joyride targets span Home + BottomTabBar | ||
| tabs so it must stay mounted while the user moves between routes. */} | ||
| {!onOnboardingRoute && <AppWalkthrough />} |
There was a problem hiding this comment.
Gate the walkthrough on authenticated, user-scoped state.
AppWalkthrough currently mounts on every non-onboarding route, while its run state comes from global localStorage flags. If a user logs out or switches accounts before finishing the tour, that stale flag can replay the overlay in the next session or on routes where the Joyride targets are missing. Please only mount/run it once the session is authenticated and onboarding is complete, and move or scope the pending/completed flags to user-scoped persisted state instead of app-global storage.
♻️ Minimal guard for the mount site
- {!onOnboardingRoute && <AppWalkthrough />}
+ {!onOnboardingRoute && snapshot.sessionToken && snapshot.onboardingCompleted && (
+ <AppWalkthrough />
+ )}As per coding guidelines, "Use Redux and persist where configured for app state; prefer Redux over ad hoc localStorage for state management".
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| {/* Post-onboarding Joyride walkthrough — mounted here (outside routes) so | |
| it persists across tab navigations. Joyride targets span Home + BottomTabBar | |
| tabs so it must stay mounted while the user moves between routes. */} | |
| {!onOnboardingRoute && <AppWalkthrough />} | |
| {/* Post-onboarding Joyride walkthrough — mounted here (outside routes) so | |
| it persists across tab navigations. Joyride targets span Home + BottomTabBar | |
| tabs so it must stay mounted while the user moves between routes. */} | |
| {!onOnboardingRoute && snapshot.sessionToken && snapshot.onboardingCompleted && ( | |
| <AppWalkthrough /> | |
| )} |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/src/App.tsx` around lines 178 - 181, The AppWalkthrough is mounted
unconditionally on every non-onboarding route and reads global localStorage
flags; change the guard so the component only mounts for an authenticated
session and when onboarding is complete, and move the pending/completed flags
into user-scoped persisted Redux state. Concretely: update the mount condition
at the App.tsx site that uses onOnboardingRoute to also check isAuthenticated
and onboardingComplete (from your auth/selectors) and a Redux selector like
walkthroughCompletedForUser(userId) instead of any global localStorage flag;
then refactor AppWalkthrough to read and write its pending/completed state via
Redux actions/selectors (store flags keyed by userId) rather than ad-hoc
localStorage.
| it('resolves thread display title to thread title (no "Onboarding" override)', () => { | ||
| // The old welcome-lock overrode the thread display title to "Onboarding" | ||
| // for the welcome thread. After #1123 titles are always the thread's own title. | ||
| // This verifies the resolveThreadDisplayTitle function is not clamping titles. | ||
| // We test the pure logic by importing the helper indirectly through the | ||
| // isComposerInteractionBlocked export to avoid a full component mount. | ||
| // | ||
| // The title override was in the component body (not exported separately) | ||
| // so this test simply confirms the exported composer gate does not | ||
| // special-case any thread as a "welcome thread". | ||
| expect( | ||
| isComposerInteractionBlocked({ activeThreadId: null, welcomePending: false, rustChat: true }) | ||
| ).toBe(false); |
There was a problem hiding this comment.
Misleading duplicate test: it does not validate title resolution.
Line 46 says this checks thread-title behavior, but it repeats the same composer-gate assertion as Line 23, so it can’t catch any title-related regression and adds false confidence. Please either rename it to match what it actually asserts or replace it with a distinct gate branch (e.g., rustChat: false).
Suggested replacement
- it('resolves thread display title to thread title (no "Onboarding" override)', () => {
- // The old welcome-lock overrode the thread display title to "Onboarding"
- // for the welcome thread. After `#1123` titles are always the thread's own title.
- // This verifies the resolveThreadDisplayTitle function is not clamping titles.
- // We test the pure logic by importing the helper indirectly through the
- // isComposerInteractionBlocked export to avoid a full component mount.
- //
- // The title override was in the component body (not exported separately)
- // so this test simply confirms the exported composer gate does not
- // special-case any thread as a "welcome thread".
- expect(
- isComposerInteractionBlocked({ activeThreadId: null, welcomePending: false, rustChat: true })
- ).toBe(false);
- });
+ it('blocks composer interaction when rust chat transport is unavailable', () => {
+ expect(
+ isComposerInteractionBlocked({ activeThreadId: null, welcomePending: false, rustChat: false })
+ ).toBe(true);
+ });As per coding guidelines, “Prefer testing behavior over implementation details; use existing helpers from app/src/test/ before adding new harness code.”
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/src/pages/__tests__/Conversations.welcomeLock.test.tsx` around lines 46 -
58, The test named "resolves thread display title to thread title (no
\"Onboarding\" override)" is misleading because it duplicates the composer-gate
assertion already covered earlier; update the test to actually exercise a
distinct gate branch or rename it to reflect the current assertion.
Specifically, modify the invocation of isComposerInteractionBlocked in this test
to use a different branch (for example, call isComposerInteractionBlocked({
activeThreadId: null, welcomePending: false, rustChat: false }) to assert the
alternate path) or change the test title to something like "composer gate does
not block when rustChat is true" if you intend to keep the same inputs; ensure
the test content and its description match and reuse existing test helpers from
app/src/test/ where applicable.
Existing users with onboarding_completed=true but chat_onboarding_completed=false were still routed to the welcome agent because the normalization only ran inside set_onboarding_completed() (which is never called again for already-onboarded users). Move the normalization into load_config_with_timeout() so it fires on every config read — routing, snapshots, and all RPC handlers see the corrected state immediately.
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/openhuman/config/ops.rs (1)
611-622:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winUpdate the docblock to match the Joyride flow.
This comment still says the renderer fires a hidden
chat_sendwelcome thread, but this PR replaces that path with the frontend walkthrough. Leaving the old behavior documented here will send future readers to a dead flow.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/openhuman/config/ops.rs` around lines 611 - 622, The docblock above the false→true transition (referencing seed_proactive_agents) incorrectly states that the renderer fires a hidden `chat_send` welcome thread via `OnboardingLayout.completeAndExit`; update this comment to describe the current Joyride frontend walkthrough instead, remove the outdated note about a hidden `chat_send` welcome agent trigger, and explicitly state that `chat_onboarding_completed` is flipped here as part of the Joyride flow so no welcome-agent dispatch is required.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/openhuman/config/ops.rs`:
- Around line 47-50: The tracing::warn! macro block formatting in the
config.save().await error handling is not rustfmt-compliant; run cargo fmt (or
format the macro to match rustfmt style) so the tracing::warn! invocation is
properly formatted (e.g., wrap the message across lines or use a single-line
literal per rustfmt) in the error branch that logs the onboarding normalization
failure from config.save().await.
- Around line 34-50: The normalization write (config.save().await) in
load_config_with_timeout() can block indefinitely; wrap that save call in a
timeout (e.g., using tokio::time::timeout with the same or a short duration) and
handle a timeout as a non-fatal warning just like other save errors so the
function never awaits an unbounded disk I/O; update the block around
config.save() to call timeout(..., config.save()).await, log a warning on Err or
timed-out result, and proceed to return the loaded config without failing the
load.
---
Outside diff comments:
In `@src/openhuman/config/ops.rs`:
- Around line 611-622: The docblock above the false→true transition (referencing
seed_proactive_agents) incorrectly states that the renderer fires a hidden
`chat_send` welcome thread via `OnboardingLayout.completeAndExit`; update this
comment to describe the current Joyride frontend walkthrough instead, remove the
outdated note about a hidden `chat_send` welcome agent trigger, and explicitly
state that `chat_onboarding_completed` is flipped here as part of the Joyride
flow so no welcome-agent dispatch is required.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 5a20f24c-b1bc-4764-9f37-055310301d52
📒 Files selected for processing (1)
src/openhuman/config/ops.rs
| Ok(Ok(mut config)) => { | ||
| // [#1123] Normalize legacy configs at load time: existing users who | ||
| // completed onboarding before the Joyride migration may have | ||
| // onboarding_completed=true but chat_onboarding_completed=false. | ||
| // Without this, pick_target_agent_id() still routes them to the | ||
| // welcome agent on every chat message. | ||
| if config.onboarding_completed && !config.chat_onboarding_completed { | ||
| tracing::info!( | ||
| "[config] normalizing legacy onboarding state: setting \ | ||
| chat_onboarding_completed=true (Joyride migration)" | ||
| ); | ||
| config.chat_onboarding_completed = true; | ||
| // Best-effort persist — don't fail the load if save errors. | ||
| if let Err(e) = config.save().await { | ||
| tracing::warn!( | ||
| "[config] failed to persist onboarding normalization: {e}" | ||
| ); |
There was a problem hiding this comment.
Wrap the normalization write in a timeout too.
load_config_with_timeout() now does a second disk-I/O await after the timed load completes. On legacy configs, a stuck config.save() can still hang JSON-RPC/CLI indefinitely, which breaks the contract documented above this function.
Suggested fix
- if let Err(e) = config.save().await {
- tracing::warn!(
- "[config] failed to persist onboarding normalization: {e}"
- );
- }
+ match tokio::time::timeout(CONFIG_LOAD_TIMEOUT, config.save()).await {
+ Ok(Ok(())) => {}
+ Ok(Err(e)) => {
+ tracing::warn!(
+ "[config] failed to persist onboarding normalization: {e}"
+ );
+ }
+ Err(_) => {
+ tracing::warn!(
+ "[config] timed out persisting onboarding normalization"
+ );
+ }
+ }🧰 Tools
🪛 GitHub Actions: Type Check
[error] 45-45: cargo fmt --all -- --check failed due to formatting difference in tracing::warn! macro (newline formatting mismatch).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/openhuman/config/ops.rs` around lines 34 - 50, The normalization write
(config.save().await) in load_config_with_timeout() can block indefinitely; wrap
that save call in a timeout (e.g., using tokio::time::timeout with the same or a
short duration) and handle a timeout as a non-fatal warning just like other save
errors so the function never awaits an unbounded disk I/O; update the block
around config.save() to call timeout(..., config.save()).await, log a warning on
Err or timed-out result, and proceed to return the loaded config without failing
the load.
The walkthrough only triggered for users going through the wizard (via setWalkthroughPending). Existing users who were already onboarded before the Joyride migration never saw the tour because the pending flag was never set. Pass onboarded state from the CoreState snapshot into AppWalkthrough so it also shows for existing users who haven't completed or dismissed the walkthrough yet.
- Conversational, witty step copy ("Supercharge your assistant",
"Set it and forget it", "Conversations that remember")
- Frosted-glass tooltip with gradient progress bar and entrance animation
- Step-specific emoji accents for visual personality
- Rounded spotlight with padding for softer focus
- Polished button styling (scale-on-press, shadow transitions)
- "Let's go!" finish CTA instead of generic "Finish"
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/src/App.tsx`:
- Line 181: AppWalkthrough is mounting too early and captures a false onboarding
state during bootstrap; only render it once the core snapshot's onboarding value
is stable by gating its mount on snapshot.onboardingCompleted being defined.
Update the conditional that renders AppWalkthrough (the line using
onOnboardingRoute and snapshot.onboardingCompleted) to require
snapshot.onboardingCompleted !== undefined (or the snapshot "ready"/"hydrated"
flag if available) before mounting so onboarded={!!snapshot.onboardingCompleted}
is derived from a stable snapshot value.
In `@app/src/components/walkthrough/AppWalkthrough.tsx`:
- Around line 22-27: The function isWalkthroughPending incorrectly treats
userIsOnboarded as a second trigger (causing the tour to run for any onboarded
user); update isWalkthroughPending (and its return expression) so it only checks
the WALKTHROUGH_KEY and WALKTHROUGH_PENDING_KEY flags from localStorage (remove
the "|| userIsOnboarded" part and stop treating userIsOnboarded as a start
condition). Keep the function signature if needed for callers but do not use
userIsOnboarded to compute pending; rely solely on WALKTHROUGH_PENDING_KEY being
set by OnboardingLayout to start the tour.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 1ba1070d-984e-408a-8985-20293bf1513a
📒 Files selected for processing (2)
app/src/App.tsxapp/src/components/walkthrough/AppWalkthrough.tsx
| export function isWalkthroughPending(userIsOnboarded = false): boolean { | ||
| try { | ||
| if (localStorage.getItem(WALKTHROUGH_KEY) === 'true') return false; | ||
| return ( | ||
| localStorage.getItem(WALKTHROUGH_PENDING_KEY) === 'true' || userIsOnboarded | ||
| ); |
There was a problem hiding this comment.
Don't treat onboarded as a second start trigger.
app/src/pages/onboarding/OnboardingLayout.tsx:132-141 is the only provided place that sets the pending flag. With pending || userIsOnboarded, every already-onboarded session will auto-run the tour even when nothing asked to start it, which is broader than the post-onboarding flow described in this PR.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/src/components/walkthrough/AppWalkthrough.tsx` around lines 22 - 27, The
function isWalkthroughPending incorrectly treats userIsOnboarded as a second
trigger (causing the tour to run for any onboarded user); update
isWalkthroughPending (and its return expression) so it only checks the
WALKTHROUGH_KEY and WALKTHROUGH_PENDING_KEY flags from localStorage (remove the
"|| userIsOnboarded" part and stop treating userIsOnboarded as a start
condition). Keep the function signature if needed for callers but do not use
userIsOnboarded to compute pending; rely solely on WALKTHROUGH_PENDING_KEY being
set by OnboardingLayout to start the tour.
CodeRabbit review responses (round 2)App.tsx:181 — Gate on !isBootstrapping: Fixed in 44a4764. AppWalkthrough now only mounts after the core snapshot is stable. AppWalkthrough.tsx:27 — onboarded as second trigger: Intentional for migration. Existing users who completed onboarding before this PR need to see the walkthrough once. The WALKTHROUGH_KEY localStorage check prevents it from ever showing again after skip/finish — fires once per user, not every session. |
There was a problem hiding this comment.
🧹 Nitpick comments (2)
app/src/components/walkthrough/__tests__/AppWalkthrough.test.tsx (2)
30-33: ⚡ Quick winUse an
interfaceforJoyrideMockPropsto match repo TypeScript conventions.This object-shape type is currently declared with
type; switch tointerfacefor consistency with the project rule.♻️ Suggested change
-type JoyrideMockProps = { +interface JoyrideMockProps { run: boolean; onEvent?: (data: { type: string; status: string; index: number }) => void; -}; +}As per coding guidelines
**/*.{ts,tsx}: "Preferinterfacefor defining object shapes in TypeScript".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/components/walkthrough/__tests__/AppWalkthrough.test.tsx` around lines 30 - 33, Replace the declared object type alias JoyrideMockProps with an equivalent interface named JoyrideMockProps to follow the repo convention; keep the same properties (run: boolean and optional onEvent callback signature) and update any uses/imports in the test file to reference the interface name (JoyrideMockProps) so typings remain identical while conforming to the project rule preferring interface for object shapes.
380-385: ⚡ Quick winAvoid styling-coupled selector for progress dots in this test.
The class-chain selector is brittle and will fail on cosmetic refactors. Prefer an accessibility hook or stable test id so the assertion tracks behavior, not Tailwind class composition.
As per coding guidelines
app/src/**/*.test.{ts,tsx}: "Prefer testing behavior over implementation".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/components/walkthrough/__tests__/AppWalkthrough.test.tsx` around lines 380 - 385, The test uses a brittle Tailwind class chain to query progress dots; change it to assert via a stable accessibility hook or test id: update the WalkthroughTooltip component so each dot element exposes a stable selector (e.g., role="listitem" inside a container with role="list" or a data-testid like "progress-dot"), then modify the test in AppWalkthrough.test.tsx (the spec that calls render(<WalkthroughTooltip {...makeTooltipProps(...)}/>)) to use getAllByRole('listitem') or getAllByTestId('progress-dot') instead of the class-based querySelectorAll, and assert its length equals the expected step count.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@app/src/components/walkthrough/__tests__/AppWalkthrough.test.tsx`:
- Around line 30-33: Replace the declared object type alias JoyrideMockProps
with an equivalent interface named JoyrideMockProps to follow the repo
convention; keep the same properties (run: boolean and optional onEvent callback
signature) and update any uses/imports in the test file to reference the
interface name (JoyrideMockProps) so typings remain identical while conforming
to the project rule preferring interface for object shapes.
- Around line 380-385: The test uses a brittle Tailwind class chain to query
progress dots; change it to assert via a stable accessibility hook or test id:
update the WalkthroughTooltip component so each dot element exposes a stable
selector (e.g., role="listitem" inside a container with role="list" or a
data-testid like "progress-dot"), then modify the test in
AppWalkthrough.test.tsx (the spec that calls render(<WalkthroughTooltip
{...makeTooltipProps(...)}/>)) to use getAllByRole('listitem') or
getAllByTestId('progress-dot') instead of the class-based querySelectorAll, and
assert its length equals the expected step count.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: ef81cdb2-9618-4de9-919d-882491ebd329
📒 Files selected for processing (5)
app/src/App.tsxapp/src/components/walkthrough/AppWalkthrough.tsxapp/src/components/walkthrough/WalkthroughTooltip.tsxapp/src/components/walkthrough/__tests__/AppWalkthrough.test.tsxapp/src/components/walkthrough/walkthroughSteps.ts
✅ Files skipped from review due to trivial changes (2)
- app/src/components/walkthrough/walkthroughSteps.ts
- app/src/components/walkthrough/AppWalkthrough.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- app/src/components/walkthrough/WalkthroughTooltip.tsx
- Collapse tracing::warn! to single line (CI rustfmt nightly) - Update test assertions: "Finish" -> "Let's go!", "Next" -> "Next →" - Replace progress dots test with progress bar test
…gate Closes the coverage gap on the changed lines in tinyhumansai#1123 (welcome-lock removal + Joyride walkthrough integration): - Conversations.render.test.tsx (14 tests): smoke-renders Conversations in page mode with various usage-state configurations to cover the billing / quota pill JSX, thread-list rendering, messagesError branch, and the loadThreads callback (lines 247, 871, 906, 941, 1002-1014, 1083, 1417-1516). - BottomTabBar.test.tsx (4 tests): renders the tab bar with a valid session token to cover the new walkthroughAttr mapping block (line 222+) added for the Joyride step targets. - OnboardingLayout.test.tsx (+1 test): exercises the catch path in completeAndExit when setWalkthroughPending throws, covering line 138. - threadSlice.test.ts (+1 test): asserts setWelcomeThreadId is a true no-op after the welcome-agent flow was removed (line 285). All 154 test files pass; tsc --noEmit clean.
* feat(remotion): Ghosty character library with transparent MOV variants (tinyhumansai#1059) Co-authored-by: WOZCODE <contact@withwoz.com> * feat(composio/gmail): sync into memory tree (Slack-parity) (tinyhumansai#1056) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(scheduler-gate): throttle background AI on battery / busy CPU (tinyhumansai#1062) * fix(core,cef): run core in-process and stop orphaning CEF helpers on Cmd+Q (tinyhumansai#1061) * ci: add dedicated staging release workflow (tinyhumansai#1066) * fix(sentry): Rust source context + per-release deploy marker (tinyhumansai#405) (tinyhumansai#1067) * fix(welcome): re-enable OAuth buttons with focus/timeout recovery (tinyhumansai#1049) (tinyhumansai#1069) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(dependencies): update pnpm-lock.yaml and Cargo.lock for package… (tinyhumansai#1082) * fix(onboarding): personalize welcome agent greeting with user identity (tinyhumansai#1078) * fix(chat): make agent message bubbles fit content width (tinyhumansai#1083) * Feat/dmg checks (tinyhumansai#1084) * fix(linux): Add X11 platform flags to .deb package launcher (tinyhumansai#1087) Co-authored-by: unn-Known1 <unn-known1@users.noreply.github.com> * fix(sentry): auto-send React events; collapse core→tauri for desktop (tinyhumansai#1086) Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai> * fix(cef): run blank reload guard on the CEF UI thread (tinyhumansai#1092) * fix(app): reload webview instead of restart_app in dev mode (tinyhumansai#1068) (tinyhumansai#1071) * fix(linux): deliver X11 ozone flags via custom .desktop template (tinyhumansai#1091) * fix(webview-accounts): retry data-dir purge so CEF handle race doesn't leak cookies (tinyhumansai#1076) (tinyhumansai#1081) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai> * fix(webview/slack): media perms + deep-link isolation (tinyhumansai#1074) (tinyhumansai#1080) Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai> * ci(release): split staging vs production workflows; promote staging tags (tinyhumansai#1094) * Update release-staging.yml (tinyhumansai#1097) * chore(staging): v0.53.5 * chore(staging): v0.53.6 * ci(staging): cut staging from main; add act local-debug helper (tinyhumansai#1099) * chore(staging): v0.53.7 * fix(ci): correct sentry-cli download URL and trap scope (tinyhumansai#1100) * chore(staging): v0.53.8 * feat(chat): forward thread_id to backend for KV cache locality (tinyhumansai#1095) * fix(ci): bump pinned sentry-cli to 3.4.1 (2.34.2 was never published) (tinyhumansai#1102) * chore(staging): v0.53.9 * fix(ci): drop bash trap in upload_sentry_symbols.sh; inline cleanup (tinyhumansai#1103) * chore(staging): v0.53.10 * refactor(session): flatten session_raw/, switch md to YYYY_MM_DD (tinyhumansai#1098) * Add full Composio managed-auth toolkit catalog (tinyhumansai#1093) * ci: add diff-aware 80% coverage gate (Vitest + cargo-llvm-cov) (tinyhumansai#1104) * feat(scripts): pnpm work + pnpm debug for agent-driven workflows (tinyhumansai#1105) * ci: pull pnpm into CI image, drop redundant setup steps (tinyhumansai#1107) * docs: add Cursor Cloud specific instructions to AGENTS.md (tinyhumansai#1106) Co-authored-by: Cursor Agent <cursoragent@cursor.com> * chore(staging): v0.53.11 * docs: surface 80% coverage gate and scripts/debug runners (tinyhumansai#1108) * feat(app): show Composio integrations as sorted icon grid on Skills (tinyhumansai#1109) Co-authored-by: Cursor Agent <cursoragent@cursor.com> * feat(composio): client-side trigger enable/disable toggles (tinyhumansai#1110) * feat(skills): channels grid + integrations card polish; tolerant Composio trigger decode (tinyhumansai#1112) * chore(staging): v0.53.12 * feat(home): early-bird banner + assistant→agent terminology (tinyhumansai#1113) * feat(updater): in-app auto-update with auto-download + restart prompt (tinyhumansai#677) (tinyhumansai#1114) * chore(claude): add ship-and-babysit slash command (tinyhumansai#1115) * feat(home): EarlyBirdyBanner + agent terminology + LinkedIn enrichment model pin (tinyhumansai#1118) * fix(chat): single onboarding thread in sidebar after wizard (tinyhumansai#1116) Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Steven Enamakel <senamakel@users.noreply.github.com> * fix: filter out global namespace from citation chips (tinyhumansai#1124) Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> Co-authored-by: senamakel-droid <281415773+senamakel-droid@users.noreply.github.com> * feat(nav): enable Memory tab in BottomTabBar (tinyhumansai#1125) * feat(memory): singleton ingestion + status RPC + UI pill (tinyhumansai#1126) * feat(human): mascot tab with viseme-driven lipsync (staging only) (tinyhumansai#1127) * Fix CEF zombie processes on full app close and restart (tinyhumansai#1128) Co-authored-by: senamakel-droid <281415773+senamakel-droid@users.noreply.github.com> Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai> * Update issue templates for GitHub issue types (tinyhumansai#1146) * feat(human): expand mascot expressions and tighten reply-speech state machine (tinyhumansai#1147) * feat(memory): ingestion pipeline + tree-architecture docs + ops/schemas split (tinyhumansai#1142) * feat(threads): surface live subagent work in parent thread (tinyhumansai#1122) (tinyhumansai#1159) * fix(human): keep mascot mouth animating when TTS ships no viseme data (tinyhumansai#1160) * feat(composio): consume backend markdownFormatted for LLM output (tinyhumansai#1165) * fix(subagent): lazy-register toolkit actions filtered out of fuzzy top-K (tinyhumansai#1162) * feat(memory): user-facing long-term memory window preset (tinyhumansai#1137) (tinyhumansai#1161) * fix(tauri-shell): proactively kill stale openhuman RPC on startup (tinyhumansai#1166) * chore(staging): v0.53.13 * fix(composio): per-action tool consumes backend markdownFormatted (tinyhumansai#1167) * fix(threads): persist selectedThreadId across reloads (tinyhumansai#1168) * feat(memory_tree): switch embed model to bge-m3 (1024-dim, 8K context) (tinyhumansai#1174) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(agent): drop redundant [Memory context] recall injection (tinyhumansai#1173) * chore(memory_tree): drop body-read timeouts on Ollama HTTP calls (tinyhumansai#1171) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(transcript): emit thread_id + fix orchestrator missing cost (tinyhumansai#1169) * fix(composio/gmail): phase out html2md, prefer text/plain MIME part (tinyhumansai#1170) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(tools): markdown output for internal tool results (tinyhumansai#1172) * feat(security): enforce prompt-injection guard before model and tool execution (tinyhumansai#1175) * fix(cef): popup paint dies after first frame — skip blank-page guard for popups (tinyhumansai#1079) (tinyhumansai#1182) Co-authored-by: Steven Enamakel <31011319+senamakel@users.noreply.github.com> * chore(sentry): rename OPENHUMAN_SENTRY_DSN → OPENHUMAN_CORE_SENTRY_DSN (tinyhumansai#1186) * feat(remotion): add yellow mascot character with all animation variants (tinyhumansai#1193) Co-authored-by: Neel Mistry <neelmistry@Neels-MacBook-Pro.local> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(composio): hide raw connection ID, derive friendly label (tinyhumansai#1153) (tinyhumansai#1185) Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> * fix(windows): align install.ps1 MSI with per-machine scope (tinyhumansai#913) (tinyhumansai#1187) Co-authored-by: Cursor <cursoragent@cursor.com> * fix(tauri): deterministic CEF teardown on full app close (tinyhumansai#1120) (tinyhumansai#1189) Co-authored-by: Cursor <cursoragent@cursor.com> * fix(composio): cap Gmail HTML body before strip (crash mitigation) (tinyhumansai#1191) Co-authored-by: Cursor <cursoragent@cursor.com> * fix(auth): stop stale chat threads after signup (tinyhumansai#1192) Co-authored-by: Cursor <cursoragent@cursor.com> * feat(sentry): staging-only "Trigger Sentry Test" button (tinyhumansai#1072) (tinyhumansai#1183) * chore(staging): v0.53.14 * chore(staging): v0.53.15 * feat(composio): format trigger slugs into human-readable labels (tinyhumansai#1129) (tinyhumansai#1179) Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> * fix(ui): hide unsupported permission UI on non-macOS for Screen Intelligence (tinyhumansai#1194) Co-authored-by: Cursor <cursoragent@cursor.com> * chore(tauri-shell): retire embedded Gmail webview-account flow (tinyhumansai#1181) * feat(onboarding): replace welcome-agent bot with react-joyride walkthrough (tinyhumansai#1180) * chore(release): v0.53.16 * fix(threads): preserve selectedThreadId on cold-boot identity hydration (tinyhumansai#1196) * feat(core): version/shutdown/update RPCs + mid-thread integration refresh (tinyhumansai#1195) * fix(mascot): swap to yellow mascot via @remotion/player (tinyhumansai#1200) * feat(memory_tree): cloud-default LLM, queue priority, entity filter, Memory tab UI (tinyhumansai#1198) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Persist turn state + restore conversation history on cold-boot (tinyhumansai#1202) * feat(mascot): floating desktop mascot via native NSPanel + WKWebView (macOS) (tinyhumansai#1203) * fix(memory/tree): emit summary children as Obsidian wikilinks (tinyhumansai#1210) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(tools): coding-harness baseline primitives (tinyhumansai#1205) (tinyhumansai#1208) * docs: add Codex PR checklist for remote agents --------- Co-authored-by: Steven Enamakel <31011319+senamakel@users.noreply.github.com> Co-authored-by: WOZCODE <contact@withwoz.com> Co-authored-by: sanil-23 <sanil@vezures.xyz> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Cyrus Gray <144336577+graycyrus@users.noreply.github.com> Co-authored-by: CodeGhost21 <164498022+CodeGhost21@users.noreply.github.com> Co-authored-by: oxoxDev <164490987+oxoxDev@users.noreply.github.com> Co-authored-by: Mega Mind <146339422+M3gA-Mind@users.noreply.github.com> Co-authored-by: Gaurang Patel <ptelgm.yt@gmail.com> Co-authored-by: unn-Known1 <unn-known1@users.noreply.github.com> Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Steven Enamakel <senamakel@users.noreply.github.com> Co-authored-by: Steven Enamakel's Droid <enamakel.agent@tinyhumans.ai> Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> Co-authored-by: senamakel-droid <281415773+senamakel-droid@users.noreply.github.com> Co-authored-by: YellowSnnowmann <167776381+YellowSnnowmann@users.noreply.github.com> Co-authored-by: Neil <neil@maha.xyz> Co-authored-by: Neel Mistry <neelmistry@Neels-MacBook-Pro.local> Co-authored-by: obchain <167975049+obchain@users.noreply.github.com> Co-authored-by: Jwalin Shah <jshah1331@gmail.com>
Summary
Replace the post-wizard welcome-agent chat lockdown with a deterministic React Joyride product tour that highlights real app surfaces.
react-joyridev3 with custom tooltip matching design system (ocean primary#2F6EF4)/home→ Joyride tour starts[#1123]markers for easy rollbackchat_onboarding_completed=trueon theonboarding_completedfalse→true transition inconfig/ops.rs, so agent routing immediately uses orchestratorlocalStoragekeys (openhuman:walkthrough_pending,openhuman:walkthrough_completed)What changed
app/src/components/walkthrough/— AppWalkthrough, WalkthroughTooltip, walkthroughSteps, testsconfig/ops.rs—set_onboarding_completedalso flipschat_onboarding_completedMigration notes
[#1123]commentscomplete_onboarding,check_onboarding_status) are untouchedset_onboarding_completedchange on next app startTest plan
pnpm typecheck— 0 errorspnpm lint— 0 errors (32 pre-existing warnings)pnpm format:check— cleanpnpm build— succeedscargo check— cleanCloses #1123
Summary by CodeRabbit
New Features
Refactor
Tests