@@ -268,7 +268,7 @@ def test_scratchbook_adapter_write_and_read_state_roundtrip(tmp_path: Path) -> N
268268 from examples .e2e .plan_and_task .state_models import RuntimeState
269269
270270 adapter = PlanTaskScratchbookAdapter (base_dir = tmp_path , workflow_id = "wf-rt" )
271- now = datetime .datetime .utcnow ( ).isoformat ()
271+ now = datetime .datetime .now ( datetime . UTC ).isoformat ()
272272 state = RuntimeState (
273273 workflow_id = "wf-rt" ,
274274 phase = "DRAFT_INTERVIEW" ,
@@ -303,7 +303,7 @@ def test_scratchbook_adapter_write_review_verdict_creates_file(tmp_path: Path) -
303303 verdict = ReviewVerdict (
304304 phase = "DRAFT_ADVISOR_REVIEW" ,
305305 verdict = "approved" ,
306- decided_at = datetime .datetime .utcnow ( ).isoformat (),
306+ decided_at = datetime .datetime .now ( datetime . UTC ).isoformat (),
307307 )
308308 path_str = adapter .write_review_verdict ("DRAFT_ADVISOR_REVIEW" , verdict )
309309 assert path_str
@@ -2662,7 +2662,7 @@ async def test_plan_resume_handler_restores_state_from_disk(tmp_path: Path) -> N
26622662
26632663 workflow_id = "resume-test-workflow"
26642664 adapter = PlanTaskScratchbookAdapter (base_dir = tmp_path , workflow_id = workflow_id )
2665- now = datetime .datetime .utcnow ( ).isoformat ()
2665+ now = datetime .datetime .now ( datetime . UTC ).isoformat ()
26662666 persisted = RuntimeState (
26672667 workflow_id = workflow_id ,
26682668 phase = "TASK_BLOCKED" ,
@@ -2759,7 +2759,7 @@ async def test_plan_resume_handler_marks_stale_subagents(tmp_path: Path) -> None
27592759
27602760 workflow_id = "stale-subagent-workflow"
27612761 adapter = PlanTaskScratchbookAdapter (base_dir = tmp_path , workflow_id = workflow_id )
2762- now = datetime .datetime .utcnow ( ).isoformat ()
2762+ now = datetime .datetime .now ( datetime . UTC ).isoformat ()
27632763 persisted = RuntimeState (
27642764 workflow_id = workflow_id ,
27652765 phase = "TASK_RUNNING" ,
@@ -2819,7 +2819,7 @@ async def test_plan_resume_handler_updates_scratchbook_prompt_config(
28192819
28202820 workflow_id = "scratchbook-config-workflow"
28212821 adapter = PlanTaskScratchbookAdapter (base_dir = tmp_path , workflow_id = workflow_id )
2822- now = datetime .datetime .utcnow ( ).isoformat ()
2822+ now = datetime .datetime .now ( datetime . UTC ).isoformat ()
28232823 persisted = RuntimeState (
28242824 workflow_id = workflow_id ,
28252825 phase = "DRAFT_INTERVIEW" ,
@@ -2873,7 +2873,7 @@ def test_read_state_planning_phase_does_not_require_workflow_plan(
28732873 adapter .plan_dir .mkdir (parents = True , exist_ok = True )
28742874 (adapter .plan_dir / "draft.md" ).write_text ("# Draft\n " , encoding = "utf-8" )
28752875
2876- now = datetime .datetime .utcnow ( ).isoformat ()
2876+ now = datetime .datetime .now ( datetime . UTC ).isoformat ()
28772877 state = RuntimeState (
28782878 workflow_id = workflow_id ,
28792879 phase = phase ,
@@ -2908,7 +2908,7 @@ def test_read_state_task_execution_phase_requires_active_plan_file(
29082908 adapter = PlanTaskScratchbookAdapter (base_dir = tmp_path , workflow_id = workflow_id )
29092909 # Do NOT write workflow_plan.md
29102910
2911- now = datetime .datetime .utcnow ( ).isoformat ()
2911+ now = datetime .datetime .now ( datetime . UTC ).isoformat ()
29122912 state = RuntimeState (
29132913 workflow_id = workflow_id ,
29142914 phase = "TASK_BLOCKED" ,
@@ -2950,7 +2950,7 @@ async def test_plan_resume_handler_restores_planning_phase(tmp_path: Path) -> No
29502950 (adapter .plan_dir / "draft.md" ).write_text ("# Draft\n " , encoding = "utf-8" )
29512951 # Intentionally do NOT create workflow_plan.md
29522952
2953- now = datetime .datetime .utcnow ( ).isoformat ()
2953+ now = datetime .datetime .now ( datetime . UTC ).isoformat ()
29542954 persisted = RuntimeState (
29552955 workflow_id = workflow_id ,
29562956 phase = "DRAFT_ADVISOR_REVIEW" ,
@@ -2998,7 +2998,7 @@ def test_require_plan_artifact_skipped_for_planning_phases(tmp_path: Path) -> No
29982998 adapter .plan_dir .mkdir (parents = True , exist_ok = True )
29992999 (adapter .plan_dir / "draft.md" ).write_text ("# Draft\n " , encoding = "utf-8" )
30003000
3001- now = datetime .datetime .utcnow ( ).isoformat ()
3001+ now = datetime .datetime .now ( datetime . UTC ).isoformat ()
30023002 state = RuntimeState (
30033003 workflow_id = workflow_id ,
30043004 phase = phase ,
@@ -3510,6 +3510,44 @@ def test_provider_config_enable_store_can_be_set_true() -> None:
35103510 assert config .enable_store is True
35113511
35123512
3513+ def test_plan_controller_utcnow_isoformat_is_timezone_aware () -> None :
3514+ import datetime
3515+
3516+ controller = PlanController ()
3517+
3518+ value = controller ._utcnow_isoformat () # type: ignore[attr-defined]
3519+ parsed = datetime .datetime .fromisoformat (value )
3520+
3521+ assert parsed .tzinfo is not None
3522+ assert parsed .utcoffset () == datetime .timedelta (0 )
3523+
3524+
3525+ def test_task_exec_utcnow_isoformat_is_timezone_aware () -> None :
3526+ import datetime
3527+ from examples .e2e .plan_and_task .task_exec import TaskExec
3528+
3529+ executor = TaskExec (state = _make_runtime_state ())
3530+
3531+ value = executor ._utcnow_isoformat () # type: ignore[attr-defined]
3532+ parsed = datetime .datetime .fromisoformat (value )
3533+
3534+ assert parsed .tzinfo is not None
3535+ assert parsed .utcoffset () == datetime .timedelta (0 )
3536+
3537+
3538+ def test_workflow_state_machine_utcnow_isoformat_is_timezone_aware () -> None :
3539+ import datetime
3540+ from examples .e2e .plan_and_task .state_machine import WorkflowStateMachine
3541+
3542+ machine = WorkflowStateMachine ()
3543+
3544+ value = machine ._utcnow_isoformat () # type: ignore[attr-defined]
3545+ parsed = datetime .datetime .fromisoformat (value )
3546+
3547+ assert parsed .tzinfo is not None
3548+ assert parsed .utcoffset () == datetime .timedelta (0 )
3549+
3550+
35133551def _make_state_at_phase (phase : str ) -> RuntimeState :
35143552 return RuntimeState (
35153553 workflow_id = "test-wf" ,
0 commit comments