From 05a2848b028d2d62fb5f587fe67643495fb85bbf Mon Sep 17 00:00:00 2001 From: scotttong Date: Wed, 18 Mar 2026 03:18:14 -0700 Subject: [PATCH] experiment: CEO welcome flow, merged steps, orientation screen, UX polish MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CEO welcome & user agency: - Removed auto-posted user message — CEO greets the board first - "CEO is waking up..." → "CEO is composing..." → welcome message fades in - Response chips ("Yes, get started!" / "Let's discuss first") fade in after - Planning task created unassigned — CEO only wakes when user initiates - "Yes" chip sends directly; "Discuss" pre-fills input for editing Merged steps 5+6: - "Approve & hire" on the Plan step creates hire tasks directly - No redundant confirmation step - Step 6 is now a welcome/orientation screen (hidden from nav tabs) Orientation screen: - Shows what to expect: Tasks, Agents, Approvals, Dashboard - "Go to dashboard" button closes wizard and navigates Nav tabs cleaned up: - Mission → Launch → CEO → Plan → Review (5 visible tabs) - Orientation step exists but not in nav (no redundant rockets) Chat UX polish: - Single-line input (like a URL bar) instead of multi-line textarea - CEO welcome message always visible in conversation history - Structured task description tells CEO exact format for role specs - "Confirm mission" button hidden until user has chosen a path - Switching paths preserves previously entered text Parser improvements: - Handles N. **Role Name** with indented bullets (fallback format) - Summary field populated from first expertise line when no explicit summary - Better skip patterns for non-role sections Co-Authored-By: Claude Opus 4.6 (1M context) --- ui/src/components/OnboardingChat.tsx | 139 ++++++++++-- ui/src/components/OnboardingWizard.tsx | 301 +++++++++++++++++-------- 2 files changed, 321 insertions(+), 119 deletions(-) diff --git a/ui/src/components/OnboardingChat.tsx b/ui/src/components/OnboardingChat.tsx index 3d9e67e8..73c76b42 100644 --- a/ui/src/components/OnboardingChat.tsx +++ b/ui/src/components/OnboardingChat.tsx @@ -13,6 +13,8 @@ interface OnboardingChatProps { taskId: string; agentId: string; agentName: string; + companyName: string; + companyGoal: string; onPlanDetected?: (planMarkdown: string) => void; onReviewPlan?: () => void; } @@ -81,6 +83,8 @@ export function OnboardingChat({ taskId, agentId, agentName, + companyName, + companyGoal, onPlanDetected, onReviewPlan, }: OnboardingChatProps) { @@ -96,7 +100,7 @@ export function OnboardingChat({ string | null >(null); const scrollRef = useRef(null); - const inputRef = useRef(null); + const inputRef = useRef(null); const { data: rawComments, @@ -165,14 +169,12 @@ export function OnboardingChat({ } }, [comments, onPlanDetected, detectedPlanCommentId, ignoreBeforeCommentId, taskId]); - const handleSend = useCallback(async () => { - const body = input.trim(); - if (!body || sending) return; + const sendMessage = useCallback(async (body: string) => { + const trimmed = body.trim(); + if (!trimmed || sending) return; setSending(true); try { // Ensure the task is assigned to the CEO and in_progress before commenting. - // The CEO tends to unassign itself and set status to in_review after responding, - // which prevents the comment wakeup from working. try { await issuesApi.update(taskId, { assigneeUserId: null }); } catch { /* may already be null */ } @@ -183,11 +185,9 @@ export function OnboardingChat({ }); } catch { /* may already be assigned */ } - await issuesApi.addComment(taskId, body, true, true); + await issuesApi.addComment(taskId, trimmed, true, true); setInput(""); - // Clear detected plan — user is asking for revisions, so the old plan - // is stale. A new plan will be detected when the CEO responds again. - // Mark the last known comment so the detector ignores older plans. + // Clear detected plan — user is asking for revisions const latestId = comments?.[comments.length - 1]?.id ?? null; setIgnoreBeforeCommentId(latestId); setDetectedPlanCommentId(null); @@ -198,7 +198,11 @@ export function OnboardingChat({ setSending(false); inputRef.current?.focus(); } - }, [input, sending, taskId, queryClient]); + }, [sending, taskId, agentId, queryClient, comments]); + + const handleSend = useCallback(() => { + sendMessage(input); + }, [input, sendMessage]); const handleKeyDown = useCallback( (e: React.KeyboardEvent) => { @@ -257,11 +261,18 @@ export function OnboardingChat({ ref={scrollRef} className="flex-1 overflow-y-auto space-y-3 mb-3 min-h-[180px] max-h-[320px] pr-1" > - {(!comments || comments.length === 0) && ( -

- Starting conversation with {agentName}... -

- )} + {/* CEO welcome message + chips — delayed reveal */} + { + setInput("I want to discuss the plan before you get started."); + inputRef.current?.focus(); + }} + onStart={() => sendMessage("Yes, get started on the hiring plan!")} + /> {comments?.map((comment) => { const isAgent = Boolean(comment.authorAgentId); const isPlan = @@ -355,15 +366,15 @@ export function OnboardingChat({ )} {/* Input area */} -
-