From 60403b0c8fee61be900cfdea987810b7665ceb4f Mon Sep 17 00:00:00 2001 From: tend-agent <270458913+tend-agent@users.noreply.github.com> Date: Thu, 6 Aug 2026 08:56:01 +0000 Subject: [PATCH 1/3] fix(running-in-ci): leave a review that lands mid-poll to tend-mention Closes #865 --- plugins/tend-ci-runner/skills/nightly/SKILL.md | 2 +- plugins/tend-ci-runner/skills/running-in-ci/SKILL.md | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/plugins/tend-ci-runner/skills/nightly/SKILL.md b/plugins/tend-ci-runner/skills/nightly/SKILL.md index 101cf5f7..2368473b 100644 --- a/plugins/tend-ci-runner/skills/nightly/SKILL.md +++ b/plugins/tend-ci-runner/skills/nightly/SKILL.md @@ -320,7 +320,7 @@ The default action is a PR, not an issue. If there's a plausible fix, make it For each finding: -1. **Create a PR** — branch, fix, run full test suite, commit, push, create PR, poll CI. **Every bug fix must include a regression test that would have failed before the fix.** If a test is not feasible (e.g., pure documentation changes), note why in the PR description. When uncertain about the approach, explain the trade-offs in the description. +1. **Create a PR** — branch, fix, run full test suite, commit, push, create PR, then poll CI per **CI Monitoring** in `/tend-ci-runner:running-in-ci`. Your job ends when those checks are terminal: a review posted on the PR while you poll belongs to `tend-mention`. **Every bug fix must include a regression test that would have failed before the fix.** If a test is not feasible (e.g., pure documentation changes), note why in the PR description. When uncertain about the approach, explain the trade-offs in the description. 2. **Create an issue only when there's no obvious fix** — design questions, problems needing maintainer input, or findings requiring investigation beyond what the survey can provide. ## Optional steps diff --git a/plugins/tend-ci-runner/skills/running-in-ci/SKILL.md b/plugins/tend-ci-runner/skills/running-in-ci/SKILL.md index b3f01a83..ecf07284 100644 --- a/plugins/tend-ci-runner/skills/running-in-ci/SKILL.md +++ b/plugins/tend-ci-runner/skills/running-in-ci/SKILL.md @@ -290,6 +290,12 @@ Invoke this Bash call with `timeout: 600000` (10 min). The default 2-min Bash ti 3. Once terminal, do the follow-up: ship a green fix, comment an unresolved failure, or dismiss your approval on red. 4. If the cap hits with checks still running, comment the still-pending checks as unverified before ending — don't exit as if done. +### A review that lands while you poll is not yours to action + +`tend-review` fires on any PR you open, so its review often arrives while you are still polling that PR's checks. Don't act on it. `tend-mention` is dispatched on `pull_request_review` for every PR the bot authored, and that dispatch runs whether or not you also respond — so a session that starts editing is racing a run already making the same edits and running the same suite. The loser only finds out at `git push`, discards its commit, and the whole fix-and-verify cycle is paid twice for one review. + +Poll your checks to terminal, do the follow-up you were gated on, and exit; name the outstanding review in your summary. This covers a review that arrives *while* you work — a session dispatched to answer a specific review owns that review and actions it normally. + Before dismissing local test failures as "pre-existing", check main branch CI: ```bash From 0dfde81d8ee5a286ea34cad781ca4feafe4519d0 Mon Sep 17 00:00:00 2001 From: tend-agent <270458913+tend-agent@users.noreply.github.com> Date: Tue, 11 Aug 2026 08:25:44 +0000 Subject: [PATCH 2/3] fix(running-in-ci): check the head SHA before the expensive verify, not just the push --- .../tend-ci-runner/skills/running-in-ci/SKILL.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/plugins/tend-ci-runner/skills/running-in-ci/SKILL.md b/plugins/tend-ci-runner/skills/running-in-ci/SKILL.md index ecf07284..eb435930 100644 --- a/plugins/tend-ci-runner/skills/running-in-ci/SKILL.md +++ b/plugins/tend-ci-runner/skills/running-in-ci/SKILL.md @@ -192,6 +192,19 @@ STATE=$(gh pr view --json state --jq '.state') If the PR is merged, the work is superseded. Comment if a real gap remains; do not push to the now-orphan branch. After merge, `gh pr view --json headRefOid` returns the SHA at merge time and never advances — polling it for a new push is a guaranteed deadlock. +### Re-check the head SHA before the expensive verify, not just before the push + +A PR another tend session opened keeps that session alive polling its checks, and closing a red gate is exactly the follow-up it stays alive for — so a sibling commit can land on the branch while you edit it. Find that out at `git push` and the suite you just ran was scoped against a stale head, so the whole verify cycle is paid again after the rebase. Record the head before editing and re-check it immediately before each expensive step — full test suite, coverage or snapshot regeneration, a long build: + +```bash +BASE_OID=$(gh pr view --json headRefOid --jq '.headRefOid') +# ...edits... +[ "$(gh pr view --json headRefOid --jq '.headRefOid')" = "$BASE_OID" ] \ + || echo "sibling pushed — fetch and re-scope before verifying" +``` + +If it moved, `git fetch` and read the new commits before verifying: drop whatever the sibling already landed, rebase what's left, and verify once against the new head. Expect the overlap rather than treating it as a surprise — a reviewer and a coverage gate reading the same new code ask for the same missing test. The runs API can't substitute for this check: a `schedule` or `repository_dispatch` run reports `head_branch: main`, not the branch it is editing, so a live sibling is invisible there. + ## Merging Upstream into PR Branches When asked to merge the default branch into a PR branch: From 6754fa6a894c7de216e4188970603f2cfa1681c2 Mon Sep 17 00:00:00 2001 From: tend-agent <270458913+tend-agent@users.noreply.github.com> Date: Tue, 11 Aug 2026 08:35:27 +0000 Subject: [PATCH 3/3] fix(running-in-ci): check state alongside the head SHA, restore section altitude MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two review fixes on the head-SHA subsection. The OID comparison alone passes on a merged or closed PR, since headRefOid freezes at its merge-time value — exactly the case the rule exists to avoid, so the expensive step ran on superseded work. Fetch `state` on the same call. Rename BASE_OID to HEAD_OID: the review skill uses BASE_SHA for baseRefOid, so a BASE_ name holding a head SHA reads as the wrong ref when copied. Move the "review that lands while you poll" subsection below the "pre-existing" paragraph — that paragraph is a general CI Monitoring rule and was reading as scoped to the new heading. --- .../skills/running-in-ci/SKILL.md | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/plugins/tend-ci-runner/skills/running-in-ci/SKILL.md b/plugins/tend-ci-runner/skills/running-in-ci/SKILL.md index eb435930..73720985 100644 --- a/plugins/tend-ci-runner/skills/running-in-ci/SKILL.md +++ b/plugins/tend-ci-runner/skills/running-in-ci/SKILL.md @@ -197,12 +197,15 @@ If the PR is merged, the work is superseded. Comment if a real gap remains; do n A PR another tend session opened keeps that session alive polling its checks, and closing a red gate is exactly the follow-up it stays alive for — so a sibling commit can land on the branch while you edit it. Find that out at `git push` and the suite you just ran was scoped against a stale head, so the whole verify cycle is paid again after the rebase. Record the head before editing and re-check it immediately before each expensive step — full test suite, coverage or snapshot regeneration, a long build: ```bash -BASE_OID=$(gh pr view --json headRefOid --jq '.headRefOid') +HEAD_OID=$(gh pr view --json headRefOid --jq '.headRefOid') # ...edits... -[ "$(gh pr view --json headRefOid --jq '.headRefOid')" = "$BASE_OID" ] \ - || echo "sibling pushed — fetch and re-scope before verifying" +read -r NOW_OID NOW_STATE < <(gh pr view --json headRefOid,state --jq '"\(.headRefOid) \(.state)"') +[ "$NOW_OID" = "$HEAD_OID" ] && [ "$NOW_STATE" = "OPEN" ] \ + || echo "sibling pushed or PR closed — fetch and re-scope before verifying" ``` +`state` rides along on the same call because a merged or closed PR freezes `headRefOid` at its merge-time value (see above) — the OID comparison alone passes and the expensive step runs on work that is already superseded. On a non-`OPEN` state, stop per the subsection above rather than re-scoping. + If it moved, `git fetch` and read the new commits before verifying: drop whatever the sibling already landed, rebase what's left, and verify once against the new head. Expect the overlap rather than treating it as a surprise — a reviewer and a coverage gate reading the same new code ask for the same missing test. The runs API can't substitute for this check: a `schedule` or `repository_dispatch` run reports `head_branch: main`, not the branch it is editing, so a live sibling is invisible there. ## Merging Upstream into PR Branches @@ -303,12 +306,6 @@ Invoke this Bash call with `timeout: 600000` (10 min). The default 2-min Bash ti 3. Once terminal, do the follow-up: ship a green fix, comment an unresolved failure, or dismiss your approval on red. 4. If the cap hits with checks still running, comment the still-pending checks as unverified before ending — don't exit as if done. -### A review that lands while you poll is not yours to action - -`tend-review` fires on any PR you open, so its review often arrives while you are still polling that PR's checks. Don't act on it. `tend-mention` is dispatched on `pull_request_review` for every PR the bot authored, and that dispatch runs whether or not you also respond — so a session that starts editing is racing a run already making the same edits and running the same suite. The loser only finds out at `git push`, discards its commit, and the whole fix-and-verify cycle is paid twice for one review. - -Poll your checks to terminal, do the follow-up you were gated on, and exit; name the outstanding review in your summary. This covers a review that arrives *while* you work — a session dispatched to answer a specific review owns that review and actions it normally. - Before dismissing local test failures as "pre-existing", check main branch CI: ```bash @@ -318,6 +315,12 @@ gh api "repos/{owner}/{repo}/actions/runs?branch=main&status=completed&per_page= If you cannot verify, say "I haven't confirmed whether these failures are pre-existing." +### A review that lands while you poll is not yours to action + +`tend-review` fires on any PR you open, so its review often arrives while you are still polling that PR's checks. Don't act on it. `tend-mention` is dispatched on `pull_request_review` for every PR the bot authored, and that dispatch runs whether or not you also respond — so a session that starts editing is racing a run already making the same edits and running the same suite. The loser only finds out at `git push`, discards its commit, and the whole fix-and-verify cycle is paid twice for one review. + +Poll your checks to terminal, do the follow-up you were gated on, and exit; name the outstanding review in your summary. This covers a review that arrives *while* you work — a session dispatched to answer a specific review owns that review and actions it normally. + ### Polling `gh run rerun --failed` After `gh run rerun --failed`, poll the rerun jobs directly. The parent run's `.status` stays `in_progress` until every sibling job finishes, including unrelated long-running ones, and the `pending()` recipe above also doesn't help — sibling check-runs on the head SHA still appear pending. Polling specific job IDs is the only fix.