forked from HAL9000/cleveragents-core
a321fb3b37
- What was implemented
- Added PLAN_ROLLED_BACK event type to the EventType enum at src/cleveragents/infrastructure/events/types.py to properly represent successful rollbacks in the domain model.
- Implemented rollback_plan(plan_id: str, checkpoint_id: str) -> RollbackResult in PlanLifecycleService (src/cleveragents/application/services/plan_lifecycle_service.py) with:
- Plan state validation: rejects rollback when the plan is in terminal APPLIED or CANCELLED states.
- Delegation to CheckpointService.selective_rollback() to perform the actual rollback logic and obtain a RollbackResult.
- Emission of PLAN_ROLLED_BACK as a domain event to reflect the completed rollback.
- checkpoint_service is accepted as an optional constructor parameter; if not provided, a PlanError is raised to preserve backward compatibility.
- Updated CLI behavior in src/cleveragents/cli/commands/plan.py so agents plan rollback routes through PlanLifecycleService.rollback_plan() rather than calling CheckpointService.selective_rollback() directly.
- Updated PlanLifecycleService module docstring to include rollback_plan in the documented API.
- Added Behave feature file features/plan_lifecycle_rollback.feature with 11 scenarios covering state validation, domain events, and delegation.
- Added step implementations in features/steps/plan_lifecycle_rollback_steps.py to support the new scenarios.
- Key design decisions
- rollback_plan returns RollbackResult (the same result type produced by CheckpointService.selective_rollback) so the CLI can display rollback details consistently.
- Terminal states APPLIED and CANCELLED are disallowed for rollback to prevent inconsistent or invalid state transitions.
- checkpoint_service is optional in the PlanLifecycleService constructor; when omitted (None), a PlanError is raised to retain backward compatibility while signaling explicit dependency requirements.
- CLI UI remains powered by CheckpointService for metadata enrichment (e.g., confirmation prompts), but the actual rollback action is performed via PlanLifecycleService to ensure proper domain workflow and event emission.
- Technical implications
- All rollback logic now flows through the domain service layer (PlanLifecycleService) to preserve invariants and emit domain events, rather than allowing ad-hoc UI routes to bypass service validation.
- The UI can still retrieve checkpoint metadata for user confirmation, but the operation that modifies state uses the new rollback_plan pathway.
- Tests and behavior coverage were expanded via the new Behave feature and step implementations to validate state handling, events, and delegation.
- Affected modules/components
- src/cleveragents/infrastructure/events/types.py
- src/cleveragents/application/services/plan_lifecycle_service.py
- src/cleveragents/cli/commands/plan.py
- PlanLifecycleService module docstring
- features/plan_lifecycle_rollback.feature
- features/steps/plan_lifecycle_rollback_steps.py
ISSUES CLOSED: #3677
122 lines
6.0 KiB
Plaintext
122 lines
6.0 KiB
Plaintext
*** Settings ***
|
|
Documentation Integration test for actor context export-then-import round-trip
|
|
Library Process
|
|
Library OperatingSystem
|
|
Library String
|
|
Library DateTime
|
|
Library Collections
|
|
Resource ${CURDIR}/common.resource
|
|
Suite Setup Setup Test Environment
|
|
Suite Teardown Cleanup Test Environment
|
|
|
|
*** Variables ***
|
|
${TEST_CTX_DIR} ${EMPTY}
|
|
${EXPORT_FILE} ${EMPTY}
|
|
${CONTEXT_NAME} roundtrip-robot
|
|
|
|
*** Test Cases ***
|
|
Export Then Import Round-Trip Preserves Context
|
|
[Documentation] Create a context, export it, remove it, import it, and verify data integrity.
|
|
|
|
# 1. Create a named context by running actor with --context
|
|
${ctx_dir} = Set Variable ${TEMP}/actor_ctx
|
|
Set Suite Variable ${TEST_CTX_DIR} ${ctx_dir}
|
|
Create Directory ${ctx_dir}
|
|
|
|
# Create context data manually via Python helper
|
|
${result} = Run Process ${PYTHON} -c
|
|
... from cleveragents.reactive.context_manager import ContextManager; mgr \= ContextManager("${CONTEXT_NAME}", "${ctx_dir}"); mgr.add_message("user", "Hello from Robot"); mgr.add_message("assistant", "Hello Robot!"); print("created")
|
|
Log Create stdout: ${result.stdout}
|
|
Log Create stderr: ${result.stderr}
|
|
Should Be Equal As Integers ${result.rc} 0
|
|
Should Contain ${result.stdout} created
|
|
|
|
# 2. Export the context to JSON
|
|
${export_path} = Set Variable ${TEMP}/exported-context.json
|
|
Set Suite Variable ${EXPORT_FILE} ${export_path}
|
|
|
|
${result} = Run Process ${PYTHON} -m cleveragents actor context export
|
|
... ${CONTEXT_NAME} ${export_path} --context-dir ${ctx_dir}
|
|
Log Export stdout: ${result.stdout}
|
|
Log Export stderr: ${result.stderr}
|
|
Should Be Equal As Integers ${result.rc} 0
|
|
File Should Exist ${export_path}
|
|
|
|
# 3. Delete the original context (use 'delete' not 'remove' for named contexts)
|
|
${result} = Run Process ${PYTHON} -m cleveragents actor context delete
|
|
... ${CONTEXT_NAME} --yes --context-dir ${ctx_dir}
|
|
Log Delete stdout: ${result.stdout}
|
|
Log Delete stderr: ${result.stderr}
|
|
Should Be Equal As Integers ${result.rc} 0
|
|
Directory Should Not Exist ${ctx_dir}/${CONTEXT_NAME}
|
|
|
|
# 4. Import the context back
|
|
${result} = Run Process ${PYTHON} -m cleveragents actor context import
|
|
... ${CONTEXT_NAME} ${export_path} --context-dir ${ctx_dir}
|
|
Log Import stdout: ${result.stdout}
|
|
Log Import stderr: ${result.stderr}
|
|
Should Be Equal As Integers ${result.rc} 0
|
|
|
|
# 5. Verify the round-trip preserved messages
|
|
${result} = Run Process ${PYTHON} -c
|
|
... import json; from cleveragents.reactive.context_manager import ContextManager; mgr \= ContextManager("${CONTEXT_NAME}", "${ctx_dir}"); msgs \= mgr.messages; assert len(msgs) \=\= 2, f"Expected 2 messages, got {len(msgs)}"; assert msgs[0]["content"] \=\= "Hello from Robot"; assert msgs[1]["content"] \=\= "Hello Robot!"; print("verified")
|
|
Log Verify stdout: ${result.stdout}
|
|
Log Verify stderr: ${result.stderr}
|
|
Should Be Equal As Integers ${result.rc} 0
|
|
Should Contain ${result.stdout} verified
|
|
|
|
Export With JSON Format Flag Shows Structured Output
|
|
[Documentation] Verify export to JSON file works correctly.
|
|
|
|
${ctx_dir} = Set Variable ${TEMP}/actor_ctx_fmt
|
|
Create Directory ${ctx_dir}
|
|
|
|
# Create context
|
|
${result} = Run Process ${PYTHON} -c
|
|
... from cleveragents.reactive.context_manager import ContextManager; mgr \= ContextManager("fmt-test", "${ctx_dir}"); mgr.add_message("user", "test"); print("ok")
|
|
Should Be Equal As Integers ${result.rc} 0
|
|
|
|
# Export to JSON file (positional argument, per CLI spec)
|
|
${export_path} = Set Variable ${TEMP}/fmt-export.json
|
|
${result} = Run Process ${PYTHON} -m cleveragents actor context export
|
|
... fmt-test ${export_path} --context-dir ${ctx_dir}
|
|
Log Export stdout: ${result.stdout}
|
|
Log Export stderr: ${result.stderr}
|
|
Should Be Equal As Integers ${result.rc} 0
|
|
File Should Exist ${export_path}
|
|
|
|
Import Into Existing Context Overwrites Data
|
|
[Documentation] Verify importing into an existing context overwrites it.
|
|
|
|
${ctx_dir} = Set Variable ${TEMP}/actor_ctx_overwrite
|
|
Create Directory ${ctx_dir}
|
|
|
|
# Create existing context
|
|
${result} = Run Process ${PYTHON} -c
|
|
... from cleveragents.reactive.context_manager import ContextManager; mgr \= ContextManager("existing", "${ctx_dir}"); mgr.add_message("user", "original"); print("ok")
|
|
Should Be Equal As Integers ${result.rc} 0
|
|
|
|
# Create import file with new content
|
|
${import_path} = Set Variable ${TEMP}/existing-import.json
|
|
${result} = Run Process ${PYTHON} -c
|
|
... import json; data \= {"context_name": "existing", "messages": [{"role": "user", "content": "new", "timestamp": "2026-01-01", "metadata": {}}], "metadata": {}, "state": {}, "global_context": {}}; open("${import_path}", "w").write(json.dumps(data)); print("ok")
|
|
Should Be Equal As Integers ${result.rc} 0
|
|
|
|
# Import should succeed (overwrites existing context; positional argument, per CLI spec)
|
|
${result} = Run Process ${PYTHON} -m cleveragents actor context import
|
|
... existing ${import_path} --context-dir ${ctx_dir}
|
|
Should Be Equal As Integers ${result.rc} 0
|
|
|
|
*** Keywords ***
|
|
Setup Test Environment
|
|
[Documentation] Create test environment
|
|
common.Setup Test Environment
|
|
${temp} = Evaluate tempfile.mkdtemp() modules=tempfile
|
|
Set Suite Variable ${TEMP} ${temp}
|
|
Create Directory ${TEMP}
|
|
Log Test environment created at: ${TEMP}
|
|
|
|
Cleanup Test Environment
|
|
[Documentation] Clean up test environment
|
|
Run Keyword If '${TEMP}' != '${EMPTY}' Remove Directory ${TEMP} recursive=True
|