Skip to content

Poll the PR through observe_pr's retry policy instead of a workflow timer - #45

Merged
krokicki merged 2 commits into
mainfrom
worktree-poll-via-retry
Oct 13, 2026
Merged

krokicki merged 2 commits into
mainfrom
worktree-poll-via-retry

Conversation

@krokicki

Copy link
Copy Markdown
Member

Problem

A quiet PR added an observe_pr activity and a timer to the history every poll_minutes, drawing a staircase on the Temporal UI timeline that hid the real work (the agent rounds, merges and escalations).

Change

Temporal's "infrequent polling" pattern: observe_pr fails (retryably) while decide() would WAIT, and the workflow calls it once with a fixed-interval retry policy. Retries add no history events, so the whole wait is one activity row on the timeline. A revision round still shows as its own cluster of activities between two long observe bars.

  • ObserveInput carries the decision context (handled, revisions, max_revisions, escalated) so the activity can run decide() itself.
  • decide() treats an escalation already raised for the same reason and head as WAIT; IssueInput.escalated records it, so an escalated PR polls quietly instead of hot-looping on the same escalation.
  • A failed merge still sleeps one poll interval before re-observing, so GitHub is not hammered when a PR stays approved but unmergeable. That is now the only timer in the loop.
  • The old loop stays behind workflow.patched("observe-by-retry") so in-flight runs replay. Those runs get an unbounded retry budget on observe_pr so they do not fail after six quiet hours. Drop the old branch once pre-change runs have rolled over.

Only IssueWorkflow polled with a timer; PollWorkflow and the mia_data workflows are unaffected.

Tests

  • New workflow test: an escalation is commented once and then waits (fails if the gate is removed).
  • New decide test for the escalation gate.
  • Activity tests that inspected a WAIT snapshot now assert the retryable error; the ETag cache test calls the snapshot helper directly.
  • The continue-as-new test caps at one poll per execution so it still exercises the rollover.
  • The recorded-history replay test passes unchanged, confirming in-flight runs keep replaying.

330 passed, ruff clean.

Behaviour notes

A transient GitHub error during a poll now waits a full poll interval before retrying instead of backing off from one second. Acceptable at a two-minute poll.

🤖 Generated with Claude Code

krokicki and others added 2 commits October 13, 2026 14:41
…imer

A quiet PR used to add an observe_pr activity and a timer to the history
every poll_minutes, drawing a staircase on the Temporal UI timeline that hid
the real work. Now observe_pr fails (retryably) while decide() would WAIT and
the workflow calls it once with a fixed-interval retry policy, so the whole
wait is one activity row and retries add no events (Temporal's "infrequent
polling" pattern).

- ObserveInput carries the decision context (handled, revisions,
  max_revisions, escalated) so the activity can run decide() itself.
- decide() treats an escalation already raised for the same reason and head
  as WAIT; IssueInput.escalated records it, so an escalated PR polls quietly
  instead of re-commenting every round.
- The old loop stays behind workflow.patched("observe-by-retry") so in-flight
  runs replay; drop it once they have rolled over.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@krokicki
krokicki merged commit 3a69893 into main Oct 13, 2026
1 check passed
@krokicki
krokicki deleted the worktree-poll-via-retry branch October 13, 2026 18:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant