Skip to content

Commit 121a325

Browse files
fix(pr-reengagement): evict no-PR completed issues to stop log flood
state.completed only ever shed an issue when PrReengagement dispatched a re-engagement, so any completed issue that never re-engages sat in the set until orchestrator restart. PrReengagement re-scans the whole set every poll cycle, emitting skip_no_pr / skip_no_critical per lingering issue — ~2880 events/day each, the dominant source of decision-log flood. A completed issue with no PR url can never produce a review to re-engage on, so this evicts it from state.completed on the no-PR branch instead of re-scanning it forever. If it is later re-dispatched and opens a PR, the normal completion path re-adds it. Scoped to the no-PR case only. The skip_no_critical path (PR present, no critical review yet) is left intact because a review can still arrive minutes after the agent exits — the exact late-review window PrReengagement exists to catch (SYM-16). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent d3c141d commit 121a325

2 files changed

Lines changed: 15 additions & 5 deletions

File tree

elixir/lib/symphony_elixir/orchestrator/pr_reengagement.ex

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,13 @@ defmodule SymphonyElixir.Orchestrator.PrReengagement do
9898
identifier: issue.identifier
9999
})
100100

101-
state
101+
# A completed issue with no PR can never produce a review to
102+
# re-engage on, so evict it from `state.completed` instead of
103+
# re-scanning it every poll cycle — the dominant source of the
104+
# decision-log flood (skip_no_pr fired ~2880x/day per lingering
105+
# issue). If it is later re-dispatched and opens a PR, the normal
106+
# completion path re-adds it. Safe: no PR means nothing to lose.
107+
%{state | completed: MapSet.delete(state.completed, issue.id)}
102108

103109
pr_url ->
104110
detector_fn = Map.fetch!(opts, :detector_fn)

elixir/test/symphony_elixir/orchestrator/pr_reengagement_test.exs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ defmodule SymphonyElixir.Orchestrator.PrReengagementTest do
355355
refute_receive :commented, 50
356356
end
357357

358-
test "no-op when fetched issue has no PR url (defensive)" do
358+
test "evicts a completed issue that has no PR url" do
359359
parent = self()
360360

361361
issue_no_pr = %Issue{
@@ -385,8 +385,11 @@ defmodule SymphonyElixir.Orchestrator.PrReengagementTest do
385385
end
386386
})
387387

388-
assert ^state = PrReengagement.run(state, opts)
388+
new_state = PrReengagement.run(state, opts)
389389

390+
# No PR means nothing to re-engage on, so the issue is dropped from
391+
# state.completed and will not be re-scanned every poll cycle.
392+
refute MapSet.member?(new_state.completed, "i-no-pr")
390393
refute_receive :detector_called, 50
391394
refute_receive :transitioned, 50
392395
refute_receive :commented, 50
@@ -422,7 +425,7 @@ defmodule SymphonyElixir.Orchestrator.PrReengagementTest do
422425
refute_receive :commented, 50
423426
end
424427

425-
test "no-op when fetched issue carries repos but none have a PR yet (defensive flat_map fallback)" do
428+
test "evicts a completed issue whose repos carry no PR yet (defensive flat_map fallback)" do
426429
parent = self()
427430

428431
issue_repo_no_pr = %Issue{
@@ -451,8 +454,9 @@ defmodule SymphonyElixir.Orchestrator.PrReengagementTest do
451454
end
452455
})
453456

454-
assert ^state = PrReengagement.run(state, opts)
457+
new_state = PrReengagement.run(state, opts)
455458

459+
refute MapSet.member?(new_state.completed, "i-pre-pr")
456460
refute_receive :detector_called, 50
457461
refute_receive :transitioned, 50
458462
refute_receive :commented, 50

0 commit comments

Comments
 (0)