Skip to content

[Bug]: watch_pull_request misses "checks passed" after a push on Bitbucket because the head SHA is never reported #16301

Description

@bman654

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Related but different: #15362 (fixed by #15804) covers a new required check that first appears already passed on the same head. Here the check name stays the same and a new commit is pushed.

Area

apps/server

Steps to reproduce

Seen on a Bitbucket Cloud pull request in a private repository. It has one required check, a Bitbucket Pipelines run that takes about 47 s.

Timeline (UTC, 2026-10-05):

Time Event
16:47:56 PR opened. The pipeline for the first commit started at 16:47:59 and passed after ~47 s.
~16:48–16:49 Agent called watch_pull_request while that run was still IN_PROGRESS. Soon after, the wake "All 1 check passed." arrived. ✅
~16:57 A reviewer approved and posted 7 comments. A wake listing them arrived. ✅
~17:03 The agent pushed a new commit to the source branch. Its pipeline started at 17:03:22 and passed after ~47 s (~17:04:09). ❌ No wake.
later (time unknown) The agent's session restarted.
01:46 (next day) The user noticed the PR and prompted the agent manually. Until then the PR sat open, approved and green.

Minimal repro:

  1. On a Bitbucket PR, call watch_pull_request and wait for the "checks passed" wake.
  2. Push a commit whose required pipeline finishes in less than one sweep interval.
  3. No "checks passed" wake arrives for the new commit.

Expected behavior

The tool description says T3 Code "wakes you with a message when a check fails, the required checks pass, someone else comments or reviews, or the branch starts to conflict with its base." After a push, the agent should be woken again when the required checks pass for the new commit. As far as I can tell from the code, that is what happens on hosts that report a head SHA.

Actual behavior

No wake arrived, and the watch didn't end (the PR stayed open and readable). The PR sat ready to merge and unattended for about 8.7 hours.

Likely cause (from reading the source; not confirmed with logs because the server traces for that window had rotated out):

  • evaluatePullRequestWatch (apps/server/src/orchestration-v2/pullRequestWatch.ts) resets passed only when the head moves (headMoved = headSha !== watch.headSha). After that, a second checks-passed needs passedNow && !passed, or on main the new gateGrew condition from fix(server): PR watch reports a required check that first appears already passed #15804.
  • The Bitbucket provider never reports headSha. RawBranchSchema in apps/server/src/pullRequest/bitbucketPullRequestJson.ts decodes only source.branch.name and source.repository, not source.commit.hash. toPullRequest and getChangeRequest don't set headSha either. ProviderChangeRequestDetail.headSha is optional ("where the host's detail read reports it").
  • So for Bitbucket, headSha is null on every pass and headMoved is always false. After the first "checks passed" wake, the watch only wakes again if some pass happens to see a required check pending or failed. A ~47 s run between 1-minute sweeps can easily go unseen. Main now sweeps every 2 minutes (fix(server): PR watches stop burning GitHub's rate limit and giving up #16208), which makes the window wider. gateGrew doesn't help because the check name doesn't change.
  • Unverified: if /pullrequests/{id}/statuses also returns the previous commit's status under the same key, the "later one wins" dedupe in decodeStatusesJson could hide the pending run even when a sweep does land during it.

Before reading the code, there were three candidate explanations: (1) the polling interval missed the in-progress state, (2) the wake was lost across the agent session restart, or (3) green → green isn't treated as a transition. The code points to (3) for Bitbucket specifically, with (1) explaining why the pending state wasn't seen. I can't rule out (2), but it isn't needed to explain this.

Asks:

  • Populate headSha for Bitbucket from the pull request's source.commit.hash, so a push resets the watch the same way it does for hosts that report the head. Any other provider without headSha would have the same gap.
  • Clarify in the watch_pull_request description whether "the required checks pass" wakes once per commit or only when the overall verdict changes, and that per-commit behavior depends on the host reporting the head commit.
  • Make sure wakes generated while the agent session is restarting are delivered afterwards, if that isn't already guaranteed.
  • To confirm: on Bitbucket, push a commit during a watch whose CI runs well beyond the sweep interval. If a green wake arrives, the pending state was observed, which fits the analysis above. If no wake arrives, the statuses dedupe is also involved.

Impact

Major degradation or frequent failure

Version or commit

macOS desktop nightly, a 0.0.46 build older than 0.0.46-nightly.20261005.2676; the updater was offering .2676 throughout the incident. The code references above are against current main. The headMoved reset and the Bitbucket decoder are the same in the code from #15057 that was running at the time.

Environment

macOS desktop app, Claude agent, Bitbucket Cloud PR (private repository) with a single required Bitbucket Pipelines check.

Logs or stack traces

No response (server traces covering the incident window had already rotated out.)

Screenshots, recordings, or supporting files

No response

Workaround

The reporter's agent skill now uses the PR watch only for comments, reviews and PR state, and watches CI results itself.

Activity

  1. juliusmarminge commented on Oct 6, 2026

    @juliusmarminge
    Member

    Note

    Grok responding on behalf of Julius.

    Triage

    Thanks @bman654 for tracing this all the way into the source. Your diagnosis of the missing head SHA holds up, and it looks distinct from what #15362 / #15804 addressed.

    What I found

    • evaluatePullRequestWatch treats a new head as a new check run: headMoved clears passed, so a later all-green result can wake again. PullRequestService only copies headSha when the provider sets it.
    • Bitbucket never sets it. RawBranchSchema decodes source.branch.name and source.repository, not source.commit.hash, and toChangeRequest doesn't set headSha. Every Bitbucket read has headSha === null, so headMoved is always false and the watch stays in the "already passed" state.
    • That fits your timeline. The first wake came from a real pending-to-passed transition because the watch started while the pipeline was IN_PROGRESS. The second commit's ~47s pipeline finished between sweeps, so the sweep only saw SUCCESSFUL under the same name and stayed silent. No lost wake or session restart is needed to explain the hours of silence; a produced wake is dispatched with queue_after_active, so a restart wouldn't drop it.
    • Two side notes that don't change the diagnosis: Bitbucket checks are never marked required (the "All 1 check passed." wording is the non-required path), so gateGrew from fix(server): PR watch reports a required check that first appears already passed #15804 can't fire here. And the statuses "later one wins" dedupe isn't needed to explain this incident, though the decoder does drop created_on and the commit link.
    • Current main sweeps every two minutes (fix(server): PR watches stop burning GitHub's rate limit and giving up #16208), which makes short pipelines easier to miss than on your build.
    • The same missing headSha appears on GitLab (sha / diff_refs.head_sha) and Forgejo (head.sha). Azure DevOps also omits it, and its detail read currently returns checks: [].

    Likely fix area

    • One option is mapping Bitbucket's source.commit.hash onto the detail headSha (optional, so payloads without it still decode), with watch tests covering a green-to-green push with a null head versus a changed head.
    • GitLab and Forgejo could get the same field.
    • The watch_pull_request description could then say "checks passed" fires once per head commit, when the host reports the head.

    A maintainer will decide on the fix direction.

  2. added
    bugSomething is broken or behaving incorrectly.
    via-triageFiled through npx t3 triage
    on Oct 6, 2026
  3. bman654 commented on Oct 6, 2026

    @bman654
    Author

    A second data point, from another Bitbucket Cloud pull request in the same private repository, on the same day and the same T3 Code build:

    • The watch started while the PR's checks were already green on its first commit.
    • The agent then pushed a new commit. That commit's pipeline sat PENDING for ~15s, then IN_PROGRESS, and finished SUCCESSFUL. From creation to completion it took about 62s, against ~47s for the run whose wake was missed.
    • This time a "All 1 check passed" wake did arrive for the new commit.

    That fits the reading in the issue. With no head SHA from the Bitbucket provider, the "checks passed" state only resets when a poll happens to see the run in a non-green state. Here a poll most likely landed while the run was pending or in progress, which cleared the passed state, so the next green produced a wake. In the original case the run was shorter than the poll interval and no poll saw it in progress, so the state stayed passed and no wake fired.

    So a missed green wake after a push depends on whether a poll lands during the run. Populating the head SHA for Bitbucket would make it deterministic.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething is broken or behaving incorrectly.via-triageFiled through npx t3 triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions