---
adr_number: 45
title: TUI Persona System
status_history:
- - '2026-03-10'
  - Proposed
  - Jeffrey Phillips Freeman
- - '2026-03-10'
  - Accepted
  - Jeffrey Phillips Freeman
tier: 4
authors:
- Jeffrey Phillips Freeman
superseded_by: null
related_adrs:
- number: 10
  title: Actor and Agent Architecture
  relationship: Personas reference actors defined by the actor architecture
- number: 17
  title: Automation Profiles
  relationship: Persona argument presets may override automation profile selection
- number: 20
  title: Session Model
  relationship: Each TUI session binds to a persona which configures the session's actor and scope
- number: 44
  title: TUI Architecture and Framework
  relationship: Personas are a TUI-specific concept within the TUI architecture
- number: 9
  title: Project Model
  relationship: Personas scope to projects defined by the project model
- number: 6
  title: Plan Lifecycle
  relationship: Personas scope to plans that follow the plan lifecycle
acceptance:
  votes_for:
  - voter: Jeffrey Phillips Freeman <Jeffrey.Freeman@CleverThis.com>
    comment: The persona abstraction elegantly bundles the TUI-specific combination of actor selection, argument configuration, project/plan scoping, and argument presets into a single switchable unit — solving the multi-project workflow ergonomics problem without polluting the domain model
  votes_against: []
  abstentions: []
---
## Context

In the CLI, each invocation specifies its configuration explicitly: `agents plan use --actor anthropic/claude-4 --project local/api-service --automation-profile trusted ...`. This is appropriate for stateless command execution but creates friction in the TUI, where users conduct long-running interactive sessions and frequently switch between different actor configurations, project scopes, and reasoning modes.

Consider a developer managing three projects simultaneously. In the CLI, they would run separate commands with different flags for each project. In the TUI, they need a way to quickly switch their entire working context — the actor, its configuration arguments, and the set of projects and plans the actor should focus on — without re-typing configuration every time.

Additionally, the CleverAgents system supports actors with configurable arguments (e.g., `thinking_effort`, `temperature`, `max_tokens`). Power users frequently want to toggle between reasoning modes (e.g., "quick answer" vs. "deep analysis") within the same session. This requires a mechanism to define named argument overlays that can be cycled through rapidly.

None of these concerns belong in the domain model: they are purely Presentation-layer ergonomics for the TUI. The domain layer knows about actors, sessions, projects, and plans — but not about the TUI-specific bundling of these into a switchable unit.

## Decision Drivers

- Users working with multiple projects need to switch their entire working context (actor, arguments, project scope, plan scope) as a single atomic operation, not as individual flag changes
- Argument presets (e.g., cycling through thinking effort levels) are a common workflow pattern that should be achievable with a single keypress, not by editing configuration
- The persona concept must remain a TUI-only abstraction — it must not leak into the domain model, A2A protocol, or database schema
- Default behavior must be sensible without any persona configuration: first-run users should get a working persona automatically based on their actor selection
- The persona system must integrate cleanly with the existing actor argument system — presets overlay arguments, they do not replace them
- Personas must be portable (exportable/importable) for team sharing and machine migration

## Decision

CleverAgents introduces the **Persona** concept as a TUI-only abstraction that bundles:

1. **Actor reference** — a namespaced actor name (e.g., `anthropic/claude-4-sonnet`)
2. **Base arguments** — the default arguments passed to the actor on every invocation
3. **Scoped projects** — a list of project references that are always included in the session's context
4. **Scoped plans** — a list of plan references that are always included in the session's context
5. **Argument presets** — named sets of argument overrides that can be cycled with `ctrl+tab`
6. **Display metadata** — a short name, optional accent color, and description

Personas are:

- **Stored locally** in `~/.config/cleveragents/personas/` as individual YAML files
- **Cyclable** via `tab` in the main chat screen, cycling through a user-configured ordered list
- **Session-bound** — each TUI session has a current persona; changing the persona mid-session updates the actor and scope for subsequent prompts (previous conversation history is retained)
- **Auto-generated** on first run from the actor selection, with thinking-level presets auto-detected from the actor's argument schema

## Design

### Persona Data Model

```yaml
# ~/.config/cleveragents/personas/feature-dev.yaml
name: "feature-dev"
description: "Feature development with Claude on main projects"
actor: "anthropic/claude-4-sonnet"
color: "$primary"                    # Textual CSS color token or hex

base_arguments:
  thinking_effort: "medium"
  temperature: 0.7
  max_tokens: 16384

scoped_projects:
  - "local/cleveragents"
  - "local/api-service"

scoped_plans: []                     # Usually empty; populated ad-hoc

argument_presets:
  - name: "default"                  # Always present, always first
    display: "default"               # Short label for PersonaBar
    overrides: {}                    # No overrides — uses base_arguments as-is

  - name: "think-more"
    display: "think: high"
    overrides:
      thinking_effort: "high"

  - name: "think-max"
    display: "think: max"
    overrides:
      thinking_effort: "max"
      temperature: 0.3               # Lower temperature for deep reasoning

  - name: "quick"
    display: "quick"
    overrides:
      thinking_effort: "low"
      max_tokens: 4096

cycle_order: 1                       # Position in tab-cycle list (1-indexed, 0 = not in cycle)
```

### Argument Preset Resolution

When the user selects a preset (via `ctrl+tab`), the effective arguments are computed as:

```
effective_arguments = merge(base_arguments, active_preset.overrides)
```

Where `merge` is a shallow dictionary merge — preset keys override base keys, base keys not in the preset are preserved. This is the same semantics as CLI flag overrides of YAML-configured defaults.

The "default" preset always has empty overrides (`{}`), meaning it produces exactly `base_arguments`. This ensures the user can always return to their configured baseline.

### Auto-Generated Presets

When a persona is created (either automatically on first run or manually via the persona editor), the system inspects the actor's argument schema to auto-generate useful presets:

1. **Thinking effort detection** — If the actor schema includes a `thinking_effort` argument (as all built-in LLM actors do), three presets are auto-generated:
   - `"default"` → `{}` (uses base `thinking_effort`)
   - `"think: high"` → `{ thinking_effort: "high" }`
   - `"think: max"` → `{ thinking_effort: "max" }`

2. **Temperature detection** — If the actor schema includes a `temperature` argument, presets for `"precise"` (`{ temperature: 0.1 }`) and `"creative"` (`{ temperature: 0.9 }`) are appended.

3. **No matching arguments** — If the actor has no recognized argument patterns, only the `"default"` preset is created.

The user can modify, delete, or add presets at any time via the PersonaEditorModal.

### Tab Cycling Behavior

The `tab` key in the main chat prompt cycles through personas in the user's configured cycle list:

```
tab:       persona_1 → persona_2 → persona_3 → persona_1 → ...
shift+tab: (used for sidebar cycling, NOT persona reverse-cycling)
```

The cycle list is the set of personas with `cycle_order > 0`, sorted by `cycle_order`. Personas with `cycle_order: 0` exist but are not part of the tab cycle — they can only be selected explicitly via `/persona:set <name>` or through the fullscreen sidebar.

When cycling, the PersonaBar updates immediately to show the new persona's name, actor, and current preset. The actor binding for the session is updated — subsequent prompts use the new persona's actor and scope. Previous conversation messages remain visible (they are part of the session history, not the persona).

### Ctrl+Tab Preset Cycling

`ctrl+tab` cycles through the current persona's argument presets:

```
ctrl+tab: default → think: high → think: max → quick → default → ...
```

The active preset's `display` name appears in the PersonaBar. The effective arguments change immediately for the next prompt.

### Persona Lifecycle

```
┌───────────┐     ┌──────────────┐     ┌───────────────┐     ┌──────────┐
│ First Run │────►│ Auto-create  │────►│ Active in     │────►│ Modified │
│ (no       │     │ "default"    │     │ session       │     │ by user  │
│  personas)│     │ persona from │     │               │     │          │
└───────────┘     │ actor select │     └───────────────┘     └──────────┘
                  └──────────────┘            ▲                     │
                                              │                     │
                                              └─── Saved to YAML ───┘
```

1. **First run**: No personas exist. Actor selection overlay creates a `"default"` persona with auto-generated presets.
2. **Normal startup**: Last active persona is restored from `~/.config/cleveragents/tui-state.yaml`.
3. **Manual creation**: User creates personas via PersonaEditorModal (from fullscreen sidebar or `/persona:create` command). The editor provides:
   - Actor selection (searchable list from Actor Registry)
   - Base argument editor (form fields derived from actor's argument schema)
   - Project/plan scope selection (checkboxes from Project/Plan registries)
   - Preset editor (add/remove/edit named argument overlays)
   - Cycle order assignment
4. **Modification**: User edits an existing persona; changes are saved to the YAML file immediately.
5. **Deletion**: User deletes a persona; the YAML file is removed. If the deleted persona was active, the next persona in the cycle list becomes active (or `"default"` if none remain).

### Scope Behavior

When a persona has scoped projects or plans:

1. **Every prompt** submitted in a session using that persona implicitly includes those project/plan references in the context. This is equivalent to the user typing `@project:<name>` for each scoped project in every prompt.
2. **The `@` reference system** can add additional projects/plans for a single prompt — these are additive to the persona's scope.
3. **The sidebar** highlights scoped projects/plans with a distinct indicator (e.g., a pin icon or accent color) to distinguish them from unscoped items.
4. **Context assembly** (ACMS) receives the combined scope (persona scope + prompt @ references) and prioritizes those resources in the CRP for the resulting plan.

### PersonaBar Display

The PersonaBar sits directly below the prompt input and is always visible:

```
┌─────────────────────────────────────────────────────────────────────┐
│ ❯ What would you like to do?                                        │
│   ▌@▐ refs  ▌/▐ commands  ▌!▐ shell                                 │
└─────────────────────────────────────────────────────────────────────┘
  feature-dev │ anthropic/claude-4-sonnet │ think: high │ 2 projects │ $0.12
```

| Segment | Content | Style | Updates When |
|---------|---------|-------|-------------|
| Persona name | `feature-dev` | `$text-primary` on `$primary 10%` background | `tab` cycle |
| Actor name | `anthropic/claude-4-sonnet` | `$text-secondary` | `tab` cycle |
| Preset label | `think: high` | `$text-warning` (differs from default), `$text-muted` (default) | `ctrl+tab` cycle |
| Scope indicator | `2 projects` / `1 plan, 3 projects` | `$text-muted` | Persona change or `/scope:add/remove` |
| Session cost | `$0.12` | `$secondary 70%`, right-aligned | After each actor response |

### Persona Storage Format

Personas are stored as individual YAML files in `~/.config/cleveragents/personas/`:

```
~/.config/cleveragents/
├── config.toml              # Global config (existing)
├── tui-state.yaml           # TUI state (last persona, window sizes, etc.)
└── personas/
    ├── default.yaml
    ├── feature-dev.yaml
    ├── reviewer.yaml
    └── infra-admin.yaml
```

The TUI state file tracks transient state:

```yaml
# ~/.config/cleveragents/tui-state.yaml
last_persona: "feature-dev"
last_preset: "think: high"
last_session_id: "01HXM8C2..."
sidebar_state: "visible"            # hidden | visible | fullscreen
theme_override: null                # null = use config.toml setting
window_width: 180
window_height: 50
```

### Persona Export and Import

Personas can be shared between users or machines:

```
/persona:export feature-dev          # Writes feature-dev.yaml to current directory
/persona:import ./feature-dev.yaml   # Copies to personas/ directory
```

Exported files are the same YAML format as stored files — no transformation needed. This enables teams to share persona configurations via version control or messaging.

## Constraints

- Personas must never appear in the domain model, A2A protocol, database schema, or any non-TUI code path — they are a pure Presentation-layer concept
- Every persona must have exactly one `"default"` argument preset with empty overrides; this preset cannot be deleted or renamed
- The `cycle_order` field must be unique across all personas with `cycle_order > 0`; validation occurs on save
- Actor references in personas must resolve to registered actors at session start; if an actor is unregistered, the persona is marked as invalid with a visible warning
- Base arguments must conform to the referenced actor's argument schema; invalid arguments are rejected at persona save time with a validation error
- Scoped projects and plans must reference existing entities at session start; stale references produce warnings (not errors) since projects/plans may be created or removed between TUI sessions

## Consequences

### Positive

- Users can switch their entire working context (actor, arguments, project scope) with a single `tab` keypress, dramatically reducing friction in multi-project workflows
- Argument presets provide instant access to different reasoning modes (quick vs. deep thinking) without editing configuration — a single `ctrl+tab` changes the actor's behavior
- Auto-generated presets mean first-run users get useful preset cycling without any configuration — the system discovers the actor's capabilities and creates sensible defaults
- Persona portability (YAML export/import) enables team standardization — a lead developer can define personas for common workflows and share them with the team
- The strict separation from the domain model means personas can evolve freely without database migrations, A2A version bumps, or backward compatibility concerns

### Negative

- The persona concept introduces a TUI-specific abstraction that has no CLI equivalent — CLI users must specify actor, arguments, and project scope explicitly on each command (this is by design, but creates a feature gap between interfaces)
- Auto-generated presets may not match user expectations for all actor types — custom actors with unusual argument schemas may produce unhelpful or no auto-generated presets
- Persona state is local to the machine; users on multiple machines must manually sync persona files (mitigated by export/import)

### Risks

- If the actor argument schema changes (e.g., a new version of an actor adds or removes arguments), existing persona base_arguments and preset overrides may become invalid — the system should validate on load and warn rather than fail
- The `cycle_order` uniqueness constraint may frustrate users who want to reorder personas frequently — consider a drag-and-drop interface in the fullscreen sidebar to simplify reordering
- Stale project/plan references in scoped_projects/scoped_plans could silently degrade context quality if the user doesn't notice the warning — consider making stale references more prominent (e.g., PersonaBar turns warning color)

## Alternatives Considered

**Store personas in the database via A2A** — This would make personas available across CLI, TUI, and Web. Rejected because it adds domain complexity for a Presentation-layer concept. Different frontends have different ergonomic needs; the CLI's stateless model doesn't benefit from personas. If cross-frontend persona sharing is needed later, it can be added as a new A2A operation without changing the persona model.

**Use environment variables or shell aliases instead of personas** — Users could define shell aliases like `alias ca-dev="agents --actor anthropic/claude-4 --project local/api-service"`. Rejected because this doesn't work within a TUI session (can't change the alias mid-session), doesn't support argument presets, and provides no discoverability or management UI.

**Actor profiles (actor-side configuration)** — Define different configurations as separate actors (e.g., `local/claude-quick`, `local/claude-deep`). Rejected because this pollutes the actor registry with configuration variants rather than distinct actors, and doesn't address project scoping.

**Session-level configuration only** — Allow changing actor and scope per-session via commands but without the persona bundling. Rejected because it requires multiple commands to switch context (change actor, change scope, change arguments) rather than a single tab press.

## Compliance

- **Unit tests**: Persona YAML serialization/deserialization round-trip tests for all fields
- **Validation tests**: Verify that invalid argument schemas, missing actors, duplicate cycle orders, and missing default presets are caught at save time
- **Integration tests**: Pilot tests verify that `tab` cycling updates the PersonaBar and subsequent prompts use the new persona's actor
- **Preset tests**: Verify that `ctrl+tab` cycling computes effective arguments correctly (merge semantics)
- **Auto-generation tests**: Verify that actors with `thinking_effort` arguments produce the expected presets, and actors without recognized arguments produce only the default preset
- **State persistence tests**: Verify that `tui-state.yaml` correctly saves and restores last persona, preset, and sidebar state across TUI restarts
- **Export/import tests**: Verify that exported YAML files are valid and importable on a different machine (no absolute paths or machine-specific references)
