Compare commits

...

1 Commits

Author SHA1 Message Date
freemo c2b18f3689 test(e2e): workflow example 9 — session-driven interactive exploration (review profile)
CI / benchmark-publish (pull_request) Has been skipped
CI / build (pull_request) Successful in 23s
CI / lint (pull_request) Successful in 32s
CI / helm (pull_request) Successful in 42s
CI / typecheck (pull_request) Successful in 4m0s
CI / quality (pull_request) Successful in 4m4s
CI / security (pull_request) Successful in 4m11s
CI / integration_tests (pull_request) Successful in 6m8s
CI / unit_tests (pull_request) Successful in 9m41s
CI / docker (pull_request) Successful in 14s
CI / coverage (pull_request) Successful in 11m55s
CI / e2e_tests (pull_request) Successful in 20m42s
CI / status-check (pull_request) Successful in 1s
CI / benchmark-regression (pull_request) Successful in 1h4m59s
Rewrite robot/e2e/wf09_session.robot — E2E test for Workflow Example 9
(Session-Driven Interactive Exploration).

The test exercises the full session lifecycle with zero mocking:
- Creates a temp git repo with a sample multi-module Python project
  (src/auth.py, src/routes.py, src/models.py, README.md) using unique
  names with UUID suffix for parallel CI safety
- Registers the repo as a resource and creates a project
- Creates a session via session create with a dynamically selected actor
- Sends three conversational queries via session tell: codebase overview,
  auth module deep-dive, and action creation request (per spec Example 9)
- Verifies accumulated session history via session show
- Exports the conversation to JSON via session export
- Validates the exported JSON structure with hard assertions (session_id,
  messages keys)

Uses Skip If No LLM Keys for graceful degradation when API keys are
absent.  Extracts reusable keywords (Create Sample Project Fixture,
Create Session With Actor, Verify Session Tell Response,
Verify Session History, Verify Session Export) to reduce test case
size and improve maintainability.  Uses Safe Parse Json Field from
common_e2e.resource instead of custom JSON parsing.  Removes redundant
library imports already provided by common_e2e.resource.

ISSUES CLOSED: #755
2026-03-30 11:08:48 +00:00
3 changed files with 378 additions and 0 deletions
+3
View File
@@ -167,3 +167,6 @@ PIPE
# Git worktrees for parallel task branches
worktrees/
# Copy-on-write sandbox backup artifacts
ca-cow-backup-*/
+10
View File
@@ -854,6 +854,16 @@ attribute 'db'` after `agents init`. Root cause: `_get_session_service()`
session subcommands. Includes Behave BDD regression scenarios, Robot
Framework integration smoke tests, and structlog isolation for parallel
test execution. (#554, #570, #680)
- Added E2E test for Workflow Example 9 — session-driven interactive
exploration. `robot/e2e/wf09_session.robot` exercises the full session
lifecycle: creates a temp git repo with a sample multi-module Python project,
registers it as a resource/project, creates a session with an actor via
`session create`, sends conversational queries via `session tell` (including
an action-creation request per the spec), verifies accumulated history via
`session show`, exports the conversation to JSON via `session export`, and
validates the exported JSON structure (session_id, messages array).
Uses `Force Tags E2E`, `common_e2e.resource`, and skips gracefully
when LLM API keys are absent via `Skip If No LLM Keys`. (#755)
- Added Robot Framework E2E acceptance test for M1 (v3.0.0) milestone.
Tests the complete plan lifecycle (action create → resource add → project
create → plan use → plan execute strategize → plan execute → plan diff →
+365
View File
@@ -0,0 +1,365 @@
*** Settings ***
Documentation E2E Workflow Example 9 — Session-Driven Interactive Exploration.
...
... A new developer uses sessions to interactively explore an
... unfamiliar codebase: creates a session, sends conversational
... queries via ``session tell``, asks the AI to create an action
... from the conversation, reviews accumulated history via
... ``session show``, and exports the conversation to JSON via
... ``session export``.
...
... **Zero mocking** — exercises the real CleverAgents CLI with
... real LLM API keys. LLM-dependent steps use
... ``Skip If No LLM Keys`` for graceful degradation in
... environments without API keys.
...
... **Note:** ``session tell`` is currently stubbed at the CLI
... layer (M3 milestone) — the assistant echoes an
... acknowledgement rather than invoking a real LLM. AC #5
... (action creation) therefore always takes the WARN path.
... Assertions validate the echo response pattern until real
... LLM integration lands.
Library Collections
Resource common_e2e.resource
Suite Setup WF09 Suite Setup
Suite Teardown E2E Suite Teardown
Force Tags E2E
*** Variables ***
${AUTH_PY} SEPARATOR=\n
... """Simple authentication module."""
...
...
... def authenticate(username: str, password: str) -> bool:
... """Authenticate a user with username and password."""
... # Placeholder implementation
... return username == "test-user" and password == "test-password"
...
...
... def create_token(user_id: str) -> str:
... """Create a JWT token for authenticated user."""
... return f"token-{user_id}"
...
...
... def verify_token(token: str) -> bool:
... """Verify a JWT token is valid."""
... return token.startswith("token-")
${ROUTES_PY} SEPARATOR=\n
... """HTTP route definitions."""
...
... from auth import authenticate, verify_token
...
...
... ROUTES = {
... "/login": "handle_login",
... "/dashboard": "handle_dashboard",
... "/api/users": "handle_users",
... }
...
...
... def handle_login(request):
... """Handle login requests."""
... return {"status": "ok"}
...
...
... def handle_dashboard(request):
... """Handle dashboard requests."""
... if not verify_token(request.get("token", "")):
... return {"status": "unauthorized"}
... return {"status": "ok", "data": "dashboard"}
${MODELS_PY} SEPARATOR=\n
... """Data models for the application."""
...
...
... class User:
... """User model with basic fields."""
...
... def __init__(self, user_id: str, username: str, email: str):
... self.user_id = user_id
... self.username = username
... self.email = email
...
... def to_dict(self):
... return {"id": self.user_id, "username": self.username, "email": self.email}
...
...
... class Session:
... """Session model for tracking user sessions."""
...
... def __init__(self, session_id: str, user: User):
... self.session_id = session_id
... self.user = user
${README_CONTENT} SEPARATOR=\n
... # Sample Web Application
...
... A simple web application with authentication, routing, and data models.
...
... ## Modules
...
... - **auth** — authentication and token management
... - **routes** — HTTP route definitions and handlers
... - **models** — data models (User, Session)
...
... ## Getting Started
...
... Install dependencies and run the application.
*** Keywords ***
WF09 Suite Setup
[Documentation] E2E Suite Setup plus unique suffix generation and actor selection.
E2E Suite Setup
# Initialise the database so session/config commands work in all tests.
${init}= Run CleverAgents Command init --force --yes
Should Be Equal As Integers ${init.rc} 0
Should Not Contain ${init.stdout}${init.stderr} Traceback
Should Not Contain ${init.stdout}${init.stderr} INTERNAL
# Generate a unique suffix for resource/project names to avoid UNIQUE
# constraint collisions on repeated or parallel CI runs.
${suffix}= Evaluate __import__('uuid').uuid4().hex[:12]
Set Suite Variable ${RUN_SUFFIX} ${suffix}
# Pick an actor that matches the available API key.
# Prefer Anthropic for session-based exploration: Claude models tend to
# produce richer conversational replies that improve session-history
# assertions. WF04/WF05 prefer OpenAI to balance provider coverage.
# Unlike WF04/WF05 which check both providers, WF09 only checks
# Anthropic and falls through to OpenAI unconditionally — this is
# intentional: ``session tell`` is currently stubbed (M3), so the
# actor choice affects only the echo prefix, not real LLM behaviour.
${has_anthropic}= Evaluate bool(__import__('os').environ.get('ANTHROPIC_API_KEY', ''))
IF ${has_anthropic}
${actor}= Set Variable anthropic/claude-sonnet-4-20250514
ELSE
${actor}= Set Variable openai/gpt-4o
END
Set Suite Variable ${LLM_ACTOR} ${actor}
Create Sample Project Fixture
[Documentation] Create a temp git repo with a multi-module Python project,
... register it as a resource and create a project. Returns
... the project name.
${repo}= Create Temp Git Repo wf09-explore-${RUN_SUFFIX}
Create File ${repo}${/}src${/}auth.py ${AUTH_PY}
Create File ${repo}${/}src${/}routes.py ${ROUTES_PY}
Create File ${repo}${/}src${/}models.py ${MODELS_PY}
Create File ${repo}${/}README.md ${README_CONTENT}
${git_add}= Run Process git add . cwd=${repo} timeout=30s on_timeout=kill
Should Be Equal As Integers ${git_add.rc} 0 git add failed: ${git_add.stderr}
${git_commit}= Run Process git commit -m Add sample project files cwd=${repo} timeout=30s on_timeout=kill
Should Be Equal As Integers ${git_commit.rc} 0 git commit failed: ${git_commit.stderr}
# Register repo as a resource
${res_name}= Set Variable wf09-res-${RUN_SUFFIX}
${res_result}= Run CleverAgents Command
... resource add git-checkout ${res_name} --path ${repo}
Should Be Equal As Integers ${res_result.rc} 0
Should Not Contain ${res_result.stdout}${res_result.stderr} Traceback
Should Not Contain ${res_result.stdout}${res_result.stderr} INTERNAL
Output Should Contain ${res_result} ${res_name}
# Create a project linked to the resource
${proj_name}= Set Variable wf09-proj-${RUN_SUFFIX}
${proj_result}= Run CleverAgents Command
... project create --resource ${res_name} ${proj_name}
Should Be Equal As Integers ${proj_result.rc} 0
Should Not Contain ${proj_result.stdout}${proj_result.stderr} Traceback
Should Not Contain ${proj_result.stdout}${proj_result.stderr} INTERNAL
Output Should Contain ${proj_result} ${proj_name}
RETURN ${proj_name}
Create Session With Actor
[Documentation] Create a session with an LLM actor via ``session create``.
... Returns the session ID.
...
... Note: ``session create`` does not accept a ``--project``
... parameter. The session is actor-scoped; the project
... fixture is registered separately so the LLM can discover
... it via resource/project queries during the conversation.
${create_result}= Run CleverAgents Command
... session create --actor ${LLM_ACTOR} --format json
Should Not Be Empty ${create_result.stdout}
... msg=session create produced no output
Should Not Contain ${create_result.stdout}${create_result.stderr} Traceback
Should Not Contain ${create_result.stdout}${create_result.stderr} INTERNAL
Output Should Contain ${create_result} session_id
${session_id}= Safe Parse Json Field ${create_result.stdout} session_id
Should Not Be Empty ${session_id}
... msg=Could not extract session_id from session create output
RETURN ${session_id}
Verify Session Tell Response
[Documentation] Send a message to the session via ``session tell`` and verify
... that it produces a non-empty response with content relevance.
...
... ``session tell`` is currently stubbed (M3): the assistant
... echoes ``Acknowledged: <prompt>``. The content check
... validates this echo pattern. Update when real LLM
... integration lands.
[Arguments] ${session_id} ${message} ${timeout}=120s
${tell_result}= Run CleverAgents Command
... session tell --session ${session_id} ${message}
... timeout=${timeout}
Should Not Be Empty ${tell_result.stdout}
... msg=session tell produced no response for: ${message}
Should Not Contain ${tell_result.stdout}${tell_result.stderr} Traceback
Should Not Contain ${tell_result.stdout}${tell_result.stderr} INTERNAL
# Content relevance: stubbed session tell echoes "Acknowledged: <prompt>".
Output Should Contain ${tell_result} Acknowledged
RETURN ${tell_result}
Verify Session History
[Documentation] Verify that ``session show`` returns output containing
... the session ID and evidence of accumulated messages.
... Parses JSON output and asserts ``message_count >= 6``
... (3 user turns + 3 assistant responses) and that
... ``recent_messages`` is populated.
[Arguments] ${session_id}
${show_result}= Run CleverAgents Command
... session show ${session_id} --format json
Should Not Contain ${show_result.stdout}${show_result.stderr} Traceback
Should Not Contain ${show_result.stdout}${show_result.stderr} INTERNAL
Output Should Contain ${show_result} ${session_id}
# Parse JSON and verify message accumulation (AC #4).
# ``session show --format json`` exposes ``message_count`` (total) and
# ``recent_messages`` (last 5). Use Extract JSON From Stdout (raw_decode)
# to handle potential non-JSON preamble from the CLI.
${show_json}= Extract JSON From Stdout ${show_result.stdout}
${msg_count}= Evaluate int($show_json.get('message_count', 0))
Should Be True ${msg_count} >= 6
... msg=Expected message_count >= 6 (3 user turns + 3 assistant replies), found ${msg_count}
# Also verify recent_messages is populated (proves messages are persisted).
# After 3 user + 3 assistant turns the recent window (last 5) should
# contain at least 3 entries.
${recent}= Evaluate $show_json.get('recent_messages', [])
${recent_count}= Evaluate len($recent) if isinstance($recent, list) else 0
Should Be True ${recent_count} >= 3
... msg=Expected at least 3 recent_messages after 3 turns, found ${recent_count}
RETURN ${show_result}
Verify Session Export
[Documentation] Export the session to JSON and validate the exported file
... has the expected structure (session_id matching the input,
... non-empty messages list with valid message objects).
[Arguments] ${session_id}
${export_path}= Set Variable ${SUITE_HOME}${/}wf09_export.json
# Remove any stale export from a previous partial run to avoid false passes.
Remove File ${export_path}
# Spec canonical form: flag before positional arg.
${export_result}= Run CleverAgents Command
... session export --output ${export_path} ${session_id}
Should Not Contain ${export_result.stdout}${export_result.stderr} Traceback
Should Not Contain ${export_result.stdout}${export_result.stderr} INTERNAL
File Should Exist ${export_path} msg=session export did not create file
${export_content}= Get File ${export_path}
Should Not Be Empty ${export_content} msg=Export file is empty
TRY
${export_json}= Evaluate json.loads($export_content) json
EXCEPT AS ${err}
Fail session export produced invalid JSON: ${err}
END
Dictionary Should Contain Key ${export_json} session_id
Dictionary Should Contain Key ${export_json} messages
# Verify exported session_id matches the actual session.
${exported_sid}= Evaluate str($export_json.get('session_id', ''))
Should Be Equal As Strings ${exported_sid} ${session_id}
... msg=Exported session_id does not match the actual session
# Verify messages list is non-empty (not just structurally present).
${msg_list}= Evaluate $export_json.get('messages', [])
Should Not Be Empty ${msg_list} msg=Exported messages list should not be empty
# Spot-check first message has the expected structure (role field).
${first_msg}= Evaluate $msg_list[0] if $msg_list else {}
Dictionary Should Contain Key ${first_msg} role
... msg=Each exported message should have a 'role' field
WF09 Test Teardown
[Documentation] Log diagnostic context on failure for debugging.
... Captures session state so CI failures in this LLM-dependent
... test have actionable data.
${session_id}= Get Variable Value ${WF09_SESSION_ID} ${EMPTY}
IF '${session_id}' != ''
${status} ${result}= Run Keyword And Ignore Error
... Run CleverAgents Command session show ${session_id} --format json expected_rc=None timeout=30s
IF '${status}' == 'PASS'
Log Teardown session show: ${result.stdout} WARN
END
END
*** Test Cases ***
Workflow 09 — Session-Driven Interactive Exploration
[Documentation] End-to-end workflow: create a session, explore a codebase
... interactively via session tell, request action creation,
... review history, and export.
[Timeout] 20 minutes
[Teardown] WF09 Test Teardown
Skip If No LLM Keys
# Initialise test variable for teardown access.
Set Test Variable ${WF09_SESSION_ID} ${EMPTY}
# ------------------------------------------------------------------
# Step 1: Create a temp git repo with a sample project fixture
# ------------------------------------------------------------------
# proj_name exists for LLM discovery; session create is actor-scoped,
# not project-scoped, so the variable is not referenced again directly.
${proj_name}= Create Sample Project Fixture
# ------------------------------------------------------------------
# Step 2: Create a session with an actor
# ------------------------------------------------------------------
${session_id}= Create Session With Actor
Set Test Variable ${WF09_SESSION_ID} ${session_id}
# ------------------------------------------------------------------
# Step 3: First conversational query — ask about modules
# ``session tell`` is currently stubbed (M3) — echoes an
# acknowledgement rather than calling a real LLM.
# ------------------------------------------------------------------
${tell1_result}= Verify Session Tell Response ${session_id}
... What are the main modules in this project?
# ------------------------------------------------------------------
# Step 4: Second conversational query — ask about auth module
# ------------------------------------------------------------------
${tell2_result}= Verify Session Tell Response ${session_id}
... Can you explain how the auth module works?
# ------------------------------------------------------------------
# Step 5: Third conversational query — request action creation
# (per spec Example 9, Step 2, third session tell interaction)
# Use 300s timeout: action creation involves LLM tool-calling which
# is significantly slower than simple conversational replies. This
# timeout is forward-compatible for when real LLM integration lands;
# while ``session tell`` remains stubbed the command returns
# instantly so the timeout has no practical effect.
# ------------------------------------------------------------------
${tell3_result}= Verify Session Tell Response ${session_id}
... Create an action called local/wf09-refresh-${RUN_SUFFIX} that automates dependency lock refresh
... timeout=300s
# ------------------------------------------------------------------
# Step 5b: Verify action creation (AC #5)
# The LLM was asked to create an action; verify it exists. Since LLM
# behavior is non-deterministic, use a soft check: warn if the action
# was not created rather than hard-failing.
# ------------------------------------------------------------------
${action_check}= Run CleverAgents Command
... action show local/wf09-refresh-${RUN_SUFFIX} --format json
... expected_rc=None
IF ${action_check.rc} != 0
Log AC #5 note: LLM did not create action local/wf09-refresh-${RUN_SUFFIX} in this run WARN
ELSE
Should Not Contain ${action_check.stdout}${action_check.stderr} Traceback
Should Not Contain ${action_check.stdout}${action_check.stderr} INTERNAL
Output Should Contain ${action_check} wf09-refresh
END
# ------------------------------------------------------------------
# Step 6: Verify session has accumulated history
# ------------------------------------------------------------------
${show_result}= Verify Session History ${session_id}
# ------------------------------------------------------------------
# Step 7: Export session to JSON and validate structure
# ------------------------------------------------------------------
Verify Session Export ${session_id}
Log Workflow 09 completed: session ${session_id}