mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
[F062] work_session.merge_pr: idempotency + active-status guard
merge_pr unconditionally set pr_status=merged, pr_merged_at, merged_by, status=COMPLETED on whatever session it loaded — the only session-terminal transition in WorkSessionService lacking both the active-status guard (complete/abandon) and the terminal-idempotency guard (close). Two failure modes: (1) a retried merge after a successful-but-unconfirmed GitHub merge overwrote merged_by/pr_merged_at with the retry's actor/timestamp, corrupting the merge audit trail; (2) merge_pr on an ABANDONED session resurrected it to COMPLETED, undoing the single-active abandonment. Mirrors close()'s guard: if status != ACTIVE, return the session unchanged. Both git.py callers await merge_pr and discard the return, so the no-op is safe. TDD: 3 tests (happy-path + both modes).
This commit is contained in:
@@ -506,6 +506,17 @@ class WorkSessionService(BaseService):
|
||||
if not work_session:
|
||||
return None
|
||||
|
||||
# Idempotency + active-guard (F062), mirroring close(): a session that is
|
||||
# already terminal (COMPLETED — a retried merge after a
|
||||
# successful-but-unconfirmed GitHub merge; or ABANDONED — a superseded
|
||||
# session) is returned unchanged. Without this, a retry overwrote
|
||||
# merged_by / pr_merged_at with the retry's actor/timestamp (corrupting
|
||||
# the merge audit trail) and an ABANDONED session was resurrected to
|
||||
# COMPLETED (undoing the single-active abandonment). Not a warning
|
||||
# because the caller may legitimately double-call on retry/idempotency.
|
||||
if work_session.status != WorkSessionStatus.ACTIVE:
|
||||
return work_session
|
||||
|
||||
work_session.pr_status = "merged"
|
||||
work_session.pr_merged_at = datetime.now(UTC)
|
||||
work_session.merged_by = cast("Any", merged_by)
|
||||
|
||||
Reference in New Issue
Block a user