tenseleyflow/loader / 83ee8d0

Browse files

Surface planned verification lifecycle

Authored by espadonne
SHA
83ee8d0b235e1127eb8abd2699c6bd1cddcf4f4b
Parents
f1feb9e
Tree
fc00ea6

4 changed files

StatusFile+-
M src/loader/cli/main.py 2 0
M src/loader/runtime/workflow_timeline_read_model.py 2 0
M tests/test_inspection.py 91 0
M tests/test_workflow_timeline_read_model.py 33 0
src/loader/cli/main.pymodified
@@ -1512,6 +1512,7 @@ def _print_status_snapshot(snapshot: StatusSnapshot) -> None:
15121512
         evidence.add_column("Detail", style="dim")
15131513
         for item in snapshot.recent_verification:
15141514
             result = {
1515
+                "planned": "[blue]planned[/blue]",
15151516
                 "pending": "[cyan]pending[/cyan]",
15161517
                 "stale": "[yellow]stale[/yellow]",
15171518
                 "passed": "[green]pass[/green]",
@@ -1772,6 +1773,7 @@ def _session_show_main(session_id: str) -> None:
17721773
         verification.add_column("Detail", style="dim")
17731774
         for item in detail.recent_verification:
17741775
             result = {
1776
+                "planned": "[blue]planned[/blue]",
17751777
                 "pending": "[cyan]pending[/cyan]",
17761778
                 "stale": "[yellow]stale[/yellow]",
17771779
                 "passed": "[green]pass[/green]",
src/loader/runtime/workflow_timeline_read_model.pymodified
@@ -156,6 +156,8 @@ def workflow_timeline_highlights(
156156
     if verify_entry is not None:
157157
         if verify_entry.kind == "verify_skip":
158158
             prefix = "Skipped verify:"
159
+        elif verify_entry.policy_outcome == "planned":
160
+            prefix = "Verify planned:"
159161
         elif verify_entry.policy_outcome == "pending":
160162
             prefix = "Verify pending:"
161163
         elif verify_entry.policy_outcome == "stale":
tests/test_inspection.pymodified
@@ -529,6 +529,50 @@ def _persist_session_with_pending_verification(temp_dir: Path) -> str:
529529
     return snapshot.session_id
530530
 
531531
 
532
+def _persist_session_with_planned_verification(temp_dir: Path) -> str:
533
+    snapshot = SessionSnapshot(
534
+        session_id="20260406T160430Z-plan1234",
535
+        created_at="2026-04-06T16:04:30Z",
536
+        updated_at="2026-04-06T16:04:50Z",
537
+        messages=[
538
+            Message(role=Role.USER, content="Keep editing the runtime"),
539
+            Message(role=Role.ASSISTANT, content="Verification will run after execution."),
540
+        ],
541
+        current_task="Keep editing the runtime",
542
+        runtime_owner_type="RuntimeHandle",
543
+        runtime_owner_path="runtime-handle",
544
+        workflow_mode="execute",
545
+        permission_mode="workspace-write",
546
+        prompt_format="native",
547
+        prompt_sections=["Runtime Config", "Workflow Context", "Mode Guidance"],
548
+        workflow_timeline=[
549
+            WorkflowTimelineEntry(
550
+                timestamp="2026-04-06T16:04:50Z",
551
+                kind="verify_observation",
552
+                mode="execute",
553
+                reason_code="verification_planned",
554
+                summary="verify: verification is planned after new mutating work",
555
+                decision_kind="forced",
556
+                policy_stage="verification",
557
+                policy_outcome="planned",
558
+                verification_observations=[
559
+                    VerificationObservation(
560
+                        status="planned",
561
+                        summary="verification planned for `uv run pytest -q`",
562
+                        command="uv run pytest -q",
563
+                        kind="runtime",
564
+                        detail="write changed src/loader/runtime/tool_batches.py",
565
+                    )
566
+                ],
567
+                prompt_format="native",
568
+                prompt_sections=["Runtime Config", "Workflow Context", "Mode Guidance"],
569
+            )
570
+        ],
571
+    )
572
+    SessionStore(temp_dir).save(snapshot)
573
+    return snapshot.session_id
574
+
575
+
532576
 def _persist_session_with_stale_verification(temp_dir: Path) -> str:
533577
     snapshot = SessionSnapshot(
534578
         session_id="20260406T160700Z-stale1234",
@@ -889,6 +933,30 @@ def test_collect_status_snapshot_surfaces_pending_verification(
889933
     ]
890934
 
891935
 
936
+def test_collect_status_snapshot_surfaces_planned_verification(
937
+    temp_dir: Path,
938
+) -> None:
939
+    _write_python_workspace(temp_dir)
940
+    _ensure_loader_dirs(temp_dir)
941
+    _persist_session_with_planned_verification(temp_dir)
942
+
943
+    snapshot = collect_status_snapshot(temp_dir)
944
+
945
+    assert snapshot.latest_policy_summary is not None
946
+    assert "verification_planned" in snapshot.latest_policy_summary
947
+    assert "policy-outcome=planned" in snapshot.latest_policy_summary
948
+    assert snapshot.latest_policy_observed_verification == [
949
+        "verification planned for `uv run pytest -q` [write changed src/loader/runtime/tool_batches.py]"
950
+    ]
951
+    assert [item.status for item in snapshot.recent_verification] == ["planned"]
952
+    assert [item.command for item in snapshot.recent_verification] == [
953
+        "uv run pytest -q"
954
+    ]
955
+    assert [item.detail for item in snapshot.recent_verification] == [
956
+        "write changed src/loader/runtime/tool_batches.py"
957
+    ]
958
+
959
+
892960
 def test_collect_status_snapshot_surfaces_stale_verification(
893961
     temp_dir: Path,
894962
 ) -> None:
@@ -1094,6 +1162,29 @@ def test_workflow_command_renders_stale_verification_context(
10941162
     assert "new mutating work" in result.output
10951163
 
10961164
 
1165
+def test_workflow_command_renders_planned_verification_context(
1166
+    temp_dir: Path,
1167
+    monkeypatch: pytest.MonkeyPatch,
1168
+) -> None:
1169
+    _write_python_workspace(temp_dir)
1170
+    _ensure_loader_dirs(temp_dir)
1171
+    session_id = _persist_session_with_planned_verification(temp_dir)
1172
+    runner = CliRunner()
1173
+
1174
+    monkeypatch.chdir(temp_dir)
1175
+
1176
+    result = runner.invoke(cli_main_module.workflow_cli, ["show"])
1177
+
1178
+    assert result.exit_code == 0
1179
+    assert session_id in result.output
1180
+    assert "Verify planned:" in result.output
1181
+    assert "verification_planned" in result.output
1182
+    assert "policy-outcome=planned" in result.output
1183
+    assert "Observed Verification" in result.output
1184
+    assert "verification planned for `uv run pytest -q`" in result.output
1185
+    assert "uv run pytest -q" in result.output
1186
+
1187
+
10971188
 def test_collect_workflow_timeline_can_focus_on_policy_accountability(
10981189
     temp_dir: Path,
10991190
 ) -> None:
tests/test_workflow_timeline_read_model.pymodified
@@ -159,6 +159,39 @@ def test_project_workflow_timeline_highlights_pending_verification() -> None:
159159
     assert any(item.startswith("Verify pending:") for item in projection.highlights)
160160
 
161161
 
162
+def test_project_workflow_timeline_highlights_planned_verification() -> None:
163
+    entries = [
164
+        WorkflowTimelineEntry(
165
+            timestamp="2026-04-09T12:02:00Z",
166
+            kind="verify_observation",
167
+            mode="execute",
168
+            reason_code="verification_planned",
169
+            summary="verify: verification is planned after new mutating work",
170
+            decision_kind="forced",
171
+            policy_stage="verification",
172
+            policy_outcome="planned",
173
+            verification_observations=[
174
+                VerificationObservation(
175
+                    status="planned",
176
+                    summary="verification planned for `pytest -q`",
177
+                    command="pytest -q",
178
+                    kind="runtime",
179
+                    detail="write changed README.md",
180
+                )
181
+            ],
182
+        )
183
+    ]
184
+
185
+    projection = project_workflow_timeline(entries, accountability_only=True)
186
+
187
+    assert projection.latest_policy_summary is not None
188
+    assert "policy-outcome=planned" in projection.latest_policy_summary
189
+    assert "observed=verification planned for `pytest -q` [write changed README.md]" in (
190
+        projection.latest_policy_summary
191
+    )
192
+    assert any(item.startswith("Verify planned:") for item in projection.highlights)
193
+
194
+
162195
 def test_project_workflow_timeline_highlights_stale_verification() -> None:
163196
     entries = [
164197
         WorkflowTimelineEntry(