Closed issues can still have labels; the old `if not labels` guard
never fired for issue #272, so the loop retried the no-op close every
minute and never reached the ToPlan/Ready issue logic.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Why State/Question appeared on issue #282: the loop waited 15 min for a
CI run on branch issue-282-fix, found none (ci.yml path filter did not
trigger for workflow-file-only changes), and set State/Question.
Fixes two related issues:
1. Add `_tea_get()` — a direct Codeberg API client using urllib. Refactor
`_latest_main_ci_run` and `_latest_ci_run_for_branch` to use it (the
latter now matches PR runs via `event_payload` instead of resolving the
PR number first). Also use it for the `mergeable` check in
`_handle_pr_still_open_after_merge`. This fixes 14 pre-existing test
failures that mocked `agent_loop._tea_get` but the function didn't exist.
2. Add `_merged_issue_prs()` and a catch-up scan in `_run_loop()`: on each
tick the loop now finds merged `issue-{N}-fix` PRs whose issues still
carry state labels and closes them. This handles the case where
State/Question was set (e.g., CI path-filter miss) but the PR was later
merged manually — the next cron tick will close the issue automatically.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>