Skip to content
42 changes: 42 additions & 0 deletions docs/design/simplify-nav-new-users/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Simplify navigation for new signup users

**Status:** Implemented
**Date:** 2026-07-27

New signup users land on the full platform sidebar, which is noise when their only goal is
building an agent. This workspace plans a nav-only simplification in two phases. **Phase 1**
hides the advanced pages (Prompts, the Evaluation group, Overview, Registry, Evaluations) for
new signups. **Phase 2** adds a per-user **"Classic mode"** switch in Settings →
Feature flags so anyone can switch back to the full view to use the LLM-app pages. Existing and
returning users default to the full view throughout.

## Decisions

- Two phases: Phase 1 = hide-for-new-signups; Phase 2 = the Settings → Feature flags switch
(additive — touches no Phase-1 file except one derived atom).
- Nav-only — hide sidebar entries; no route guards, no in-app link changes.
- Hide when simplified: Prompts, Evaluation group (project); Overview, Registry, Evaluations (app).
- Keep always: Home, Agents, Observability, app Playground.
- Default mode seeded by a fresh forward-only key `navSimplifiedDefaultAtom`, written at signup
(new signups → simplified); everyone else → full. Deliberately **not** `isNewUserAtom`, which
is sticky-true for existing users and would strip their advanced nav.
- Stable seam: the sidebar reads one derived atom `advancedNavHiddenAtom` (in `state/onboarding`
selectors) — Phase 1 `= navSimplifiedDefault`, Phase 2 `= override ?? navSimplifiedDefault`.
- No backend, no team-wide enforcement — per-user preference; an invited teammate flips the
Phase-2 switch to match their team. A workspace-level flag is deferred, not blocked.
- Built on the sidebar's existing `isHidden` mechanism; one OSS sidebar file, both editions inherit it.

## Deliverables

- [context.md](./context.md) — problem, phased scope, out-of-scope, product language, success criteria.
- [research.md](./research.md) — how the sidebar, new-user state, and settings tabs work today, with `file:line`.
- [plan.md](./plan.md) — the phased, sliced implementation plan (Phase 1: Slice 0–4; Phase 2: Slice 5–6).
- [status.md](./status.md) — locked decisions, open questions, next action.

## Intended outcome

A user who has just signed up sees a focused, agent-first sidebar: Home, Agents, and
Observability at the project level, Playground + Observability inside an app. Anyone who wants
the complete platform — an existing LLM-app team, or an invited teammate — enables Classic mode
in Settings → Feature flags and gets it back, with the choice remembered. No one is ever stuck in the
wrong view.
90 changes: 90 additions & 0 deletions docs/design/simplify-nav-new-users/context.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# Context — Simplify navigation for new signup users

## Problem

The sidebar exposes the full platform to everyone the moment they sign up. For a brand-new
user whose only goal is building an agent, that surface is noise: Prompts, the whole
Evaluation group, Registry, Evaluations, and the app Overview are pages the agent flow does
not need yet. They add cognitive load and lead to dead-end or empty pages during the first
session.

The product has pivoted to agent building (see `docs/design/onboarding-revamp/HANDOFF.md`).
The navigation should match that focus for people arriving new — while still letting anyone
who needs the full platform switch to it.

## Scope

Nav-only. The entries disappear from the sidebar; nothing else changes. Delivered in two
phases.

**Advanced items hidden when the simplified view is active:**

Project scope (`projectItems` in `useSidebarConfig`):

- Prompts
- Evaluation — the entire group (Test sets, Evaluators, Evaluation runs, Annotation Queues)

App scope (`appItems` in `useSidebarConfig`):

- Overview
- Registry
- Evaluations

Everything else always stays: Home, Agents, project Observability; app Playground and app
Observability.

### Phase 1 (this delivery) — hide for new signups

New signup users get the simplified sidebar, flagged by a fresh forward-only key
(`navSimplifiedDefaultAtom`, `agenta:onboarding:<userId>:nav-simplified`) seeded at signup.
Everyone else is unaffected — existing users never have the key, so they keep the full nav. We
do not reuse `isNewUserAtom` (sticky-true for everyone who ever signed up; reusing it would
break current users). There is no switch yet, so a genuinely-new solo user stays simplified
until Phase 2 ships. Invited teammates are never flagged (see research.md §7), so they keep the
full nav.

### Phase 2 (follow-up) — the toggle

A "Classic mode" switch in **Settings → Feature flags**, backed by a per-user localStorage
override. A new user can reveal everything (e.g. to use the LLM-app pages), while existing
users continue to see the full navigation by default. Phase 2
is additive — it changes no Phase-1 file except the one derived atom.

## Out of scope (both phases)

- **Route guards.** Direct URLs (`/prompts`, `/evaluations`, `/apps/[id]/overview`,
`/apps/[id]/variants`, `/apps/[id]/evaluations`) still resolve. We only remove the nav
entry points.
- **In-app links.** Buttons or cards elsewhere that navigate to a hidden page keep working.
- **Team-wide enforcement.** The preference is per-user, not per-workspace. An agent team's
invited teammate defaults to the full view and (in Phase 2) flips the switch to match the
team; we do not force every member to the same mode. A workspace-level flag would do that,
but it needs a backend field and is deferred. The override model does not block adding it.
- **A backend field.** No server change in either phase. Everything is client-side localStorage.

## Product language

- **New signup user** — a user who signs up after this ships, flagged by the fresh
`navSimplifiedDefaultAtom` key (`agenta:onboarding:<userId>:nav-simplified`), seeded `true` at
signup alongside `setIsNewUser(true)`. Distinct from `isNewUserAtom`, which is sticky-true for
existing users and is deliberately not reused.
- **Simplified navigation** — the reduced, agent-focused sidebar (advanced items hidden).
- **Full navigation** — the complete sidebar (today's behavior).
- **Simplified-nav preference** — the per-user override (`navSimplifiedOverrideAtom`, Phase 2):
`null` = follow the default, `true` = force simplified, `false` = force full.
- **Effective mode** — `override ?? navSimplifiedDefault`, exposed as `advancedNavHiddenAtom`
(in `state/onboarding` selectors). This single value drives both the sidebar and the switch.

## Success criteria

**Phase 1**

1. A new signup user sees the simplified sidebar (no Prompts, Evaluation group, Overview,
Registry, or Evaluations).
2. An existing/returning user sees the full sidebar exactly as before.
3. Both editions (OSS and EE) get the behavior from one sidebar change — no EE fork.
4. Hidden items never render, auto-open, or become the selected key while simplified.
5. No backend change is introduced.

**Phase 2** 6. The Settings → Feature flags switch flips the effective mode and survives a
reload. 7. Toggling the switch updates the sidebar without a full page reload. 8. Phase 1's sidebar behavior is unchanged when no override is set.
206 changes: 206 additions & 0 deletions docs/design/simplify-nav-new-users/plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
# Plan — Simplify nav for new signup users

Two phases. **Phase 1** ships the hide-for-new-signups behavior. **Phase 2** adds the
Classic mode switch under Settings → Feature flags so users can switch back to the full view
(e.g. to use the LLM-app pages). Phase 2 is **purely additive** — it touches no Phase-1 file
except the one derived atom.

## Scope

- Nav-only. No route guards, no in-app link changes. No backend. No team-wide enforcement.
- **Phase 1:** new signups (flagged by the fresh `nav-simplified` key) get the simplified
sidebar; everyone else — including all existing users — is unaffected. No way to switch yet, so
a genuinely-new solo user stays simplified until Phase 2. (Invited teammates are never flagged,
so they keep the full nav — see research.md §7.)
- **Phase 2:** a per-user localStorage override + a Classic mode switch in Settings →
Feature flags, so users can reveal the full navigation.

## The stable seam

One derived atom is the sole nav-simplify dependency the sidebar reads, so the sidebar edits are
written once (Phase 1) and never touched again. It lives in `state/onboarding/selectors.ts`
alongside the other onboarding-driven nav gates (`deadEndNavDisabledAtom`, `homeNavInertAtom`):

- **Phase 1:** `advancedNavHiddenAtom = navSimplifiedDefault` (thin passthrough over a fresh,
forward-only per-user key).
- **Phase 2:** `advancedNavHiddenAtom = override ?? navSimplifiedDefault` (add the override; same
name, same consumers).

**Why not reuse `isNewUserAtom`:** it is sticky-true for everyone who ever signed up (including
existing users), so deriving from it would strip the advanced nav from current users. Phase 1
instead introduces a new per-user key `agenta:onboarding:<userId>:nav-simplified`
(`navSimplifiedDefaultAtom`, default `false`) written only on signups going forward.

The sidebar reads `advancedNavHiddenAtom` with a bare `useAtomValue` (matching its sibling nav
gates); only the atom's body changes between phases.

---

# Phase 1 — Hide advanced nav for new signups

## Slice 0 — Pin the current sidebar with a test

1. Unit test over `useSidebarConfig` output (or `filterVisibleItems` on the built items)
asserting the five target keys are **present** when `isNavSimplified === false`.
2. Assert non-targets (Home, Agents, Observability, app Playground) are present in both modes.

**Exit:** a passing test capturing the pre-change sidebar (targets + non-targets visible).

## Slice 1 — The forward-only default + the derived seam atom

1. Add the durable per-user default in `web/oss/src/lib/onboarding/atoms.ts` (reuses the
existing per-user scoping infra — `onboardingStorageUserIdAtom`, `createScopedStorageKey`):

```ts
const navSimplifiedDefaultAtomFamily = atomFamily((userId: string) =>
atomWithStorage<boolean>(
createScopedStorageKey(userId, "nav-simplified"),
false,
),
);

export const navSimplifiedDefaultAtom = atom(
(get) => {
const userId = get(onboardingStorageUserIdAtom);
return userId ? get(navSimplifiedDefaultAtomFamily(userId)) : false;
},
(get, set, next: boolean) => {
const userId = get(onboardingStorageUserIdAtom);
if (userId) set(navSimplifiedDefaultAtomFamily(userId), next);
},
);
```

Export it from the `lib/onboarding` barrel.

2. Seed it at signup in `web/oss/src/hooks/usePostAuthRedirect.ts`: call
`setNavSimplifiedDefault(true)` next to each `setIsNewUser(true)` (EE and OSS non-invited
branches). Invited users `return` before this, so they stay on full nav.

3. Add the derived seam atom to `web/oss/src/state/onboarding/selectors.ts`, next to
`deadEndNavDisabledAtom` (imports `navSimplifiedDefaultAtom` from `@/oss/lib/onboarding/atoms`):

```ts
// Phase 1: follows the signup-era default. Phase 2 adds a user override here.
export const advancedNavHiddenAtom = atom((get) =>
get(navSimplifiedDefaultAtom),
);
```

No new module or wrapper hook — consumers read it with a bare `useAtomValue`, like the
sibling nav gates.

**Exit:** `advancedNavHiddenAtom` returns the new per-user default (not `isNewUser`); a signup
sets `nav-simplified` true; both editions compile.

## Slice 2 — Hide the two project-scope items

Edit `web/oss/src/components/Sidebar/hooks/useSidebarConfig/index.tsx`.

1. `const hideAdvancedNav = useAtomValue(advancedNavHiddenAtom)` near the top (import the atom
from the existing `@/oss/state/onboarding` barrel).
2. Prompts (`PROMPTS_SIDEBAR_KEY`, `:72`): add `isHidden: hideAdvancedNav`.
3. Evaluation group (`evaluation-group`, `:88`): add `isHidden: hideAdvancedNav`.
4. Add `hideAdvancedNav` to the `projectItems` `useMemo` deps.

**Exit:** with the simplified default true, `projectItems` (post `filterVisibleItems`) has no
Prompts and no `evaluation-group`; with it false, both present. Non-targets unchanged.

## Slice 3 — Hide the three app-scope items

Same file, `appItems` memo (`:144`).

1. Overview (`:147`), Registry (`:164`), Evaluations (`:174`): change `isHidden: isHidden` to
`isHidden: isHidden || hideAdvancedNav`. Do **not** touch Playground or app Observability.
2. Add `hideAdvancedNav` to the `appItems` `useMemo` deps.

**Exit:** with the simplified default true, `appItems` (post `filterVisibleItems`) has no
Overview, Registry, or Evaluations, and still has Playground + Observability when the app-context
gate allows. With it false, all five behave exactly as before.

## Slice 4 — Manual QA (Phase 1)

1. Run the local stack (OSS + dev per root `AGENTS.md`).
2. New user: set `agenta:onboarding:active-user-id` to the user id and
`agenta:onboarding:<id>:nav-simplified` to `true`, reload → simplified sidebar. Confirm the
five items gone, Home/Agents/Observability/Playground remain.
3. Set `nav-simplified` to `false` (or remove the key), reload → full sidebar returns.
4. Check both the main (project) and workflow (app) sidebars.
5. Confirm no empty section header / stray divider where the Evaluation group was
(`filterVisibleSections` drops empty sections — verify visually).

**Exit:** both states verified in the running app; `pnpm lint-fix` clean. **Phase 1 shippable.**

---

# Phase 2 — Settings → Feature flags control

Additive. Nothing from Phase 1 changes except the body of `advancedNavHiddenAtom`.

## Slice 5 — The override state

Add the override atom (near `navSimplifiedDefaultAtom`) and extend `advancedNavHiddenAtom` in
`web/oss/src/state/onboarding/selectors.ts`:

```ts
import { atomWithStorage } from "jotai/utils";

/** null = follow default, true = force simplified, false = force full. Per-user via LS. */
export const navSimplifiedOverrideAtom = atomWithStorage<boolean | null>(
"agenta:onboarding:<userId>:nav-simplified-override",
null,
);

// Phase 2: explicit choice wins; else fall back to the signup-era default.
export const advancedNavHiddenAtom = atom((get) => {
const override = get(navSimplifiedOverrideAtom);
return override ?? get(navSimplifiedDefaultAtom);
});
```

Unit test: override `null` → `navSimplifiedDefault`; override `true`/`false` → that value.

**Exit:** derived atom follows the table in `context.md`, proven by the test. Sidebar behavior
from Phase 1 is unchanged when no override is set.

## Slice 6 — The switch

1. Add a Feature flags settings page with an antd `Switch` labeled "Classic mode".
`checked` is the inverse of `advancedNavHiddenAtom`; `onChange` writes the corresponding
simplified-nav boolean to the per-user override.
2. Register the page as the `featureFlags` settings tab, available in both OSS and EE.

**Exit:** toggling shows/hides the advanced items live (no reload); the choice survives a
reload; both directions verified. Non-new users can now opt into simplified, and new users can
reveal everything. Classic mode changes navigation only; it does not restore older routing.

---

## Files touched

**Phase 1**

- `web/oss/src/lib/onboarding/atoms.ts` — new `navSimplifiedDefaultAtom` + family + storage key.
- `web/oss/src/lib/onboarding/index.ts` — export `navSimplifiedDefaultAtom` from the barrel.
- `web/oss/src/hooks/usePostAuthRedirect.ts` — seed the default at signup (two call sites).
- `web/oss/src/state/onboarding/selectors.ts` — new `advancedNavHiddenAtom` (passthrough seam),
next to `deadEndNavDisabledAtom`.
- `web/oss/src/components/Sidebar/hooks/useSidebarConfig/index.tsx` — five `isHidden` edits +
`useAtomValue(advancedNavHiddenAtom)` + two dep-array entries.

**Phase 2**

- `web/oss/src/lib/onboarding/atoms.ts` — add the per-user `navSimplifiedOverrideAtom`.
- `web/oss/src/state/onboarding/selectors.ts` — extend `advancedNavHiddenAtom`.
- `web/oss/src/components/pages/settings/FeatureFlags/FeatureFlags.tsx` — new switch.
- `web/oss/src/pages/w/[workspace_id]/p/[project_id]/settings/index.tsx` — register the page.

No changes to `engine/` or `scopes/`, or any EE file, in either phase.

## Rollback

Phase 1: revert `advancedNavHiddenAtom`, `navSimplifiedDefaultAtom` + signup seed, the sidebar
`useAtomValue` read, and the five `isHidden` edits — pure code revert, no data migration, no
server state. The harmless `agenta:onboarding:<userId>:nav-simplified` localStorage key can be
left. Phase 2: delete the switch + settings page and revert `advancedNavHiddenAtom` to the
passthrough; the harmless per-user override localStorage key can be left.
Loading
Loading