---
phase: 32-multi-step-onboarding-wizard
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
  - ui/src/components/onboarding/OnboardingSummaryStep.tsx
  - ui/src/components/NexusOnboardingWizard.tsx
  - ui/src/components/onboarding/OnboardingSummaryStep.test.tsx
autonomous: true
requirements: [ONBD-04, ONBD-05, ONBD-06]

must_haves:
  truths:
    - "User can click Skip on step 1 (hardware) and advance to step 2"
    - "User can click Skip on step 2 (mode) and advance to step 3"
    - "User can click Skip to summary on step 4 (root dir) and advance to step 5"
    - "Step 3 (provider) already has skip — no change needed"
    - "User sees summary screen showing hardware tier, mode, provider, and root directory"
    - "Summary screen shows 'None selected' when provider was skipped"
    - "User clicks 'Start chatting' on summary and lands on dashboard with chat panel open"
    - "Step indicator shows 'Summary' on step 5 instead of 'Step 5 of 5'"
  artifacts:
    - path: "ui/src/components/onboarding/OnboardingSummaryStep.tsx"
      provides: "Read-only summary display with Start chatting CTA"
      exports: ["OnboardingSummaryStep"]
    - path: "ui/src/components/NexusOnboardingWizard.tsx"
      provides: "5-step wizard with skip buttons on steps 1, 2, 4 and summary as step 5"
      contains: "step === 5"
    - path: "ui/src/components/onboarding/OnboardingSummaryStep.test.tsx"
      provides: "Unit tests for summary step rendering and CTA behavior"
  key_links:
    - from: "ui/src/components/NexusOnboardingWizard.tsx"
      to: "ui/src/components/onboarding/OnboardingSummaryStep.tsx"
      via: "import and render at step === 5"
      pattern: "OnboardingSummaryStep"
    - from: "ui/src/components/NexusOnboardingWizard.tsx"
      to: "useChatPanel"
      via: "setChatOpen(true) after navigate in handleStartChat"
      pattern: "setChatOpen\\(true\\)"
---

<objective>
Complete the onboarding wizard by adding skip buttons to steps 1, 2, and 4, creating a summary screen (step 5) that displays all configured settings, and wiring a "Start chatting" CTA that creates the workspace and opens the chat panel.

Purpose: Users can skip through onboarding quickly or review their choices before starting — fulfilling ONBD-04 (skip any step), ONBD-05 (summary screen), and ONBD-06 (one-click to chat).

Output: OnboardingSummaryStep component, updated NexusOnboardingWizard with 5 steps and skip buttons, unit tests.
</objective>

<execution_context>
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
@$HOME/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/32-multi-step-onboarding-wizard/32-RESEARCH.md

<interfaces>
<!-- Key types and contracts the executor needs -->

From ui/src/api/hardware.ts:
```typescript
export type HardwareTier = "gpu" | "apple_silicon" | "cpu_only";

export interface HardwareInfo {
  totalGb: number;
  freeGb: number;
  usableGb: number;
  platform: string;
  gpuName: string | null;
  gpuVramGb: number | null;
  unifiedMemory: boolean;
  hardwareTier: HardwareTier;
  cpuModel: string | null;
}

export type NexusMode = "personal_ai" | "project_builder" | "both";
```

From ui/src/context/ChatPanelContext.tsx:
```typescript
export function useChatPanel(): {
  chatOpen: boolean;
  setChatOpen: (open: boolean) => void;
};
```

From ui/src/components/NexusOnboardingWizard.tsx (existing state available to summary):
```typescript
// These are the wizard state variables available to pass as props:
const [selectedMode, setSelectedMode] = useState<NexusMode>("both");
const [puterToken, setPuterToken] = useState<string | null>(null);
const [googleOAuthStateId, setGoogleOAuthStateId] = useState<string | null>(null);
const [apiKeyData, setApiKeyData] = useState<{ provider: string; apiKey: string } | null>(null);
const [rootDir, setRootDir] = useState("");
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const { data: hardwareInfo } = useHardwareInfo(effectiveOnboardingOpen);
```
</interfaces>
</context>

<tasks>

<task type="auto">
  <name>Task 1: Create OnboardingSummaryStep component and tests</name>
  <files>ui/src/components/onboarding/OnboardingSummaryStep.tsx, ui/src/components/onboarding/OnboardingSummaryStep.test.tsx</files>
  <read_first>
    - ui/src/components/onboarding/HardwareSummaryStep.tsx (existing step pattern, styling conventions)
    - ui/src/components/onboarding/ModeSelector.tsx (NexusMode usage, label patterns)
    - ui/src/api/hardware.ts (HardwareInfo and NexusMode types)
  </read_first>
  <action>
Create `ui/src/components/onboarding/OnboardingSummaryStep.tsx`:

1. Define props interface:
```typescript
interface OnboardingSummaryStepProps {
  hardwareInfo: HardwareInfo | undefined;
  selectedMode: NexusMode;
  providerLabel: string;
  rootDir: string;
  loading: boolean;
  error: string | null;
  onStartChat: () => void;
  onBack: () => void;
}
```

2. Implement a read-only summary with 4 rows in a bordered card (`rounded-lg border border-border p-4`):
   - **Hardware**: Show `hardwareInfo.hardwareTier` mapped to display labels ("GPU", "Apple Silicon", "CPU Only"), or "Unknown" if undefined
   - **Mode**: Map `NexusMode` to labels: `personal_ai` -> "Personal AI Assistant", `project_builder` -> "Project Builder", `both` -> "Both (recommended)"
   - **Provider**: Render the `providerLabel` string directly (caller computes it)
   - **Root directory**: Only show row if `rootDir` is non-empty, render value in `font-mono text-sm`

3. Create a helper `SummaryRow` component (internal, not exported) that renders a label-value pair:
   - Label in `text-muted-foreground` on the left
   - Value on the right, optionally mono (`font-mono text-sm` when `mono` prop is true)
   - Use flexbox `justify-between`

4. Show error message if `error` is non-null (`text-sm text-destructive bg-destructive/10 rounded-md px-3 py-2`)

5. "Start chatting" primary button — `onClick={onStartChat}`, `disabled={loading}`, shows "Setting up..." when loading (with the same spinner SVG used in the root dir step)

6. "Back" ghost button — `onClick={onBack}`, `disabled={loading}`

7. Do NOT include any corporate language ("company", "CEO", "mission") per ROADMAP success criteria.

Create `ui/src/components/onboarding/OnboardingSummaryStep.test.tsx`:

Using Vitest + React Testing Library (project standard):

- Test 1: Renders all summary rows with provided data (hardware tier, mode label, provider label, root dir)
- Test 2: Hides root directory row when rootDir is empty string
- Test 3: Shows error message when error prop is non-null
- Test 4: Calls onStartChat when "Start chatting" button clicked
- Test 5: Disables "Start chatting" button when loading is true
- Test 6: Shows "Setting up..." text when loading is true

Mock data: `hardwareInfo` with `hardwareTier: "apple_silicon"`, `selectedMode: "both"`, `providerLabel: "Puter (free, zero-config)"`, `rootDir: "~/projects/test"`.
  </action>
  <verify>
    <automated>cd /opt/nexus && pnpm vitest run ui/src/components/onboarding/OnboardingSummaryStep.test.tsx</automated>
  </verify>
  <acceptance_criteria>
    - grep -q "export function OnboardingSummaryStep" ui/src/components/onboarding/OnboardingSummaryStep.tsx
    - grep -q "Start chatting" ui/src/components/onboarding/OnboardingSummaryStep.tsx
    - grep -q "onStartChat" ui/src/components/onboarding/OnboardingSummaryStep.tsx
    - grep -q "SummaryRow" ui/src/components/onboarding/OnboardingSummaryStep.tsx
    - grep -q "describe" ui/src/components/onboarding/OnboardingSummaryStep.test.tsx
  </acceptance_criteria>
  <done>OnboardingSummaryStep renders hardware, mode, provider, root dir in a read-only card with Start chatting CTA. All 6 tests pass.</done>
</task>

<task type="auto">
  <name>Task 2: Wire summary step into wizard, add skip buttons, connect chat handoff</name>
  <files>ui/src/components/NexusOnboardingWizard.tsx</files>
  <read_first>
    - ui/src/components/NexusOnboardingWizard.tsx (full file — the primary modification target)
    - ui/src/context/ChatPanelContext.tsx (useChatPanel hook export and setChatOpen signature)
    - ui/src/components/onboarding/OnboardingSummaryStep.tsx (the component just created in Task 1)
  </read_first>
  <action>
Modify `ui/src/components/NexusOnboardingWizard.tsx` with these changes:

**1. Import additions (top of file):**
- `import { OnboardingSummaryStep } from "./onboarding/OnboardingSummaryStep";`
- `import { useChatPanel } from "../context/ChatPanelContext";`

**2. Add useChatPanel hook** inside the `OnboardingWizard` component body, near the other hooks:
```typescript
const { setChatOpen } = useChatPanel();
```

**3. Add `deriveProviderLabel` helper** inside or before the component:
```typescript
function deriveProviderLabel(
  puterToken: string | null,
  googleOAuthStateId: string | null,
  apiKeyData: { provider: string; apiKey: string } | null,
): string {
  if (puterToken) return "Puter (free, zero-config)";
  if (googleOAuthStateId) return "Google Gemini (free tier)";
  if (apiKeyData) return `API key — ${apiKeyData.provider}`;
  return "None selected";
}
```

**4. Update step indicator** — change `<p className="text-xs text-muted-foreground text-center">Step {step} of 4</p>` to:
```tsx
<p className="text-xs text-muted-foreground text-center">
  {step === 5 ? "Summary" : `Step ${step} of 4`}
</p>
```
Note: Keep "of 4" — the summary is not a form step, so steps 1-4 are the data entry steps. Step 5 shows "Summary" label instead.

**5. Add skip button to Step 1** (hardware detection) — after the existing Continue button, add:
```tsx
<Button type="button" variant="ghost" onClick={() => setStep(2)} className="w-full">
  Skip
</Button>
```

**6. Add skip button to Step 2** (mode selection) — after the Back button in the button group, add:
```tsx
<Button type="button" variant="ghost" onClick={() => setStep(3)} className="w-full">
  Skip
</Button>
```

**7. Modify Step 4 "Get Started" button** — change from `type="submit"` to `type="button"` and change its onClick to advance to step 5 instead of submitting:
```tsx
<Button
  type="button"
  onClick={() => setStep(5)}
  disabled={loading || probing}
  className="w-full"
>
  Review &amp; finish
</Button>
```
Also remove the `<form onSubmit={handleSubmit}>` wrapper around step 4 — replace with a plain `<div className="flex flex-col gap-4">`. The submit now happens on step 5.

Also add skip button to Step 4 after the Back button:
```tsx
<Button type="button" variant="ghost" onClick={() => setStep(5)} className="w-full" disabled={loading}>
  Skip to summary
</Button>
```

**8. Add Step 5 — Summary** after the step 4 block:
```tsx
{step === 5 && (
  <OnboardingSummaryStep
    hardwareInfo={hardwareInfo}
    selectedMode={selectedMode}
    providerLabel={deriveProviderLabel(puterToken, googleOAuthStateId, apiKeyData)}
    rootDir={rootDir}
    loading={loading}
    error={error}
    onStartChat={handleStartChat}
    onBack={() => setStep(4)}
  />
)}
```

**9. Create `handleStartChat` function** — adapts existing `handleSubmit` logic but also opens chat:
```typescript
async function handleStartChat() {
  // Guard: claude_local requires rootDir
  if (defaultAdapter === "claude_local" && !rootDir.trim()) {
    setError("Root directory is required for Claude Code. Go back to step 4 to set it.");
    return;
  }

  setLoading(true);
  setError(null);

  try {
    // === Same company creation logic as handleSubmit ===
    const company = await companiesApi.create({ name: VOCAB.appName });
    setSelectedCompanyId(company.id);
    queryClient.invalidateQueries({ queryKey: queryKeys.companies.all });

    // ... (copy all the agent creation, mode save, and credential storage code
    //      from handleSubmit — the baseAdapterConfig, hermesPromptTemplate,
    //      adapterConfig, runtimeConfig, PM agent create, Engineer agent create,
    //      queryClient invalidation, updateNexusSettings, puterToken/google/apiKey storage)

    // Navigate to dashboard then open chat panel
    closeOnboarding();
    navigate(`/${company.issuePrefix}/dashboard`);
    setChatOpen(true);  // <-- ONBD-06: opens chat panel after navigation
  } catch (err) {
    setError(err instanceof Error ? err.message : "Setup failed. Please try again.");
    setLoading(false);
  }
}
```

IMPORTANT: The `handleStartChat` function contains the SAME company/agent creation logic as the existing `handleSubmit`. The only differences are: (a) no `e: React.FormEvent` parameter, (b) error message mentions going back to step 4 for rootDir, (c) `setChatOpen(true)` is called after `navigate()`. You can either refactor the shared logic into an internal helper or duplicate it — refactoring into a helper like `createWorkspace()` is cleaner.

**10. Update the reset effect** — in the `useEffect` that resets form state when wizard closes, change `setStep(1)` to still reset to 1 (no change needed — step 5 naturally resets).

**11. Update the top comment** — change "4-step flow" to "5-step flow" and update the step list to include "summary".
  </action>
  <verify>
    <automated>cd /opt/nexus && pnpm vitest run ui/src/components/onboarding/</automated>
  </verify>
  <acceptance_criteria>
    - grep -q "OnboardingSummaryStep" ui/src/components/NexusOnboardingWizard.tsx
    - grep -q "useChatPanel" ui/src/components/NexusOnboardingWizard.tsx
    - grep -q "setChatOpen(true)" ui/src/components/NexusOnboardingWizard.tsx
    - grep -q "step === 5" ui/src/components/NexusOnboardingWizard.tsx
    - grep -q "deriveProviderLabel" ui/src/components/NexusOnboardingWizard.tsx
    - grep -c "Skip" ui/src/components/NexusOnboardingWizard.tsx shows at least 3 skip buttons
    - grep -q "Summary" ui/src/components/NexusOnboardingWizard.tsx (step indicator label)
    - grep -q "handleStartChat" ui/src/components/NexusOnboardingWizard.tsx
  </acceptance_criteria>
  <done>Wizard has 5 steps with skip on 1/2/4, summary on step 5, and "Start chatting" creates workspace then opens chat panel. TypeScript compiles and tests pass.</done>
</task>

</tasks>

<verification>
1. `pnpm vitest run ui/src/components/onboarding/` — all unit tests pass
2. `cd /opt/nexus && pnpm tsc --noEmit` — no TypeScript errors
3. Grep checks: `grep -q "setChatOpen(true)" ui/src/components/NexusOnboardingWizard.tsx` confirms ONBD-06 wiring
4. Grep checks: `grep -c "Skip" ui/src/components/NexusOnboardingWizard.tsx` confirms at least 3 skip buttons (ONBD-04)
5. Grep checks: `grep -q "OnboardingSummaryStep" ui/src/components/NexusOnboardingWizard.tsx` confirms summary wired (ONBD-05)
</verification>

<success_criteria>
- OnboardingSummaryStep component exists and renders hardware, mode, provider, root dir
- Skip buttons on steps 1, 2, and 4 (step 3 already has one)
- Step 5 shows "Summary" in the step indicator
- "Start chatting" on summary creates workspace, navigates to dashboard, and opens chat panel
- No corporate language ("company", "CEO", "mission") in the summary step
- All unit tests pass
- TypeScript compiles cleanly
</success_criteria>

<output>
After completion, create `.planning/phases/32-multi-step-onboarding-wizard/32-01-SUMMARY.md`
</output>
