a2da043cbd
CI / lint (pull_request) Successful in 25s
CI / benchmark-publish (pull_request) Has been skipped
CI / quality (pull_request) Successful in 21s
CI / typecheck (pull_request) Successful in 39s
CI / security (pull_request) Successful in 37s
CI / build (pull_request) Successful in 26s
CI / integration_tests (pull_request) Successful in 4m43s
CI / unit_tests (pull_request) Successful in 11m2s
CI / docker (pull_request) Successful in 58s
CI / benchmark-regression (pull_request) Successful in 18m58s
CI / coverage (pull_request) Successful in 24m24s
Adds PlanExecutionContext carrying plan metadata and delegating changeset ops to ChangeSetStore. RuntimeExecuteResult captures execution output (changeset_id, tool_call_count, sandbox_refs, decision_ids_processed, execution_duration_ms). RuntimeExecuteActor dispatches StrategyDecision lists through ToolRunner with full changeset capture and optional streaming callbacks. PlanExecutor gains execution_context param with has_runtime / changeset_store / execution_context properties and _run_execute_with_runtime / _run_execute_with_stub split. 31 Behave scenarios, 5 Robot smoke tests, ASV benchmark suite, and reference documentation. Ref: Day-14 Rebaseline – M1.2 Plan-execute runtime wiring [Jeff]
209 lines
5.8 KiB
Python
209 lines
5.8 KiB
Python
"""Helper script for plan execute runtime Robot Framework tests.
|
|
|
|
Dispatches to individual test functions via a COMMANDS dict.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
|
|
from ulid import ULID
|
|
|
|
from cleveragents.application.services.plan_execution_context import (
|
|
PlanExecutionContext,
|
|
RuntimeExecuteActor,
|
|
RuntimeExecuteResult,
|
|
)
|
|
from cleveragents.application.services.plan_executor import (
|
|
PlanExecutor,
|
|
StrategyDecision,
|
|
)
|
|
from cleveragents.core.exceptions import ValidationError
|
|
from cleveragents.domain.models.core.change import (
|
|
ChangeEntry,
|
|
ChangeOperation,
|
|
InMemoryChangeSetStore,
|
|
)
|
|
from cleveragents.tool.registry import ToolRegistry
|
|
from cleveragents.tool.runner import ToolRunner
|
|
|
|
|
|
def _make_runner() -> ToolRunner:
|
|
return ToolRunner(registry=ToolRegistry())
|
|
|
|
|
|
def _make_decisions(count: int) -> list[StrategyDecision]:
|
|
root_id = str(ULID())
|
|
return [
|
|
StrategyDecision(
|
|
decision_id=root_id if i == 0 else str(ULID()),
|
|
step_text=f"Step {i + 1}",
|
|
sequence=i,
|
|
parent_id=root_id if i > 0 else None,
|
|
)
|
|
for i in range(count)
|
|
]
|
|
|
|
|
|
def _context_creation() -> None:
|
|
"""Test PlanExecutionContext creation and defaults."""
|
|
plan_id = str(ULID())
|
|
ctx = PlanExecutionContext(plan_id=plan_id)
|
|
assert ctx.plan_id == plan_id
|
|
assert ctx.decision_root_id is None
|
|
assert ctx.sandbox_root is None
|
|
assert ctx.automation_profile is None
|
|
assert ctx.project_resources == {}
|
|
assert ctx.resource_bindings == {}
|
|
assert isinstance(ctx.changeset_store, InMemoryChangeSetStore)
|
|
|
|
# With all fields
|
|
store = InMemoryChangeSetStore()
|
|
ctx2 = PlanExecutionContext(
|
|
plan_id=plan_id,
|
|
decision_root_id="root-123",
|
|
sandbox_root="/tmp/sb",
|
|
automation_profile="trusted",
|
|
project_resources={"r": "v"},
|
|
changeset_store=store,
|
|
)
|
|
assert ctx2.decision_root_id == "root-123"
|
|
assert ctx2.sandbox_root == "/tmp/sb"
|
|
assert ctx2.automation_profile == "trusted"
|
|
assert ctx2.changeset_store is store
|
|
|
|
# Empty plan_id should fail
|
|
try:
|
|
PlanExecutionContext(plan_id="")
|
|
raise AssertionError("Expected ValidationError")
|
|
except ValidationError:
|
|
pass
|
|
|
|
print("context-creation-ok")
|
|
|
|
|
|
def _changeset_ops() -> None:
|
|
"""Test changeset start, record, get, summarize."""
|
|
plan_id = str(ULID())
|
|
store = InMemoryChangeSetStore()
|
|
ctx = PlanExecutionContext(plan_id=plan_id, changeset_store=store)
|
|
|
|
cs_id = ctx.start_changeset()
|
|
assert len(cs_id) == 26 # ULID length
|
|
assert cs_id in ctx.active_changeset_ids
|
|
|
|
entry = ChangeEntry(
|
|
plan_id=plan_id,
|
|
resource_id=str(ULID()),
|
|
tool_name="test/tool",
|
|
operation=ChangeOperation.MODIFY,
|
|
path="src/test.py",
|
|
)
|
|
ctx.record_change(entry)
|
|
|
|
cs = ctx.get_changeset(cs_id)
|
|
assert cs is not None
|
|
assert len(cs.entries) == 1
|
|
assert cs.entries[0].entry_id == entry.entry_id
|
|
|
|
# Unknown ID
|
|
assert ctx.get_changeset("nonexistent") is None
|
|
|
|
# Summarize
|
|
summary = ctx.summarize()
|
|
assert summary["plan_id"] == plan_id
|
|
assert summary["active_changeset_count"] == 1
|
|
assert len(summary["changeset_summaries"]) == 1
|
|
|
|
print("changeset-ops-ok")
|
|
|
|
|
|
def _runtime_actor() -> None:
|
|
"""Test RuntimeExecuteActor execution."""
|
|
plan_id = str(ULID())
|
|
store = InMemoryChangeSetStore()
|
|
ctx = PlanExecutionContext(plan_id=plan_id, changeset_store=store)
|
|
runner = _make_runner()
|
|
actor = RuntimeExecuteActor(tool_runner=runner, execution_context=ctx)
|
|
|
|
decisions = _make_decisions(2)
|
|
result = actor.execute(decisions=decisions)
|
|
|
|
assert isinstance(result, RuntimeExecuteResult)
|
|
assert len(result.changeset_id) == 26
|
|
assert len(result.decision_ids_processed) == 2
|
|
assert result.execution_duration_ms >= 0.0
|
|
|
|
# With stream callback
|
|
events: list[tuple[str, dict]] = []
|
|
actor2 = RuntimeExecuteActor(tool_runner=runner, execution_context=ctx)
|
|
actor2.execute(
|
|
decisions=_make_decisions(1),
|
|
stream_callback=lambda t, d: events.append((t, d)),
|
|
)
|
|
types = [e[0] for e in events]
|
|
assert "runtime_execute_started" in types
|
|
assert "runtime_execute_complete" in types
|
|
|
|
print("runtime-actor-ok")
|
|
|
|
|
|
def _executor_runtime() -> None:
|
|
"""Test PlanExecutor with execution context (runtime mode)."""
|
|
plan_id = str(ULID())
|
|
store = InMemoryChangeSetStore()
|
|
ctx = PlanExecutionContext(plan_id=plan_id, changeset_store=store)
|
|
|
|
from unittest.mock import MagicMock
|
|
|
|
lifecycle = MagicMock()
|
|
|
|
executor = PlanExecutor(
|
|
lifecycle_service=lifecycle,
|
|
tool_runner=_make_runner(),
|
|
execution_context=ctx,
|
|
)
|
|
assert executor.has_runtime is True
|
|
assert executor.changeset_store is store
|
|
assert executor.execution_context is ctx
|
|
|
|
print("executor-runtime-ok")
|
|
|
|
|
|
def _executor_stub() -> None:
|
|
"""Test PlanExecutor without execution context (stub mode)."""
|
|
from unittest.mock import MagicMock
|
|
|
|
lifecycle = MagicMock()
|
|
|
|
executor = PlanExecutor(lifecycle_service=lifecycle)
|
|
assert executor.has_runtime is False
|
|
assert executor.changeset_store is None
|
|
assert executor.execution_context is None
|
|
|
|
print("executor-stub-ok")
|
|
|
|
|
|
COMMANDS: dict[str, object] = {
|
|
"context-creation": _context_creation,
|
|
"changeset-ops": _changeset_ops,
|
|
"runtime-actor": _runtime_actor,
|
|
"executor-runtime": _executor_runtime,
|
|
"executor-stub": _executor_stub,
|
|
}
|
|
|
|
|
|
def main() -> None:
|
|
if len(sys.argv) < 2:
|
|
raise SystemExit("Expected command argument")
|
|
command = sys.argv[1]
|
|
if command not in COMMANDS:
|
|
raise SystemExit(f"Unknown command: {command}")
|
|
func = COMMANDS[command]
|
|
if callable(func):
|
|
func()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|