@@ -613,8 +613,12 @@ def GetWorkItems(self, request: pb.GetWorkItemsRequest, context: grpc.ServicerCo
613613 instance .pending_events .clear ()
614614
615615 # Add OrchestratorStarted for re-dispatches so that
616- # ctx.current_utc_datetime advances correctly
617- if instance .history :
616+ # ctx.current_utc_datetime advances correctly, unless
617+ # the pending batch already begins with one (e.g. a
618+ # post-rewind replay supplies its own).
619+ if instance .history and not (
620+ instance .dispatched_events
621+ and instance .dispatched_events [0 ].HasField ("orchestratorStarted" )):
618622 now = datetime .now (timezone .utc )
619623 orch_started = helpers .new_orchestrator_started_event (now )
620624 instance .dispatched_events .insert (0 , orch_started )
@@ -1901,10 +1905,15 @@ def _prepare_rewind(self, instance: OrchestrationInstance,
19011905 # Clear any stale dispatched events.
19021906 instance .dispatched_events .clear ()
19031907
1904- # Add the ExecutionRewound event as a new pending event.
1908+ # Add the ExecutionRewound event as a new pending event. Stamp it
1909+ # with the current time (rather than the default epoch) so the
1910+ # audit event carries a meaningful timestamp, consistent with
1911+ # other backend-generated events.
1912+ rewind_timestamp = timestamp_pb2 .Timestamp ()
1913+ rewind_timestamp .FromDatetime (datetime .now (timezone .utc ))
19051914 rewind_event = pb .HistoryEvent (
19061915 eventId = - 1 ,
1907- timestamp = timestamp_pb2 . Timestamp () ,
1916+ timestamp = rewind_timestamp ,
19081917 executionRewound = pb .ExecutionRewoundEvent (
19091918 reason = wrappers_pb2 .StringValue (value = reason ) if reason else None ,
19101919 ),
@@ -1977,22 +1986,18 @@ def _process_rewind_orchestration_action(
19771986 instance .instance_id , sub_instance_id , task_id )
19781987
19791988 # Re-enqueue so the orchestration replays with the clean history.
1980- # The executionRewound event is added to pending_events so the
1981- # worker can see it in new_events; the worker uses the presence
1982- # of executionRewound in old_events (history) to distinguish
1983- # this normal post-rewind replay from the initial rewind
1984- # short-circuit. Note: we do NOT add orchestratorStarted here
1985- # because the work-item dispatch loop already inserts one when
1986- # the instance has non-empty history.
1987- rewind_event = None
1988- for event in new_history :
1989- if event .HasField ("executionRewound" ):
1990- rewind_event = event
1991- break
1989+ # The executionRewound event is already present in the clean
1990+ # history (it was kept by the SDK's rewind rewrite), so it must
1991+ # NOT be re-sent as a pending event — doing so would duplicate it
1992+ # in the instance history once the dispatched events are
1993+ # committed. A lone orchestratorStarted is enough to make the
1994+ # instance dispatchable for the post-rewind replay; the worker
1995+ # replays normally because executionCompleted is no longer in the
1996+ # history.
19921997 instance .pending_events .clear ()
19931998 instance .dispatched_events .clear ()
1994- if rewind_event is not None :
1995- instance . pending_events . append ( rewind_event )
1999+ instance . pending_events . append (
2000+ helpers . new_orchestrator_started_event ( datetime . now ( timezone . utc )) )
19962001 instance .completion_token = self ._next_completion_token
19972002 self ._next_completion_token += 1
19982003 self ._orchestration_in_flight .discard (instance .instance_id )
0 commit comments