Feature: TUI first-run experience with actor selection overlay The TUI shows an actor selection overlay on first launch when no personas are configured. After the user selects an actor a default persona is created and the overlay is dismissed. # ----------------------------------------------------------------------- # is_first_run helper # ----------------------------------------------------------------------- Scenario: is_first_run returns True when registry has no personas Given an empty persona registry Then is_first_run should return True Scenario: is_first_run returns False when registry has at least one persona Given a persona registry with a default persona using actor "local/mock" Then is_first_run should return False # ----------------------------------------------------------------------- # create_default_persona_for_actor helper # ----------------------------------------------------------------------- Scenario: create_default_persona_for_actor persists a default persona Given an empty persona registry When I call create_default_persona_for_actor with actor "anthropic/claude-4-sonnet" Then the registry should contain a persona named "default" And the default persona actor should be "anthropic/claude-4-sonnet" Scenario: create_default_persona_for_actor sets last persona Given an empty persona registry When I call create_default_persona_for_actor with actor "openai/gpt-4o" Then the registry last persona should be "default" # ----------------------------------------------------------------------- # render_actor_selection helper # ----------------------------------------------------------------------- Scenario: render_actor_selection includes welcome header When I render the actor selection with default actors Then the rendered text should contain "Welcome to CleverAgents" Scenario: render_actor_selection marks the first actor as recommended When I render the actor selection with default actors Then the rendered text should contain "(recommended)" Scenario: render_actor_selection highlights the selected actor with cursor When I render the actor selection with selected index 2 Then the rendered text should contain a cursor marker Scenario: render_actor_selection shows search prompt when query is empty When I render the actor selection with default actors Then the rendered text should contain "to search..." Scenario: render_actor_selection shows active search query When I render the actor selection with search query "claude" Then the rendered text should contain "claude" Scenario: render_actor_selection includes footer keybindings When I render the actor selection with default actors Then the rendered text should contain "enter Select" And the rendered text should contain "j/k Navigate" And the rendered text should contain "/ Search" # ----------------------------------------------------------------------- # ActorSelectionOverlay widget # ----------------------------------------------------------------------- Scenario: ActorSelectionOverlay starts hidden Given a new ActorSelectionOverlay Then the overlay should not be visible Scenario: ActorSelectionOverlay show makes it visible Given a new ActorSelectionOverlay When I call show on the overlay Then the overlay should be visible Scenario: ActorSelectionOverlay hide makes it invisible Given a new ActorSelectionOverlay When I call show on the overlay And I call hide on the overlay Then the overlay should not be visible Scenario: ActorSelectionOverlay show populates default actors Given a new ActorSelectionOverlay When I call show on the overlay Then the overlay actors list should contain "anthropic/claude-sonnet-4-20250514" Scenario: ActorSelectionOverlay show accepts custom actor list Given a new ActorSelectionOverlay When I call show on the overlay with actors ["local/mock-a", "local/mock-b"] Then the overlay actors list should contain "local/mock-a" And the overlay actors list should contain "local/mock-b" Scenario: ActorSelectionOverlay move_down advances selection Given a new ActorSelectionOverlay When I call show on the overlay And I call move_down on the overlay Then the overlay selected index should be 1 Scenario: ActorSelectionOverlay move_up wraps to last item Given a new ActorSelectionOverlay When I call show on the overlay And I call move_up on the overlay Then the overlay selected index should be the last index Scenario: ActorSelectionOverlay move_down wraps to first item Given a new ActorSelectionOverlay When I call show on the overlay And I move_down past the last actor Then the overlay selected index should be 0 Scenario: ActorSelectionOverlay set_search filters actors Given a new ActorSelectionOverlay When I call show on the overlay And I call set_search with query "claude" Then the overlay filtered actors should only contain actors matching "claude" Scenario: ActorSelectionOverlay set_search with empty string clears filter Given a new ActorSelectionOverlay When I call show on the overlay And I call set_search with query "claude" And I call set_search with query "" Then the overlay filtered actors count should equal the full actors count Scenario: ActorSelectionOverlay confirm returns selected actor Given a new ActorSelectionOverlay When I call show on the overlay And I call confirm on the overlay Then the confirmed actor should be "anthropic/claude-sonnet-4-20250514" Scenario: ActorSelectionOverlay confirm hides the overlay Given a new ActorSelectionOverlay When I call show on the overlay And I call confirm on the overlay Then the overlay should not be visible Scenario: ActorSelectionOverlay confirm sets confirmed flag Given a new ActorSelectionOverlay When I call show on the overlay And I call confirm on the overlay Then the overlay confirmed flag should be True Scenario: ActorSelectionOverlay confirm on empty filtered list returns None Given a new ActorSelectionOverlay When I call show on the overlay And I call set_search with query "zzz_no_match" And I call confirm on the overlay Then the confirmed actor should be None Scenario: ActorSelectionOverlay move_down on empty list is a no-op Given a new ActorSelectionOverlay When I call show on the overlay And I call set_search with query "zzz_no_match" And I call move_down on the overlay Then the overlay selected index should be 0 Scenario: ActorSelectionOverlay move_up on empty list is a no-op Given a new ActorSelectionOverlay When I call show on the overlay And I call set_search with query "zzz_no_match" And I call move_up on the overlay Then the overlay selected index should be 0 Scenario: ActorSelectionOverlay selected_actor is None before confirm Given a new ActorSelectionOverlay When I call show on the overlay Then the overlay selected_actor should be None Scenario: ActorSelectionOverlay search_query is empty after show Given a new ActorSelectionOverlay When I call show on the overlay Then the overlay search_query should be "" # ----------------------------------------------------------------------- # TUI app integration: on_mount shows overlay on first run # ----------------------------------------------------------------------- Scenario: TUI app shows actor selection overlay on first run Given the TUI app is initialised with an empty persona registry When I call on_mount on the first-run app Then the actor selection overlay should be visible Scenario: TUI app hides actor selection overlay when personas exist Given the TUI app is initialised with an existing persona registry When I call on_mount on the first-run app Then the actor selection overlay should not be visible # ----------------------------------------------------------------------- # TUI app integration: _complete_first_run # ----------------------------------------------------------------------- Scenario: _complete_first_run creates default persona and refreshes bar Given the TUI app is initialised with an empty persona registry When I call on_mount on the first-run app And I call _complete_first_run with actor "anthropic/claude-sonnet-4-20250514" Then the registry should contain a persona named "default" And the persona bar should reflect the new actor # ----------------------------------------------------------------------- # TDD regression: issue #11039 - no _render() shadowing Textual Widget._render # ----------------------------------------------------------------------- # The overlay formerly defined its own _render() returning None, which # shadowed textual.widgets.Static._render and caused layout-engine crashes. # This scenario ensures the rename to _refresh_display eliminated that bug. @tdd_issue @tdd_issue_11039 Scenario: ActorSelectionOverlay does not define its own _render method Given a new ActorSelectionOverlay Then the overlay should not override Textual Widget._render And the inherited Widget._render should return a proper renderable @tdd_issue @tdd_issue_11039 Scenario: ActorSelectionOverlay has refresh_display callable instead of render Given a new ActorSelectionOverlay When I call show on the overlay Then the overlay _refresh_display method should be callable And _refresh_display should produce correct overlay content