nexus/.planning/phases/30-hardware-detection-mode-selection/30-01-SUMMARY.md
Nexus Dev 949f09ac54 docs(30-01): complete hardware detection + nexus settings plan
- Add 30-01-SUMMARY.md with execution record and deviation docs
- Update STATE.md: plan advanced to 2/2, progress 50%, decisions logged
- Update ROADMAP.md: phase 30 progress updated (1/2 plans complete)
- Update REQUIREMENTS.md: ONBD-01, ONBD-02, ONBD-03 marked complete
2026-04-04 03:55:49 +00:00

7.8 KiB

phase plan subsystem tags requires provides affects tech-stack key-files key-decisions patterns-established requirements-completed duration completed
30-hardware-detection-mode-selection 01 api
hardware-detection
systeminformation
settings-persistence
ollama
model-catalog
zod
phase provides
28-ollama-integration ollama service, model catalog, getRecommendedModel
hardwareService with Apple Silicon / GPU / cpu_only tier detection and 5-min cache
nexusSettingsService with Zod-validated file-backed mode persistence
Unauthenticated GET /api/system/providers endpoint for pre-auth hardware probe
GET /api/system/providers/recommendation — catalog filtered by detected tier
Board-auth-gated GET/PATCH /api/nexus/settings for mode persistence
Extended ollama-model-catalog.json with tier arrays on all variants and qwen3 family
getRecommendedModel supports optional hardwareTier parameter for tier-aware filtering
30-02-onboarding-ui
31-cloud-providers
33-personal-ai-assistant
added patterns
systeminformation@5
Promise.race with timeout for unreliable system calls (GPU detection)
vi.hoisted() for mock functions referenced in vi.mock() factories
File-backed JSON settings with Zod schema and graceful default fallback
Unauthenticated route mounted before boardMutationGuard by placing before api = Router()
created modified
server/src/services/hardware.ts
server/src/services/nexus-settings.ts
server/src/routes/hardware.ts
server/src/routes/nexus-settings.ts
server/src/__tests__/30-hardware-detection.test.ts
server/src/services/ollama.ts
server/src/data/ollama-model-catalog.json
server/src/app.ts
Use Promise.race with 3000ms timeout for si.graphics() — failure degrades to cpu_only tier, never blocks startup
Apple Silicon detected via process.platform === 'darwin' && cpuModel.startsWith('Apple') — does NOT call si.graphics()
Hardware routes mounted with app.use('/api', hardwareRoutes()) before const api = Router() to bypass boardMutationGuard
nexusSettingsService uses Zod z.enum(NEXUS_MODES).default('both') — validation at write time, default at read time
Model catalog tier arrays: cpu_only models serve all tiers, apple_silicon excludes large GPU-only models, gpu tier gets all
Promise.race timeout pattern: pair any unreliable external call with a reject-after-N-ms promise
vi.hoisted() pattern: use for mock function references needed inside vi.mock() factory callbacks
File-backed settings: mkdirSync recursive + writeFileSync in set(), graceful default in get() catch block
ONBD-02
ONBD-03
ONBD-01
15min 2026-04-02

Phase 30 Plan 01: Hardware Detection, Nexus Settings, Model Catalog Summary

Hardware tier detection (Apple Silicon/GPU/CPU-only) via systeminformation with 3s timeout, file-backed mode persistence via Zod-validated nexus-settings service, extended model catalog with tier arrays, and unauthenticated /api/system/providers endpoint

Performance

  • Duration: 15 min
  • Started: 2026-04-02T23:16:00Z
  • Completed: 2026-04-02T23:22:00Z
  • Tasks: 2
  • Files modified: 8

Accomplishments

  • Hardware detection service with Apple Silicon / discrete GPU / CPU-only tiers, 3-second timeout on GPU probe, 5-minute cache
  • Nexus settings service persisting mode to data/nexus-settings.json with Zod validation and graceful default
  • Extended model catalog with tier arrays on all 11 variants plus new qwen3:8b family (12 variants total)
  • getRecommendedModel upgraded with optional hardwareTier parameter for tier-aware filtering
  • Unauthenticated GET /api/system/providers endpoint mounted before boardMutationGuard
  • Board-auth-gated GET/PATCH /api/nexus/settings for mode persistence
  • 13 passing unit tests covering all hardware tiers, settings CRUD, catalog validation, and tier filtering

Task Commits

  1. Task 1: Hardware service, nexus-settings service, model catalog, and tests - 766460a1 (feat)
  2. Task 2: Hardware and nexus-settings routes, app.ts mounting - 86e30e5c (feat)

Files Created/Modified

  • server/src/services/hardware.ts - hardwareService factory with Apple Silicon/GPU/cpu_only detection and cache
  • server/src/services/nexus-settings.ts - nexusSettingsService with Zod validation and file persistence
  • server/src/routes/hardware.ts - Unauthenticated GET /system/providers and /system/providers/recommendation
  • server/src/routes/nexus-settings.ts - Board-auth-gated GET/PATCH /nexus/settings
  • server/src/__tests__/30-hardware-detection.test.ts - 13 unit tests using vi.hoisted() pattern
  • server/src/services/ollama.ts - Added tier?: string[] to CatalogVariant, hardwareTier param to getRecommendedModel
  • server/src/data/ollama-model-catalog.json - Added tier arrays to all variants, added qwen3 family
  • server/src/app.ts - Imported and mounted hardwareRoutes (before api router) and nexusSettingsRoutes (on api router)

Decisions Made

  • Used Promise.race with a 3000ms timeout for si.graphics() — this matches the plan spec and ensures GPU probe never blocks the server for more than 3 seconds
  • Apple Silicon path intentionally skips si.graphics() entirely — unified memory means GPU VRAM concept doesn't apply
  • Hardware routes placed before const api = Router() to remain outside boardMutationGuard — this is the correct Express mounting position for unauthenticated endpoints
  • Used vi.hoisted() to fix the Vitest mock hoisting issue — mockGraphicsFn must be initialized before vi.mock() factory runs

Deviations from Plan

Auto-fixed Issues

1. [Rule 1 - Bug] Fixed vi.mock() hoisting issue in test file

  • Found during: Task 1 (TDD test implementation)
  • Issue: const mockGraphicsFn = vi.fn() declared before vi.mock() but Vitest hoists vi.mock() calls, causing "Cannot access 'mockGraphicsFn' before initialization" error
  • Fix: Used vi.hoisted() to initialize mockGraphicsFn in a hoisted context, and updated all test references to use it directly instead of vi.mocked(si.default.graphics)
  • Files modified: server/src/tests/30-hardware-detection.test.ts
  • Verification: All 13 tests pass
  • Committed in: 766460a1 (Task 1 commit)

2. [Rule 2 - Missing Critical] Added _resetHardwareCache export for test isolation

  • Found during: Task 1 (TDD test implementation)
  • Issue: Module-level cache in hardware.ts bleeds between tests — hardware tier tests would return stale cached values
  • Fix: Exported _resetHardwareCache() function from hardware.ts, called in beforeEach in test file
  • Files modified: server/src/services/hardware.ts, server/src/tests/30-hardware-detection.test.ts
  • Verification: Each test runs with fresh detection state
  • Committed in: 766460a1 (Task 1 commit)

Total deviations: 2 auto-fixed (1 bug, 1 missing critical) Impact on plan: Both fixes necessary for correct test behavior. No scope creep.

Issues Encountered

  • Existing test suite has 18 pre-existing failures across 5 test files (skill-registry-routes, agent-permissions-routes, heartbeat-workspace-session, app-hmr-port, plugin-worker-manager) — confirmed pre-existing by stash test, not caused by this plan's changes.

User Setup Required

None - no external service configuration required.

Next Phase Readiness

  • Hardware probe API ready for consumption by Plan 02 onboarding UI
  • Mode persistence API ready — onboarding can set mode via PATCH /api/nexus/settings
  • Model catalog with tier arrays ready — onboarding can show tier-appropriate model recommendations
  • GET /api/system/providers/recommendation combines hardware detection + catalog filtering in one call

Phase: 30-hardware-detection-mode-selection Completed: 2026-04-02