Skip to content

feat(onboarding): minimalistic guided first-agent wizard - #1371

Merged
vybe merged 10 commits into
devfrom
feature/52-onboarding-wizard
Jun 29, 2026
Merged

feat(onboarding): minimalistic guided first-agent wizard#1371
vybe merged 10 commits into
devfrom
feature/52-onboarding-wizard

Conversation

@dolho

@dolho dolho commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

What

Gives a fresh self-hoster a guided path to first value instead of an empty dashboard. The wizard asks one question — "what do you want your first agent to do?" — maps the intent to a starter template, and deploys a tailored, ready-to-chat agent in a couple of clicks.

This is the first slice of the trinity-enterprise#52 onboarding epic: a single, self-contained PR that lets a person start their own AI agent in minutes with guidance. Subsequent slices (mandatory Claude-auth setup gate, ambient checklist, JIT GitHub PAT) spin off from the spec.

Design

  • Teach by doing, not reading. No mandatory click-through tour — the exact thing the "don't make me read" user rage-quits.
  • One question → a running agent. Intent cards map to the real local starter templates (scout/sage/scribe) or a blank Claude Code agent.
  • Soft-guide the one hard gate. Claude auth is surfaced as a non-blocking amber hint linking to Settings → Integrations; the user can deploy now and add it after. Everything else (e.g. GitHub PAT) is deferred just-in-time.
  • Never nags. Auto-opens once for a fresh, empty install; dismissal is remembered in localStorage.

Changes

Frontend

  • components/OnboardingWizard.vue — purpose picker → POST /api/agents → "Open chat" (/agents/:name?tab=chat). Verifies mapped templates exist against /api/templates; missing → blank-agent fallback so deploy never 404s.
  • views/Dashboard.vue — mounts the wizard, auto-opens via maybeAutoOpenOnboarding() (gated on !isFleetLoading && agents.length === 0 && !dismissed); empty-state "Get started" button re-opens it.
  • stores/sessions.js — caches claudeAuthConfigured from feature-flags.

Backend

  • routers/settings.pyGET /api/settings/feature-flags gains claude_auth_configured = bool(anthropic_api_key) (DB or env) or any registered subscription. A boolean only; never the key itself.

Removed

  • Dead OnboardingChecklist.vue + useOnboarding.js (orphaned, modeled on the removed Process Engine — mounted nowhere, linked to non-existent /processes/* routes).

Docs

  • docs/memory/feature-flows/onboarding-wizard.md — flow spec + follow-up build issues.

Test

  • Backend flag verified live: GET /api/settings/feature-flagsclaude_auth_configured: true on this dev instance.
  • Frontend: HMR-clean; OnboardingWizard.vue transforms without errors via the dev server.
  • Settings deep-link ?tab=integrations resolves to the Integrations tab.
  • Note: the wizard auto-opens only on an empty fleet, so it isn't reachable from the UI on an instance that already has agents.

Related to Abilityai/trinity-enterprise#52

🤖 Generated with Claude Code

dolho and others added 9 commits June 29, 2026 10:44
A fresh self-hoster who opens the web UI now gets a guided path to first
value instead of an empty dashboard. The wizard asks one question — "what
do you want your first agent to do?" — maps the intent to a starter
template, and deploys a tailored, ready-to-chat agent in a couple of clicks.

Design: teach by doing, not by reading. No mandatory tour. The one hard
setup gate (Claude auth) is surfaced as a non-blocking hint linking to
Settings → Integrations; everything else is deferred just-in-time.

- components/OnboardingWizard.vue: purpose picker → POST /api/agents →
  "Open chat". Verifies mapped local templates exist (scout/sage/scribe),
  falls back to a blank Claude Code agent so deploy never 404s.
- views/Dashboard.vue: auto-opens once on a fresh, empty install
  (dismissal remembered in localStorage); empty-state "Get started"
  button re-opens it.
- routers/settings.py: feature-flags gains claude_auth_configured (bool;
  anthropic key in DB/env OR any subscription) so the wizard's setup hint
  reflects reality. Cached in stores/sessions.js.
- Remove orphaned OnboardingChecklist.vue / useOnboarding.js (dead code
  built on the removed Process Engine).
- docs: onboarding-wizard flow spec + follow-up build issues.

Related to Abilityai/trinity-enterprise#52

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Lets a user re-run onboarding (and QA preview it on a non-empty fleet)
without deleting agents. Bypasses the empty-fleet gate and prior dismissal.

Related to Abilityai/trinity-enterprise#52

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Per review: the wizard shouldn't be a parallel deploy button. It now
guides the user through the REAL UI and carries them to the credential
the agent needs.

- Step 'create' delegates to the actual CreateAgentModal (template
  prefilled from the chosen purpose) instead of calling createAgent
  itself — the user clicks the real create button.
- Step 'credential' leads to connecting a Claude subscription
  (Settings -> Integrations -> Claude Subscriptions), with step-by-step
  'claude setup-token' guidance; if Claude is already connected it
  confirms and offers 'Open chat' instead.

Related to Abilityai/trinity-enterprise#52

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two wiring bugs made the wizard vanish on create with no guidance:
- Dashboard wired @deployed to closeOnboarding, unmounting the wizard the
  moment the agent was created. Now @deployed refreshes the fleet (so the
  new agent shows without a page reload) and leaves the wizard open to
  advance to its credential step.
- CreateAgentModal emits 'created' then 'close'; onModalClose reset the
  step back to the picker. Now it only resets on a real cancel (still on
  the create step), so the post-create 'close' no longer clobbers the
  credential step.

Related to Abilityai/trinity-enterprise#52

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The wizard backdrop was z-40, one tier below the app's modal/dropdown
layer (z-50), so header chrome like the time-range control showed through
the dim overlay instead of being grayed out. Bump to z-50 to match
SystemViewEditor / the build-info modal.

Related to Abilityai/trinity-enterprise#52

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
z-50 alone didn't dim the header time-range control — the wizard was
nested in the Dashboard's layout, confining its stacking. Teleport the
overlay to <body> so it renders in the document's top stacking context
and the dim backdrop covers everything behind it, every mode.

Related to Abilityai/trinity-enterprise#52

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…sters

Self-hosters get the onboarding wizard automatically on a fresh, empty
install, but the relaunch link wasn't documented anywhere. Add a 'Guided
Onboarding (First Run)' section to the Quick Start (auto-open behavior +
http://localhost/?onboarding=1 relaunch, with the 'log in first' caveat),
and a pointer from Setup so it's discoverable right after install.

Related to Abilityai/trinity-enterprise#52

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
trinity-system is present on every install and is included in the
network store's agents list, so 'agents.length === 0' was never true and
the wizard would never auto-open on a genuinely fresh install. Gate on
the count of non-system agents instead.

Related to Abilityai/trinity-enterprise#52

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…enly

A decisive click-through test confirmed the backdrop already covers the
timeline time-ruler (clicking it dismisses the wizard; elementsFromPoint
returns the backdrop) — it was never a stacking bug. The ruler just has a
white background, so at 70% opacity it read as a light-gray band that
looked 'not grayed' next to darker UI. Bumping to 85% dims white rows
closer to everything else.

Related to Abilityai/trinity-enterprise#52

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@dolho

dolho commented Jun 29, 2026

Copy link
Copy Markdown
Contributor Author
Screenshot from 2026-06-29 11-48-34 Screenshot from 2026-06-29 11-48-42

@dolho
dolho requested a review from vybe June 29, 2026 08:49
/>

<!-- intro + credential: the wizard's own guidance card -->
<div v-else class="fixed inset-0 z-50 overflow-y-auto">

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[medium] Modal a11y gap. This overlay has no role="dialog"/aria-modal="true", no focus trap, no Escape-to-close, and no body scroll-lock. The repo just standardized exactly these for ChannelConfigDialog (#19: "focus mgmt, trap, scroll lock"), so this regresses against that bar. At minimum: add role="dialog" aria-modal="true" + an aria-labelledby pointing at the <h2>, an Escape @keydown, and focus the first control on open. Reusing the existing dialog pattern/composable would keep it consistent.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 872afe0 — added role=dialog/aria-modal/aria-labelledby, Escape-to-close, a Tab focus trap, focus-on-open, and body scroll-lock.

Comment thread src/backend/routers/settings.py Outdated
# Anthropic key exists (DB or env) OR any Claude subscription is
# registered. Non-sensitive: a boolean, never the key itself.
"claude_auth_configured": bool(settings_service.get_anthropic_api_key())
or bool(db.list_subscriptions()),

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[low-med] Hot-path full table read. /api/settings/feature-flags is fetched on nearly every page load (NavBar, Dashboard, AgentDetail). When no Anthropic key is set, this calls db.list_subscriptions() — materializing every subscription row (decrypting credentials in SubscriptionWithAgents? at least full hydration) just to test existence. Prefer a cheap COUNT(*) > 0 / EXISTS helper (e.g. db.has_any_subscription()). The or short-circuits when a key exists, so this only bites key-less installs — which is exactly the onboarding state this flag targets, so it will be hit.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 872afe0 — added db.has_any_subscription() (COUNT existence check); the flag now uses it instead of bool(db.list_subscriptions()), so no rows are materialized/decrypted on this hot path.

// Confirm mapped local templates exist; fall back to blank so the prefilled
// CreateAgentModal never points at a missing template.
try {
const r = await axios.get('/api/templates', { headers: authStore.authHeader })

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[low] Duplicate templates fetch. The wizard fetches /api/templates here only to downgrade a missing local template to blank, and then CreateAgentModal fetches /api/templates again on its own mount in the create step — two round-trips for the same data within one flow. Minor; acceptable if you prefer the isolation, but worth a note. The fallback also mutates p.template in place on the ref array, which works but is a little surprising.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 872afe0 — dropped the wizard's /api/templates fetch; the missing-template→blank fallback now lives in CreateAgentModal (which already fetches), so the create flow makes one request.

…p fetch)

- a11y (medium): OnboardingWizard now has role=dialog/aria-modal/
  aria-labelledby, Escape-to-close, a Tab focus trap, focus-on-open, and
  body scroll-lock. Matches the modal-a11y bar set in #19.
- perf (low-med): /api/settings/feature-flags no longer materializes every
  subscription row to compute claude_auth_configured. New
  db.has_any_subscription() does a COUNT existence check; replaces
  bool(db.list_subscriptions()).
- cleanup (low): drop the wizard's duplicate /api/templates fetch. The
  missing-template->blank fallback now lives in CreateAgentModal (the
  component that already fetches templates), so the create flow makes one
  request instead of two.

Related to Abilityai/trinity-enterprise#52

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@vybe vybe left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Validated via /validate-pr: backend flag is boolean-only (no key leak) with a parameterized COUNT existence check; frontend reuses the real CreateAgentModal (no cloned API path, Invariant #7) and safely net-removes dead OnboardingChecklist/useOnboarding (green build confirms no remaining importers). All 18 required checks green. Non-blocking follow-ups: cross-tracker trinity-enterprise#52 needs a manual status-in-dev bump; feature-flows.md index not updated for the new flow doc.

@vybe
vybe merged commit a953bd2 into dev Jun 29, 2026
20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants