DOB-3: Setup wizard seeds the minimum at scope='global' from code-side presets #795

Closed
opened 2026-05-03 18:05:00 +00:00 by claude-desktop · 1 comment
Collaborator

As an operator booting a fresh claude-hooks install, I want the first-login wizard to seed a usable minimum directly into the DB at scope='global' (no JSON file involved), so that I can pick a preset (Blank / TypeScript / Rust / …) and immediately have a working service — without syncBuiltinsFromRepo running.

Today the wizard relies on syncBuiltinsFromRepo populating scope='builtin' rows. Once DOB-1 + DOB-2 land, builtin rows don't exist and the resolver doesn't read them — so the wizard needs its own seed path.

Acceptance criteria

Presets module

  • apps/server/src/setup/presets.ts exports a typed map of presets:
    export const PRESETS = {
      blank:      { agentTypes: [], labels: [], serviceOverrides: {} },
      typescript: { agentTypes: [...], labels: [...], serviceOverrides: {} },
      rust:       { agentTypes: [...], labels: [...], serviceOverrides: {} },
    } as const satisfies Record<string, Preset>;
    
    Shape includes whatever the wizard needs to write so the operator's first dispatch works (one or more agent_type rows, label catalog entries, optional service overrides). No more, no less — bare minimum.
  • seedPreset(presetName, db) writes the preset's contents as scope='global' rows via the same CRUD code paths the dashboard uses (no direct INSERTs that bypass validation / audit logging).

Wizard wiring

  • First-login wizard's "pick a preset" step calls seedPreset(...) instead of relying on syncBuiltinsFromRepo.
  • If the operator picks Blank, no rows are seeded at all — the dashboard offers full per-field creation. Service still boots; resolvers fall to code-side defaults from DOB-1.
  • Audit log (config_revision) records each row written by the preset with actor='setup-wizard' so the operator can see what was seeded and revision-restore it.

Tests

  • Test: empty DB → seedPreset('typescript') → expected scope='global' rows present, no scope='builtin' rows ever written.
  • Test: seedPreset('blank') → zero rows written, dashboard service-config endpoint returns the code-side defaults.
  • Test: re-running seedPreset is idempotent (or rejects with a clear message — pick one and document inline).

Out of scope

  • Removing syncBuiltinsFromRepo (DOB-4) — until DOB-4 ships, both paths can coexist; once DOB-4 lands, only the wizard seeds.
  • Adding new presets beyond what the existing config/starter-packs.json shipped — port one-for-one, polish later.
  • A "reset to defaults" button on the dashboard — separate story if wanted.

References

  • Code: apps/web/src/routes/setup.*, apps/server/src/setup/, apps/server/src/infrastructure/agent-env-sync/builtin-sync.ts (current logic to mirror).
  • Spec: specs/first-login-wizard.md.
  • Depends on: DOB-1 (#793). Sibling-of: DOB-2 (#794), DOB-4.
As an operator booting a fresh `claude-hooks` install, I want the first-login wizard to seed a usable minimum directly into the DB at `scope='global'` (no JSON file involved), so that I can pick a preset (Blank / TypeScript / Rust / …) and immediately have a working service — without `syncBuiltinsFromRepo` running. Today the wizard relies on `syncBuiltinsFromRepo` populating `scope='builtin'` rows. Once DOB-1 + DOB-2 land, builtin rows don't exist and the resolver doesn't read them — so the wizard needs its own seed path. ## Acceptance criteria ### Presets module - [ ] `apps/server/src/setup/presets.ts` exports a typed map of presets: ```ts export const PRESETS = { blank: { agentTypes: [], labels: [], serviceOverrides: {} }, typescript: { agentTypes: [...], labels: [...], serviceOverrides: {} }, rust: { agentTypes: [...], labels: [...], serviceOverrides: {} }, } as const satisfies Record<string, Preset>; ``` Shape includes whatever the wizard needs to write so the operator's first dispatch works (one or more `agent_type` rows, label catalog entries, optional service overrides). No more, no less — bare minimum. - [ ] `seedPreset(presetName, db)` writes the preset's contents as `scope='global'` rows via the same CRUD code paths the dashboard uses (no direct INSERTs that bypass validation / audit logging). ### Wizard wiring - [ ] First-login wizard's "pick a preset" step calls `seedPreset(...)` instead of relying on `syncBuiltinsFromRepo`. - [ ] If the operator picks **Blank**, no rows are seeded at all — the dashboard offers full per-field creation. Service still boots; resolvers fall to code-side defaults from DOB-1. - [ ] Audit log (`config_revision`) records each row written by the preset with `actor='setup-wizard'` so the operator can see what was seeded and revision-restore it. ### Tests - [ ] Test: empty DB → `seedPreset('typescript')` → expected `scope='global'` rows present, no `scope='builtin'` rows ever written. - [ ] Test: `seedPreset('blank')` → zero rows written, dashboard service-config endpoint returns the code-side defaults. - [ ] Test: re-running `seedPreset` is idempotent (or rejects with a clear message — pick one and document inline). ## Out of scope - Removing `syncBuiltinsFromRepo` (DOB-4) — until DOB-4 ships, both paths can coexist; once DOB-4 lands, only the wizard seeds. - Adding new presets beyond what the existing `config/starter-packs.json` shipped — port one-for-one, polish later. - A "reset to defaults" button on the dashboard — separate story if wanted. ## References - Code: `apps/web/src/routes/setup.*`, `apps/server/src/setup/`, `apps/server/src/infrastructure/agent-env-sync/builtin-sync.ts` (current logic to mirror). - Spec: `specs/first-login-wizard.md`. - Depends on: DOB-1 (#793). Sibling-of: DOB-2 (#794), DOB-4.
Collaborator

🤖 Auto-assigned to code-lead (heuristic: area:dashboard + body 2841 bytes (> 2 KB) — code-lead (heavy)). Reply /unassign to reroute.

🤖 Auto-assigned to **code-lead** (heuristic: area:dashboard + body 2841 bytes (> 2 KB) — code-lead (heavy)). Reply `/unassign` to reroute.
Sign in to join this conversation.
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
charles/claude-hooks#795
No description provided.