tenseleyflow/loader / d193644

Browse files

Add runtime boundary and verification state summaries

Authored by espadonne
SHA
d193644f6eb04578eafc7d56daba8c9aae58fd01
Parents
cc0fe04
Tree
d46f5bf

4 changed files

StatusFile+-
M src/loader/cli/main.py 14 0
M src/loader/runtime/inspection.py 78 3
M src/loader/runtime/owner_metadata.py 28 0
M tests/test_inspection.py 40 0
src/loader/cli/main.pymodified
@@ -1398,6 +1398,8 @@ def _print_status_snapshot(snapshot: StatusSnapshot) -> None:
13981398
             or "none"
13991399
         ),
14001400
     )
1401
+    if snapshot.runtime_boundary_summary:
1402
+        table.add_row("Boundary", snapshot.runtime_boundary_summary)
14011403
     table.add_row("Workflow", snapshot.workflow_mode)
14021404
     if snapshot.workflow_decision_kind:
14031405
         table.add_row("Decision Kind", snapshot.workflow_decision_kind)
@@ -1472,6 +1474,8 @@ def _print_status_snapshot(snapshot: StatusSnapshot) -> None:
14721474
     table.add_row("DoD", snapshot.dod_status or "none")
14731475
     table.add_row("Pending", str(snapshot.dod_pending_items_count))
14741476
     table.add_row("Last Verify", snapshot.last_verification_result or "none")
1477
+    if snapshot.verification_state_summary:
1478
+        table.add_row("Verification State", snapshot.verification_state_summary)
14751479
     if snapshot.usage:
14761480
         table.add_row(
14771481
             "Usage",
@@ -1592,6 +1596,8 @@ def _session_list_main() -> None:
15921596
                 or "none"
15931597
             ),
15941598
         )
1599
+        if entry.runtime_boundary_summary:
1600
+            table.add_row("Boundary", entry.runtime_boundary_summary)
15951601
         table.add_row("Workflow", entry.workflow_mode)
15961602
         if entry.workflow_decision_kind:
15971603
             table.add_row("Decision Kind", entry.workflow_decision_kind)
@@ -1657,6 +1663,8 @@ def _session_show_main(session_id: str) -> None:
16571663
             or "none"
16581664
         ),
16591665
     )
1666
+    if detail.runtime_boundary_summary:
1667
+        table.add_row("Boundary", detail.runtime_boundary_summary)
16601668
     table.add_row("Workflow", snapshot.workflow_mode)
16611669
     if snapshot.workflow_decision_kind:
16621670
         table.add_row("Decision Kind", snapshot.workflow_decision_kind)
@@ -1725,6 +1733,8 @@ def _session_show_main(session_id: str) -> None:
17251733
     table.add_row("Rules Source", snapshot.permission_rules_source or "none")
17261734
     table.add_row("Task", snapshot.current_task or "none")
17271735
     table.add_row("Active DoD", snapshot.active_dod_path or "none")
1736
+    if detail.verification_state_summary:
1737
+        table.add_row("Verification State", detail.verification_state_summary)
17281738
     if snapshot.usage:
17291739
         table.add_row(
17301740
             "Usage",
@@ -1848,8 +1858,12 @@ def _workflow_show_main(
18481858
             or "none"
18491859
         ),
18501860
     )
1861
+    if snapshot.runtime_boundary_summary:
1862
+        table.add_row("Boundary", snapshot.runtime_boundary_summary)
18511863
     table.add_row("Workflow", snapshot.workflow_mode)
18521864
     table.add_row("Task", snapshot.current_task or "none")
1865
+    if snapshot.verification_state_summary:
1866
+        table.add_row("Verification State", snapshot.verification_state_summary)
18531867
     table.add_row("Entries", f"{len(snapshot.entries)} shown / {snapshot.total_entries} total")
18541868
     if snapshot.latest_policy_summary:
18551869
         table.add_row("Latest Policy", snapshot.latest_policy_summary)
src/loader/runtime/inspection.pymodified
@@ -16,6 +16,7 @@ from ..runtime.capabilities import CapabilityProfile, resolve_capability_profile
1616
 from ..tools.base import ToolRegistry, create_default_registry
1717
 from .dod import DefinitionOfDone, DefinitionOfDoneStore, VerificationEvidence
1818
 from .explore_state import ExploreStateStore
19
+from .owner_metadata import format_runtime_boundary_label
1920
 from .permissions import (
2021
     PermissionConfigStatus,
2122
     PermissionDecision,
@@ -165,6 +166,7 @@ class StatusSnapshot:
165166
     model: str
166167
     capability_profile: CapabilityProfile
167168
     active_session_id: str | None
169
+    runtime_boundary_summary: str | None
168170
     workflow_mode: str
169171
     workflow_reason_code: str | None
170172
     workflow_reason_summary: str | None
@@ -192,6 +194,7 @@ class StatusSnapshot:
192194
     dod_status: str | None
193195
     dod_pending_items_count: int
194196
     last_verification_result: str | None
197
+    verification_state_summary: str | None
195198
     recent_verification: list[VerificationSummary]
196199
     latest_policy_supporting_evidence: list[str] = field(default_factory=list)
197200
     latest_policy_blocking_evidence: list[str] = field(default_factory=list)
@@ -232,6 +235,7 @@ class SessionSummary:
232235
     created_at: str
233236
     updated_at: str
234237
     message_count: int
238
+    runtime_boundary_summary: str | None
235239
     workflow_mode: str
236240
     workflow_reason_code: str | None
237241
     workflow_reason_summary: str | None
@@ -260,6 +264,8 @@ class SessionDetail:
260264
     snapshot: SessionSnapshot
261265
     is_current: bool
262266
     definition_of_done: DefinitionOfDone | None
267
+    runtime_boundary_summary: str | None = None
268
+    verification_state_summary: str | None = None
263269
     recent_verification: list[VerificationSummary] = field(default_factory=list)
264270
 
265271
 
@@ -290,8 +296,10 @@ class WorkflowTimelineSnapshot:
290296
     project_root: Path
291297
     session_id: str | None
292298
     is_current: bool
299
+    runtime_boundary_summary: str | None
293300
     workflow_mode: str
294301
     current_task: str | None
302
+    verification_state_summary: str | None = None
295303
     total_entries: int = 0
296304
     latest_policy_summary: str | None = None
297305
     latest_policy_supporting_evidence: list[str] = field(default_factory=list)
@@ -461,6 +469,7 @@ def collect_status_snapshot(
461469
             model=resolved_model,
462470
             capability_profile=capability_profile,
463471
             active_session_id=None,
472
+            runtime_boundary_summary=None,
464473
             runtime_owner_type=None,
465474
             runtime_owner_path=None,
466475
             workflow_mode="execute",
@@ -498,6 +507,7 @@ def collect_status_snapshot(
498507
             dod_status=None,
499508
             dod_pending_items_count=0,
500509
             last_verification_result=None,
510
+            verification_state_summary=None,
501511
             recent_verification=[],
502512
             usage={},
503513
             compaction_count=0,
@@ -535,6 +545,10 @@ def collect_status_snapshot(
535545
         model=resolved_model,
536546
         capability_profile=capability_profile,
537547
         active_session_id=snapshot.session_id,
548
+        runtime_boundary_summary=_runtime_boundary_summary(
549
+            snapshot.runtime_owner_type,
550
+            snapshot.runtime_owner_path,
551
+        ),
538552
         runtime_owner_type=snapshot.runtime_owner_type,
539553
         runtime_owner_path=snapshot.runtime_owner_path,
540554
         workflow_mode=snapshot.workflow_mode,
@@ -582,6 +596,12 @@ def collect_status_snapshot(
582596
             dod=dod,
583597
             recent_verification=recent_verification,
584598
         ),
599
+        verification_state_summary=_verification_state_summary(
600
+            recent_verification,
601
+            fallback_status=(
602
+                dod.last_verification_result if dod is not None else None
603
+            ),
604
+        ),
585605
         recent_verification=recent_verification,
586606
         usage=dict(snapshot.usage),
587607
         compaction_count=(snapshot.compaction.count if snapshot.compaction else 0),
@@ -648,6 +668,10 @@ def list_session_summaries(project_root: Path | str | None = None) -> list[Sessi
648668
                 session_id=snapshot.session_id,
649669
                 created_at=snapshot.created_at,
650670
                 updated_at=snapshot.updated_at,
671
+                runtime_boundary_summary=_runtime_boundary_summary(
672
+                    snapshot.runtime_owner_type,
673
+                    snapshot.runtime_owner_path,
674
+                ),
651675
                 runtime_owner_type=snapshot.runtime_owner_type,
652676
                 runtime_owner_path=snapshot.runtime_owner_path,
653677
                 message_count=len(snapshot.messages),
@@ -689,14 +713,23 @@ def load_session_detail(
689713
     snapshot = store.load(session_id)
690714
     current_session_id = _current_session_id(store)
691715
     dod = _load_dod(snapshot.active_dod_path, project_root=resolved_root)
716
+    recent_verification = _recent_verification_summaries(
717
+        timeline=snapshot.workflow_timeline,
718
+        evidence=dod.evidence if dod else [],
719
+    )
692720
     return SessionDetail(
693721
         snapshot=snapshot,
694722
         is_current=snapshot.session_id == current_session_id,
695723
         definition_of_done=dod,
696
-        recent_verification=_recent_verification_summaries(
697
-            timeline=snapshot.workflow_timeline,
698
-            evidence=dod.evidence if dod else [],
724
+        runtime_boundary_summary=_runtime_boundary_summary(
725
+            snapshot.runtime_owner_type,
726
+            snapshot.runtime_owner_path,
727
+        ),
728
+        verification_state_summary=_verification_state_summary(
729
+            recent_verification,
730
+            fallback_status=(dod.last_verification_result if dod is not None else None),
699731
         ),
732
+        recent_verification=recent_verification,
700733
     )
701734
 
702735
 
@@ -943,10 +976,12 @@ def collect_workflow_timeline(
943976
             project_root=resolved_root,
944977
             session_id=None,
945978
             is_current=False,
979
+            runtime_boundary_summary=None,
946980
             runtime_owner_type=None,
947981
             runtime_owner_path=None,
948982
             workflow_mode="execute",
949983
             current_task=None,
984
+            verification_state_summary=None,
950985
             total_entries=0,
951986
             latest_policy_summary=None,
952987
             latest_policy_supporting_evidence=[],
@@ -969,15 +1004,29 @@ def collect_workflow_timeline(
9691004
         accountability_only=accountability_only,
9701005
         limit=limit,
9711006
     )
1007
+    dod = _load_dod(snapshot.active_dod_path, project_root=resolved_root)
1008
+    recent_verification = _recent_verification_summaries(
1009
+        timeline=snapshot.workflow_timeline,
1010
+        evidence=dod.evidence if dod else [],
1011
+        limit=1,
1012
+    )
9721013
 
9731014
     return WorkflowTimelineSnapshot(
9741015
         project_root=resolved_root,
9751016
         session_id=snapshot.session_id,
9761017
         is_current=snapshot.session_id == current_session_id,
1018
+        runtime_boundary_summary=_runtime_boundary_summary(
1019
+            snapshot.runtime_owner_type,
1020
+            snapshot.runtime_owner_path,
1021
+        ),
9771022
         runtime_owner_type=snapshot.runtime_owner_type,
9781023
         runtime_owner_path=snapshot.runtime_owner_path,
9791024
         workflow_mode=snapshot.workflow_mode,
9801025
         current_task=snapshot.current_task,
1026
+        verification_state_summary=_verification_state_summary(
1027
+            recent_verification,
1028
+            fallback_status=(dod.last_verification_result if dod is not None else None),
1029
+        ),
9811030
         total_entries=projection.total_entries,
9821031
         latest_policy_summary=projection.latest_policy_summary,
9831032
         latest_policy_supporting_evidence=(
@@ -1658,6 +1707,32 @@ def _verification_summaries_from_evidence(
16581707
     return summaries
16591708
 
16601709
 
1710
+def _runtime_boundary_summary(
1711
+    owner_type: str | None,
1712
+    owner_path: str | None,
1713
+) -> str | None:
1714
+    return format_runtime_boundary_label(owner_type, owner_path)
1715
+
1716
+
1717
+def _verification_state_summary(
1718
+    recent_verification: list[VerificationSummary],
1719
+    *,
1720
+    fallback_status: str | None = None,
1721
+) -> str | None:
1722
+    if recent_verification:
1723
+        item = recent_verification[0]
1724
+        parts = [item.status]
1725
+        if item.attempt:
1726
+            parts.append(f"({item.attempt})")
1727
+        summary = " ".join(parts)
1728
+        if item.command:
1729
+            summary += f" for {item.command}"
1730
+        return summary
1731
+    if fallback_status:
1732
+        return fallback_status
1733
+    return None
1734
+
1735
+
16611736
 def _last_verification_result(
16621737
     *,
16631738
     dod: DefinitionOfDone | None,
src/loader/runtime/owner_metadata.pymodified
@@ -62,6 +62,34 @@ def format_runtime_owner_label(
6262
     return normalized_path or normalized_type
6363
 
6464
 
65
+def classify_runtime_owner_boundary(
66
+    owner_type: str | None,
67
+    owner_path: str | None,
68
+) -> str | None:
69
+    """Classify the persisted owner boundary for operator surfaces."""
70
+
71
+    normalized_type = normalize_runtime_owner_type(owner_type)
72
+    normalized_path = normalize_runtime_owner_path(owner_path, owner_type=normalized_type)
73
+    if normalized_path == "runtime-handle" or normalized_type == "RuntimeHandle":
74
+        return "runtime-first"
75
+    if normalized_path == "public-agent" or normalized_type == "Agent":
76
+        return "public-compat"
77
+    return None
78
+
79
+
80
+def format_runtime_boundary_label(
81
+    owner_type: str | None,
82
+    owner_path: str | None,
83
+) -> str | None:
84
+    """Render one concise operator-facing runtime boundary label."""
85
+
86
+    boundary = classify_runtime_owner_boundary(owner_type, owner_path)
87
+    owner = format_runtime_owner_label(owner_type, owner_path)
88
+    if boundary and owner:
89
+        return f"{boundary} via {owner}"
90
+    return boundary or owner
91
+
92
+
6593
 def _camel_to_kebab(value: str) -> str:
6694
     """Convert one CamelCase-ish class name into kebab-case."""
6795
 
tests/test_inspection.pymodified
@@ -732,6 +732,7 @@ def test_status_and_session_surfaces_reflect_persisted_state(temp_dir: Path) ->
732732
     assert snapshot.last_verification_result == "failed"
733733
     assert snapshot.active_dod_path == dod_path
734734
     assert snapshot.permission_mode == "prompt"
735
+    assert snapshot.runtime_boundary_summary == "runtime-first via runtime-handle (RuntimeHandle)"
735736
     assert snapshot.runtime_owner_type == "RuntimeHandle"
736737
     assert snapshot.runtime_owner_path == "runtime-handle"
737738
     assert snapshot.permission_rule_counts == {"allow": 1, "deny": 2, "ask": 1}
@@ -769,12 +770,16 @@ def test_status_and_session_surfaces_reflect_persisted_state(temp_dir: Path) ->
769770
     assert [item.status for item in snapshot.recent_verification] == ["failed"]
770771
     assert [item.command for item in snapshot.recent_verification] == ["pytest -q"]
771772
     assert [item.detail for item in snapshot.recent_verification] == ["1 failed"]
773
+    assert snapshot.verification_state_summary == "failed for pytest -q"
772774
 
773775
     assert len(sessions) == 1
774776
     assert sessions[0].session_id == session_id
775777
     assert sessions[0].is_current is True
776778
     assert sessions[0].runtime_owner_type == "RuntimeHandle"
777779
     assert sessions[0].runtime_owner_path == "runtime-handle"
780
+    assert sessions[0].runtime_boundary_summary == (
781
+        "runtime-first via runtime-handle (RuntimeHandle)"
782
+    )
778783
     assert sessions[0].dod_status == "fixing"
779784
     assert sessions[0].permission_prompting_enabled is True
780785
     assert sessions[0].permission_rule_counts == {"allow": 1, "deny": 2, "ask": 1}
@@ -799,6 +804,10 @@ def test_status_and_session_surfaces_reflect_persisted_state(temp_dir: Path) ->
799804
     assert detail.is_current is True
800805
     assert detail.snapshot.runtime_owner_type == "RuntimeHandle"
801806
     assert detail.snapshot.runtime_owner_path == "runtime-handle"
807
+    assert detail.runtime_boundary_summary == (
808
+        "runtime-first via runtime-handle (RuntimeHandle)"
809
+    )
810
+    assert detail.verification_state_summary == "failed for pytest -q"
802811
     assert detail.definition_of_done is not None
803812
     assert detail.definition_of_done.status == "fixing"
804813
     assert detail.snapshot.permission_rules_source == str(
@@ -830,8 +839,12 @@ def test_collect_workflow_timeline_reflects_persisted_history(temp_dir: Path) ->
830839
     assert snapshot.is_current is True
831840
     assert snapshot.runtime_owner_type == "RuntimeHandle"
832841
     assert snapshot.runtime_owner_path == "runtime-handle"
842
+    assert snapshot.runtime_boundary_summary == (
843
+        "runtime-first via runtime-handle (RuntimeHandle)"
844
+    )
833845
     assert snapshot.workflow_mode == "execute"
834846
     assert snapshot.current_task == "Fix the failing tests"
847
+    assert snapshot.verification_state_summary == "failed for pytest -q"
835848
     assert snapshot.total_entries == 2
836849
     assert [entry.kind for entry in snapshot.entries] == ["handoff", "reentry"]
837850
     assert snapshot.entries[-1].reason_code == "verification_failed_reentry"
@@ -939,6 +952,9 @@ def test_collect_status_snapshot_surfaces_pending_verification(
939952
         "uv run pytest -q"
940953
     ]
941954
     assert [item.attempt for item in snapshot.recent_verification] == ["attempt 2"]
955
+    assert snapshot.verification_state_summary == (
956
+        "pending (attempt 2) for uv run pytest -q"
957
+    )
942958
 
943959
 
944960
 def test_collect_status_snapshot_surfaces_planned_verification(
@@ -964,6 +980,9 @@ def test_collect_status_snapshot_surfaces_planned_verification(
964980
     assert [item.detail for item in snapshot.recent_verification] == [
965981
         "write changed src/loader/runtime/tool_batches.py"
966982
     ]
983
+    assert snapshot.verification_state_summary == (
984
+        "planned (attempt 3) for uv run pytest -q"
985
+    )
967986
 
968987
 
969988
 def test_collect_status_snapshot_surfaces_stale_verification(
@@ -991,6 +1010,9 @@ def test_collect_status_snapshot_surfaces_stale_verification(
9911010
     assert [item.detail for item in snapshot.recent_verification] == [
9921011
         "write changed src/loader/runtime/finalization.py"
9931012
     ]
1013
+    assert snapshot.verification_state_summary == (
1014
+        "stale (attempt 1 -> attempt 2) for uv run pytest -q"
1015
+    )
9941016
 
9951017
 
9961018
 def test_collect_prompt_diff_uses_persisted_prompt_history(temp_dir: Path) -> None:
@@ -1051,7 +1073,9 @@ def test_status_and_session_commands_render_persisted_state(
10511073
     assert session_id in status_result.output
10521074
     assert "fixing" in status_result.output
10531075
     assert "Runtime Owner" in status_result.output
1076
+    assert "Boundary" in status_result.output
10541077
     assert "runtime-handle (RuntimeHandle)" in status_result.output
1078
+    assert "runtime-first via runtime-handle (RuntimeHandle)" in status_result.output
10551079
     assert "1 allow / 2 deny / 1 ask" in status_result.output
10561080
     assert "native" in status_result.output
10571081
     assert "Runtime Config, Workflow Context, Mode Guidance" in status_result.output
@@ -1066,11 +1090,15 @@ def test_status_and_session_commands_render_persisted_state(
10661090
     assert "What file did you mention?" in status_result.output
10671091
     assert "pytest -q" in status_result.output
10681092
     assert "1 failed" in status_result.output
1093
+    assert "Verification State" in status_result.output
1094
+    assert "failed for pytest -q" in status_result.output
10691095
 
10701096
     assert list_result.exit_code == 0
10711097
     assert session_id in list_result.output
10721098
     assert "Runtime Owner" in list_result.output
1099
+    assert "Boundary" in list_result.output
10731100
     assert "runtime-handle (RuntimeHandle)" in list_result.output
1101
+    assert "runtime-first via runtime-handle (RuntimeHandle)" in list_result.output
10741102
     assert "1 allow / 2 deny / 1 ask" in list_result.output
10751103
     assert "prompting enabled" in list_result.output
10761104
     assert "native" in list_result.output
@@ -1082,7 +1110,9 @@ def test_status_and_session_commands_render_persisted_state(
10821110
     assert show_result.exit_code == 0
10831111
     assert session_id in show_result.output
10841112
     assert "Runtime Owner" in show_result.output
1113
+    assert "Boundary" in show_result.output
10851114
     assert "runtime-handle (RuntimeHandle)" in show_result.output
1115
+    assert "runtime-first via runtime-handle (RuntimeHandle)" in show_result.output
10861116
     assert "Patch the broken parser" in show_result.output
10871117
     assert "1 allow / 2 deny / 1 ask" in show_result.output
10881118
     assert "enabled" in show_result.output
@@ -1092,6 +1122,8 @@ def test_status_and_session_commands_render_persisted_state(
10921122
     assert "Completion Decision" in show_result.output
10931123
     assert "Completion Trace" in show_result.output
10941124
     assert "Recent Verification" in show_result.output
1125
+    assert "Verification State" in show_result.output
1126
+    assert "failed for pytest -q" in show_result.output
10951127
     assert "continuation_check" in show_result.output
10961128
     assert "completion -> finalize" in show_result.output
10971129
     assert "Finalizing completed turn" in show_result.output
@@ -1107,7 +1139,11 @@ def test_status_and_session_commands_render_persisted_state(
11071139
     assert "Workflow Timeline" in workflow_result.output
11081140
     assert session_id in workflow_result.output
11091141
     assert "Runtime Owner" in workflow_result.output
1142
+    assert "Boundary" in workflow_result.output
11101143
     assert "runtime-handle (RuntimeHandle)" in workflow_result.output
1144
+    assert "runtime-first via runtime-handle (RuntimeHandle)" in workflow_result.output
1145
+    assert "Verification State" in workflow_result.output
1146
+    assert "failed for pytest -q" in workflow_result.output
11111147
     assert "handoff" in workflow_result.output
11121148
     assert "next=verify" in workflow_result.output
11131149
 
@@ -1170,6 +1206,8 @@ def test_workflow_command_renders_stale_verification_context(
11701206
     assert "verification_stale" in result.output
11711207
     assert "policy-outcome=stale" in result.output
11721208
     assert "Observed Verification" in result.output
1209
+    assert "Verification State" in result.output
1210
+    assert "stale (attempt 1 -> attempt 2) for uv run pytest -q" in result.output
11731211
     assert "uv run pytest -q" in result.output
11741212
     assert "new mutating work" in result.output
11751213
 
@@ -1193,6 +1231,8 @@ def test_workflow_command_renders_planned_verification_context(
11931231
     assert "verification_planned" in result.output
11941232
     assert "policy-outcome=planned" in result.output
11951233
     assert "Observed Verification" in result.output
1234
+    assert "Verification State" in result.output
1235
+    assert "planned (attempt 3) for uv run pytest -q" in result.output
11961236
     assert "verification planned for `uv run pytest -q`" in result.output
11971237
     assert "uv run pytest -q" in result.output
11981238