fix(onboarding): restore post-wizard handoff and chat connection; properly bootstrap hatching prompt - #312
Conversation
…perly bootstrap hatching prompt Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…eService owns identity Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…bootstrap injector Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ector Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Thanks for this — #312 is very close and CI is green. The core direction looks right: restoring the post-wizard chat handoff, preventing duplicate bootstrap sends, using the node-role token check, and making the connection-manager node-start predicate gateway-aware all look like useful fixes. I did a deep pass against the current branch and I would like a small follow-up before merge, mostly around making the bootstrap one-shot gate harder to consume accidentally. Main thing to tighten:
|
…rving Wait for the operator hello-ok boundary before allowing the ChatPage WebView to navigate, then probe the tokenized chat URL until the HTTP surface returns success. Keep the Chat tab open in a bounded waiting/retry state so post-wizard auto-launch still opens Chat without exposing a transient 404. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Strengthen bootstrap injection so a submit/click attempt returns sent only after the composer clears or the bootstrap text appears in a user-message-like transcript element. If acceptance is not proven within the bounded poll window, return unconfirmed and leave HasInjectedFirstRunBootstrap open for retry; transcript selectors are intentionally broad because no single stable live-DOM selector is guaranteed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…rective Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Keep .squad state local so repo-hygiene can pass.
|
I pushed a tiny maintainer follow-up commit to this branch: No product code was changed in that commit. It only removes the tracked Thanks for the substantive onboarding/chat rework here — after CI reruns, I’ll re-check the native-chat/bootstrap changes separately from the hygiene cleanup. |
Bostick PR #312 Hanselman Dual-Model Review
Scope reviewedReviewed PR #312 diff vs Both Models Agree -> HIGH consensus
Only One Model Flagged -> LOW consensus
Consensus synthesisBoth reviewers independently found the same completion-event race in the new gateway-side bootstrap path. I verified the code shape: Cleanup checklist before merge
Bostick review noteThis is not a broad rejection of Option Z. The architecture of moving hatching bootstrap out of WebView DOM injection and into authenticated |
….send RPC Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
H1 is fixed in the new commits I used the subscribe-first + buffer-replay shape: Final validation: Ref: #312 (comment) |
|
@steipete — PR ready for your review when you have a chance. Summary of the journey: This PR went through 5 rounds of fixes for the post-wizard hatching prompt. The final state delivers all 6 verifications I tested manually against a clean WSL gateway install:
Architectural shift in the final round (Option Z): Bootstrap hatching message now goes through the authenticated gateway Validation: Other notes:
Happy to walk through any part of it. — Mike |
Master added AddCronJobAsync/UpdateCronJobAsync/RequestCronJobsAsync/ CronRunsUpdated to IOperatorGatewayClient. The bootstrap test fake needs stub implementations to compile after merging master. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fixes regressions introduced by #307 and properly addresses the hatching-prompt bug it tried to fix.
Hand-off chain
After onboarding finishes on the Ready page,
OnboardingWindow.OnWizardComplete()checks that setup is no longer required, then defersShowHubChatAfterWizardClose()onto the dispatcher. That opensApp.ShowHub("chat"), routes theHubWindowto the Chat tab, initializesChatPage, connects the WebView to the gateway with resolved operator credentials, and runsBootstrapMessageInjectorafter successful navigation.What changed
ChatPageso the Chat tab connects with the same operator credentials as the rest of the tray.BootstrapMessageInjectorinto a defensive, testable one-shot service that broad-matches composer primitives across document/open shadow roots, verifies the prompt rendered, and never lets injector failures bubble into the wizard/chat handoff.Follow-up commits address double-init race
These follow-up commits implement Aaron's 2026-05-11 RCA fixes for bugs A/B/C:
NodeConnectorstartup path after operator handshake. That path is still preserved for remote/no-local-setup gateways, but it is now suppressed for an active local gateway when the canonical localNodeServicealready owns a stored Windows-node identity underIdentityDataPath.BootstrapMessageInjector.InjectAsync(...)now has an in-process in-flight guard and re-checksHasInjectedFirstRunBootstrapafter the initial delay, immediately before JavaScript execution, so two WebView navigations cannot both pass the pre-delay gate and execute.sentfromrendered; onlysentconsumesHasInjectedFirstRunBootstrap, whilerenderedlogs that the prompt reached the composer without confirmed submit and leaves the gate open for retry.Round 3 ΓÇö chat readiness gate + accepted-send proof
ChatPagenow opens the Chat tab immediately but defersWebView.CoreWebView2.Navigate(...)until the activeGatewayConnectionManageroperator state reaches the handshake-succeeded/hello-ok boundary and the tokenized chat HTTP URL returns a successful response. While waiting, it shows a lightweight “Waiting for chat to start…” state; if readiness times out, it logs the failure and shows an inline Retry button instead of exposing a WebView 404.BootstrapMessageInjectornow treats submit/click as only an attempt. It returns provensentonly after bounded polling observes accepted-send proof: the composer cleared or the hatching text appears in a user-message-like transcript element. If proof is not observed, it returnsunconfirmedand leavesHasInjectedFirstRunBootstrapopen for retry.What we deliberately did NOT change
NodeConnector; remote/no-local-setup gateways still use that path.HasInjectedFirstRunBootstrapunless the injector receives provensentstatus.⚠️ REQUIRED MANUAL VERIFICATION BEFORE MERGE
Mike must walk the real WSL wizard end-to-end before merge and verify all six checks:
BOOTSTRAP.mdsubmits, not just types; an assistant reply is visible in the chat transcript.wsl -d OpenClawGateway -- cat ~/.openclaw/devices/paired.jsonshows exactly one Windows-node entry, not two.I reset WSL validation state, launched with
OPENCLAW_VISUAL_TEST=1, confirmed the app starts without startup exceptions, capturedvisual-test-output\verify\openclaw-setup-smoke.png, and also captured an isolated Chat smoke screenshot atvisual-test-output\verify-chat\chat-waiting-desktop.png. I could not fully drive the wizard end-to-end without Mike completing the interactive WSL flow, so A/B/C/D/E/F remain required.Validation
./build.ps1retry was briefly blocked by a transient file lock onOpenClaw.Shared.deps.json; rerunning after the lock cleared passed../build.ps1passed: Shared, Cli, WinNodeCli, and WinUI all built.dotnet test ./tests/OpenClaw.Shared.Tests/OpenClaw.Shared.Tests.csproj --no-restorepassed withOPENCLAW_REPO_ROOTset: 1465 total, 1443 passed, 22 skipped.dotnet test ./tests/OpenClaw.Tray.Tests/OpenClaw.Tray.Tests.csproj --no-restorepassed withOPENCLAW_REPO_ROOTset: 924 total, 924 passed.Expertise
See
.squad/agents/aaron/expertise/onboarding-chat-bootstrap.mdfor PR archeology, the hidden defensive-injector contract, selector/timing rationale, the dual-startup-path hazard, the injector race-window pattern, the chat-readiness gate, and the accepted-send-proof pattern.Round 4 ΓÇö Option Z: gateway-side bootstrap (no DOM injection)
chat.sendWebSocket API instead of clicking/filling WebView DOM.ChatPagekeeps the readiness gate: operator handshake → tokenized chat HTTP probe → gatewaychat.send→ wait for the matching assistant final when a run ID is returned → navigate WebView.BootstrapMessageInjectorand its WebView composer injection scaffolding are gone;ChatWindowand the legacy onboarding chat overlay no longer dispatch post-navigation injection.OnboardingChatBootstrappertests for success, send failure, and completion timeout. Updated test counts: Shared 1465 total / 1443 passed / 22 skipped; Tray 909 total / 909 passed../build.ps1;dotnet test ./tests/OpenClaw.Shared.Tests/OpenClaw.Shared.Tests.csproj --no-restore;dotnet test ./tests/OpenClaw.Tray.Tests/OpenClaw.Tray.Tests.csproj --no-restore -m:1.OPENCLAW_VISUAL_TEST=1, confirmed startup stayed alive and log showed no new exceptions in the Option Z paths. Startup visual output dir:visual-test-output\option-z-startup.Mike manual verification checklist
A. Tray auto-opens to Chat tab when wizard finishes.
B. Chat connects to gateway.
C. Hatching conversation appears as a real two-way thread: one user message and one claw response; composer is empty and send button is ready.
D. No second pairing notification.
E.
wsl -d OpenClawGateway -- cat ~/.openclaw/devices/paired.jsonshows exactly one Windows-node entry.F. WebView never shows a 404; it shows the waiting state until gateway/chat/bootstrap readiness completes, then loads chat.
Round 5 — H1 race fix per Hanselman review
OnboardingChatBootstrapper; completion events are observed beforechat.send, buffered byrunIduntil the send response identifies the run, and the timeout is anchored at subscription time.BootstrapAsync_ConsumesGate_WhenCompletionArrivesSynchronouslyDuringSend, where the fake gateway raises the final assistant event synchronously insideSendChatMessageForRunAsync; it fails against the old send-first path and passes with the new observer../build.ps1passed;OpenClaw.Shared.Testspassed 1465 total (1443 passed, 0 failed, 22 skipped);OpenClaw.Tray.Testspassed 910 total (910 passed, 0 failed, 0 skipped).OPENCLAW_RUN_INTEGRATIONwas unset;OPENCLAW_REPO_ROOTwas set to the worktree for final validation..squad/decisions/inbox/bostick-pr312-hanselman-review.mdH1.