nexus/.planning/phases/30-hardware-detection-mode-selection/30-VERIFICATION.md
2026-04-04 03:55:49 +00:00

15 KiB

phase verified status score human_verification
30-hardware-detection-mode-selection 2026-04-02T23:58:20Z human_needed 11/11 must-haves verified
test expected why_human
Visual walkthrough of 3-step onboarding wizard Step 1 shows skeleton then hardware info; Step 2 shows three mode cards with 'Both (recommended)' pre-selected with blue border; Step 3 shows root directory input; Back navigation works between steps; wizard closes after Get Started Visual rendering, interaction flow, and selected state styling cannot be verified programmatically
test expected why_human
Privacy frame appears for GPU and Apple Silicon tiers On a machine with GPU or Apple Silicon, 'Local AI (recommended for privacy)' copy and 'Runs entirely on your machine. No accounts. No tracking. Works offline.' appears below hardware stats Tier detection depends on runtime hardware; test environment is linux/cpu_only so only the cpu_only path runs in CI
test expected why_human
Mode selection persists to disk on wizard completion After completing onboarding with 'Personal AI Assistant' selected, data/nexus-settings.json contains { mode: 'personal_ai' } Requires running the full wizard flow end-to-end with a real server instance

Phase 30: Hardware Detection + Mode Selection — Verification Report

Phase Goal: Users see accurate hardware information during onboarding, get a model recommendation matched to their machine, and choose a mode that correctly gates all downstream features — with the probe working before board auth exists Verified: 2026-04-02T23:58:20Z Status: human_needed Re-verification: No — initial verification

Goal Achievement

Observable Truths

# Truth Status Evidence
1 GET /api/system/providers returns 200 with hardware info without any auth token ✓ VERIFIED hardwareRoutes() mounted with app.use("/api", hardwareRoutes()) at line 132 of app.ts, BEFORE const api = Router() at line 135 and api.use(boardMutationGuard()) at line 136
2 Apple Silicon is detected via CPU brand string and returns unifiedMemory: true with hardwareTier: apple_silicon ✓ VERIFIED hardware.ts line 45: platform === "darwin" && cpuModel?.startsWith("Apple") — returns unifiedMemory: true, hardwareTier: "apple_silicon". Test confirms: returns apple_silicon tier when platform is darwin and CPU starts with Apple
3 GPU detection via systeminformation has a 3-second timeout; failure degrades to cpu_only tier ✓ VERIFIED hardware.ts lines 68-71: Promise.race([si.graphics(), new Promise<never>((_resolve, reject) => { setTimeout(() => reject(...), 3000) })]). Catch block sets hardwareTier = "cpu_only". Test confirms timeout scenario passes
4 nexusSettingsService persists mode to data/nexus-settings.json and reads it back ✓ VERIFIED nexus-settings.ts: set() writes to resolvePaperclipInstanceRoot()/data/nexus-settings.json, get() reads it back with Zod validation and graceful default of { mode: "both" }. Tests confirm CRUD round-trip
5 PATCH /api/nexus/settings requires board auth and persists the mode value ✓ VERIFIED nexus-settings route lines 23-35: assertBoard(req) before nexusSettingsService().set(req.body). Route mounted on api router (inside boardMutationGuard) via api.use(nexusSettingsRoutes()) at app.ts line 163
6 Model catalog contains tier field on every variant and includes qwen3:8b family ✓ VERIFIED node -e check: Total variants: 12, Missing tier: 0, Has qwen3: true — all 12 variants have tier arrays, qwen3 family exists
7 getRecommendedModel filters by hardware tier when tier data is present ✓ VERIFIED ollama.ts line 186: if (hardwareTier && entry.tier && !entry.tier.includes(hardwareTier)) continue;. Signature accepts optional `hardwareTier?: "gpu"
8 User sees hardware detection results (GPU/RAM/unified memory) during onboarding within 5 seconds ✓ VERIFIED HardwareSummaryStep.tsx renders tier-appropriate stat rows using hardwareInfo from useHardwareInfo() hook. Server-side 3s GPU timeout + React Query retry:1 ensures result within 5s
9 User can select mode: Personal AI Assistant, Project Builder, or Both (default pre-selected) ✓ VERIFIED ModeSelector.tsx renders three cards with correct labels. NexusOnboardingWizard.tsx: useState<NexusMode>("both") — default "both" as required
10 Selected mode is persisted via PATCH /api/nexus/settings on wizard completion ✓ VERIFIED NexusOnboardingWizard.tsx lines 180-184: await updateNexusSettings({ mode: selectedMode }) called after company creation, wrapped in try/catch
11 Wizard has 3 steps: hardware detection, mode selection, root directory (existing) ✓ VERIFIED NexusOnboardingWizard.tsx: useState(1) for step, Step {step} of 3 indicator, conditional rendering for steps 1/2/3 with Back navigation on steps 2 and 3

Score: 11/11 truths verified

Required Artifacts

Artifact Expected Status Details
server/src/services/hardware.ts hardwareService with detect() returning HardwareInfo ✓ VERIFIED Exports hardwareService, HardwareInfo, HardwareTier, _resetHardwareCache. 105 lines, fully implemented
server/src/services/nexus-settings.ts File-backed nexus settings persistence ✓ VERIFIED Exports nexusSettingsService, NexusMode, NEXUS_MODES. Zod-validated, file-backed. 47 lines
server/src/routes/hardware.ts Unauthenticated GET /api/system/providers ✓ VERIFIED Exports hardwareRoutes. Two routes: /system/providers and /system/providers/recommendation. Unauthenticated comment present
server/src/routes/nexus-settings.ts Board-auth-gated GET/PATCH /api/nexus/settings ✓ VERIFIED Exports nexusSettingsRoutes. Both routes call assertBoard(req) before service calls
server/src/data/ollama-model-catalog.json Extended model catalog with tier arrays and qwen3 family ✓ VERIFIED 12 variants, 0 missing tier, qwen3 family present with qwen3:8b
server/src/__tests__/30-hardware-detection.test.ts Unit tests for hardware service, settings service, routes, and catalog ✓ VERIFIED 13 tests across 4 describe blocks; all pass
ui/src/api/hardware.ts Typed fetch wrappers for hardware probe and nexus settings ✓ VERIFIED Exports fetchHardwareInfo, fetchNexusSettings, updateNexusSettings + all types. 34 lines
ui/src/hooks/useHardwareInfo.ts useQuery wrapper for hardware data ✓ VERIFIED Exports useHardwareInfo. Uses queryKeys.hardware.info, 5-min staleTime, retry:1
ui/src/components/onboarding/ModeSelector.tsx Three-card mode selector with selected state styling ✓ VERIFIED Three cards, border-primary bg-primary/5 selected state, correct labels
ui/src/components/onboarding/HardwareSummaryStep.tsx Hardware info display with skeleton loading, tier-appropriate labels, privacy frame ✓ VERIFIED All three tier paths implemented, privacy frame for non-cpu_only, Skeleton loading, error state
ui/src/components/NexusOnboardingWizard.tsx Multi-step wizard: hardware -> mode -> root directory ✓ VERIFIED 3-step flow, step indicator, Back navigation, mode persistence on submit, export function OnboardingWizard() preserved
From To Via Status Details
server/src/routes/hardware.ts server/src/services/hardware.ts hardwareService().detect() ✓ WIRED Line 12: const info = await hardwareService().detect()
server/src/app.ts server/src/routes/hardware.ts app.use("/api", hardwareRoutes()) ✓ WIRED Line 132: app.use("/api", hardwareRoutes()) — before const api = Router() at line 135
server/src/services/ollama.ts server/src/data/ollama-model-catalog.json loadCatalog() ✓ WIRED loadCatalog() exists in ollama.ts (line 79), reads catalog JSON by resolved __dirname path
ui/src/hooks/useHardwareInfo.ts /api/system/providers fetch in useQuery ✓ WIRED Hook calls fetchHardwareInfo() from api/hardware.ts which calls api.get("/system/providers")
ui/src/components/onboarding/HardwareSummaryStep.tsx ui/src/hooks/useHardwareInfo.ts useHardwareInfo hook ✓ WIRED Props flow: hardwareInfo, isLoading, isError from useHardwareInfo() in wizard, passed down to HardwareSummaryStep
ui/src/components/NexusOnboardingWizard.tsx ui/src/api/hardware.ts updateNexusSettings on wizard complete ✓ WIRED Lines 180-184: await updateNexusSettings({ mode: selectedMode })
ui/src/components/NexusOnboardingWizard.tsx ui/src/components/onboarding/ModeSelector.tsx React component composition ✓ WIRED Line 250: <ModeSelector value={selectedMode} onChange={setSelectedMode} />

Data-Flow Trace (Level 4)

Artifact Data Variable Source Produces Real Data Status
HardwareSummaryStep.tsx hardwareInfo useHardwareInfo()fetchHardwareInfo()GET /api/system/providershardwareService().detect()os.totalmem(), os.freemem(), si.graphics() Yes — live OS calls ✓ FLOWING
ModeSelector.tsx value: NexusMode useState<NexusMode>("both") in wizard, updated by onChange Yes — interactive state ✓ FLOWING
NexusOnboardingWizard.tsx (persist) selectedMode useState<NexusMode>("both")updateNexusSettings({ mode: selectedMode })PATCH /api/nexus/settingsnexusSettingsService().set()writeFileSync Yes — writes to data/nexus-settings.json ✓ FLOWING

Behavioral Spot-Checks

Behavior Command Result Status
All 13 hardware tests pass npx vitest run src/__tests__/30-hardware-detection.test.ts 13/13 passed (308ms) ✓ PASS
Model catalog: all variants have tier node -e "..." catalog check Total: 12, Missing tier: 0, Has qwen3: true ✓ PASS
Hardware routes mounted before boardMutationGuard grep lines 131-136 of app.ts app.use("/api", hardwareRoutes()) at line 132, const api = Router() at line 135 ✓ PASS
TypeScript compilation (UI) pnpm --filter ui exec tsc --noEmit 1 pre-existing error in AgentConfigForm.tsx (detectModel — introduced by prior Hermes commit 1583a2d6, not by phase 30; phase 30 files have 0 TS errors) ⚠️ PRE-EXISTING

Requirements Coverage

Requirement Source Plan Description Status Evidence
ONBD-01 30-01, 30-02 User can select mode (Personal AI Assistant / Project Builder / Both) during onboarding ✓ SATISFIED ModeSelector.tsx three-card UI; NexusOnboardingWizard.tsx persists via updateNexusSettings
ONBD-02 30-01 System auto-detects GPU, RAM, and Apple Silicon unified memory within 5 seconds ✓ SATISFIED hardwareService().detect() with 3s GPU timeout; HardwareSummaryStep displays results; server + React Query retry:1 ensures < 5s total
ONBD-03 30-01 System recommends best local model from pre-built JSON database based on detected hardware ✓ SATISFIED Catalog extended with tier arrays; getRecommendedModel(models, ram, hardwareTier) filters by tier; GET /api/system/providers/recommendation endpoint returns tier-filtered catalog
ONBD-07 30-02 Local AI framed as privacy premium ("runs entirely on your machine, no accounts, works offline") ✓ SATISFIED HardwareSummaryStep.tsx lines 85-92: "Local AI (recommended for privacy)" + "Runs entirely on your machine. No accounts. No tracking. Works offline." shown when hardwareTier !== "cpu_only"

All 4 requirement IDs from PLAN frontmatter are accounted for. No orphaned requirements found for this phase.

Anti-Patterns Found

File Line Pattern Severity Impact
ui/src/components/AgentConfigForm.tsx 351 agentsApi.detectModel — property does not exist on agentsApi type (TS2339) ⚠️ Warning Pre-existing error from commit 1583a2d6 (Hermes adapter upgrade); not introduced by phase 30; does not affect hardware/mode-selection functionality

No stub patterns, empty returns, placeholder comments, or disconnected data paths found in any phase 30 artifacts.

Human Verification Required

1. Visual Walkthrough of 3-Step Onboarding Wizard

Test: Start dev server (pnpm dev in /opt/nexus), open browser to http://localhost:3100. Trigger the onboarding wizard. Walk through all 3 steps. Expected:

  • Step 1: Shows "Detecting your hardware..." heading with skeleton rows while probe is in-flight, transitions to "Your hardware" with real stats. Continue button always enabled.
  • Step 2: "Choose your mode" heading. Three vertically-stacked cards: "Personal AI Assistant", "Project Builder", "Both (recommended)". "Both" pre-selected with blue border (border-primary bg-primary/5). Clicking other cards moves selection. Step indicator shows "Step 2 of 3". Back button present.
  • Step 3: Existing root directory form preserved. "Welcome to [appName]" heading. Back button works (returns to step 2). Get Started submits and closes wizard. Why human: Visual rendering, interaction state, and card selection styling cannot be verified programmatically.

2. Privacy Frame for GPU/Apple Silicon Tiers

Test: Run on a machine with a GPU (>= 4GB VRAM) or Apple Silicon. Complete step 1 of onboarding wizard. Expected: Below the hardware stats, "Local AI (recommended for privacy)" heading appears with "Runs entirely on your machine. No accounts. No tracking. Works offline." body text. This text should NOT appear on CPU-only machines. Why human: Hardware tier depends on the runtime machine. The test environment (Linux, no discrete GPU) only exercises the cpu_only path.

3. Mode Persistence to Disk

Test: Complete the full onboarding wizard with "Personal AI Assistant" selected. After wizard closes, check ~/.paperclip/<instance>/data/nexus-settings.json (or wherever resolvePaperclipInstanceRoot() resolves). Expected: File contains { "mode": "personal_ai" }. Why human: Requires a full running server instance and end-to-end wizard submission.

Gaps Summary

No automated gaps found. All 11 must-have truths are verified. All 11 artifacts are substantive and wired. All 4 key links are confirmed. All 4 requirement IDs (ONBD-01, ONBD-02, ONBD-03, ONBD-07) are satisfied.

The sole TS error (AgentConfigForm.tsx detectModel) is pre-existing and introduced by commit 1583a2d6 prior to phase 30. Phase 30 did not touch AgentConfigForm.tsx (confirmed by git log -- ui/src/components/AgentConfigForm.tsx showing no phase-30 commits).

3 items are routed to human verification: the visual wizard flow, privacy frame display on capable hardware, and mode persistence end-to-end.


Verified: 2026-04-02T23:58:20Z Verifier: Claude (gsd-verifier)