tenseleyflow/loader / ff08957

Browse files

Add direct tests for workflow state control

Authored by espadonne
SHA
ff0895701aefb15b59f22840a2b3ef829eb1db96
Parents
8c70869
Tree
2a7bbdc

1 changed file

StatusFile+-
A tests/test_workflow_state.py 117 0
tests/test_workflow_state.pyadded
@@ -0,0 +1,117 @@
1
+"""Tests for workflow-state coordination helpers."""
2
+
3
+from __future__ import annotations
4
+
5
+from pathlib import Path
6
+
7
+import pytest
8
+
9
+from loader.agent.loop import Agent, AgentConfig
10
+from loader.runtime.conversation import ConversationRuntime
11
+from loader.runtime.dod import DefinitionOfDoneStore
12
+from loader.runtime.events import TurnSummary
13
+from loader.runtime.workflow import WorkflowDecisionKind, WorkflowMode
14
+from loader.runtime.workflow_policy import ModeDecision
15
+from tests.helpers.runtime_harness import ScriptedBackend
16
+
17
+
18
+def non_streaming_config() -> AgentConfig:
19
+    """Shared config for direct workflow-state tests."""
20
+
21
+    return AgentConfig(auto_context=False, stream=False, max_iterations=8)
22
+
23
+
24
+@pytest.mark.asyncio
25
+async def test_workflow_state_applies_mode_to_session_dod_and_timeline(
26
+    temp_dir: Path,
27
+) -> None:
28
+    backend = ScriptedBackend()
29
+    agent = Agent(
30
+        backend=backend,
31
+        config=non_streaming_config(),
32
+        project_root=temp_dir,
33
+    )
34
+    agent.prompt_format = "native"
35
+    agent.prompt_sections = ["Workflow Context", "Runtime Config"]
36
+    runtime = ConversationRuntime(agent)
37
+    dod = runtime.dod_store.create_or_resume("Tighten the workflow state controller.")
38
+    brief_path = temp_dir / "brief.md"
39
+    brief_path.write_text("# Task Brief\n\n## Likely Touchpoints\n- src/loader/runtime\n")
40
+    dod.clarify_brief = str(brief_path)
41
+    summary = TurnSummary(final_response="")
42
+    events = []
43
+
44
+    async def capture(event) -> None:
45
+        events.append(event)
46
+
47
+    decision = ModeDecision.transition(
48
+        WorkflowMode.PLAN,
49
+        reason_code="task_is_complex",
50
+        reason_summary="task complexity favors planning first",
51
+        decision_kind=WorkflowDecisionKind.HANDOFF,
52
+        scheduled_next_mode=WorkflowMode.EXECUTE,
53
+    )
54
+
55
+    await runtime.workflow_state.set_workflow_mode(
56
+        decision,
57
+        dod=dod,
58
+        emit=capture,
59
+        summary=summary,
60
+    )
61
+
62
+    reloaded = DefinitionOfDoneStore(temp_dir).load(Path(dod.storage_path))
63
+
64
+    assert agent.workflow_mode == "plan"
65
+    assert agent.session.workflow_mode == "plan"
66
+    assert summary.workflow_mode == "plan"
67
+    assert summary.workflow_reason_code == "task_is_complex"
68
+    assert summary.workflow_decision_kind == "handoff"
69
+    assert dod.current_mode == "plan"
70
+    assert dod.mode_history[-1] == "plan"
71
+    assert reloaded.current_mode == "plan"
72
+    assert summary.definition_of_done is dod
73
+    assert summary.workflow_timeline[-1].mode == "plan"
74
+    assert summary.workflow_timeline[-1].kind == "handoff"
75
+    assert summary.workflow_timeline[-1].artifact_paths == [str(brief_path)]
76
+    assert summary.workflow_timeline[-1].prompt_format == "native"
77
+    assert any(
78
+        event.type == "workflow_mode" and event.workflow_mode == "plan"
79
+        for event in events
80
+    )
81
+
82
+
83
+def test_workflow_state_appends_execute_bridge_once(
84
+    temp_dir: Path,
85
+) -> None:
86
+    backend = ScriptedBackend()
87
+    agent = Agent(
88
+        backend=backend,
89
+        config=non_streaming_config(),
90
+        project_root=temp_dir,
91
+    )
92
+    runtime = ConversationRuntime(agent)
93
+    dod = runtime.dod_store.create_or_resume("Use the persisted workflow artifacts.")
94
+
95
+    brief_path = temp_dir / "brief.md"
96
+    brief_path.write_text("# Task Brief\n\n## In Scope\n- Runtime workflow\n")
97
+    plan_path = temp_dir / "plan.md"
98
+    plan_path.write_text("# Implementation Plan\n\n- Extract the controller\n")
99
+    verify_path = temp_dir / "verify.md"
100
+    verify_path.write_text("# Verification Plan\n\n- Run uv run pytest -q\n")
101
+    dod.clarify_brief = str(brief_path)
102
+    dod.implementation_plan = str(plan_path)
103
+    dod.verification_plan = str(verify_path)
104
+
105
+    runtime.workflow_state.maybe_append_execute_bridge(dod)
106
+    runtime.workflow_state.maybe_append_execute_bridge(dod)
107
+
108
+    bridge_messages = [
109
+        message.content
110
+        for message in agent.session.messages
111
+        if "[WORKFLOW BRIDGE]" in message.content
112
+    ]
113
+
114
+    assert len(bridge_messages) == 1
115
+    assert "Use the clarify brief below as the requirements source of truth." in bridge_messages[0]
116
+    assert "# Implementation Plan" in bridge_messages[0]
117
+    assert "# Verification Plan" in bridge_messages[0]