Skip to content

fix(cloud): PAT-first PR tracking, and block agent launch on checkout - #5379

Open
Hendrixx-RE wants to merge 2 commits into
Untrivial-ai:mainfrom
Hendrixx-RE:fix/cloud-pr-tracking-and-workspace-checkout
Open

Hendrixx-RE wants to merge 2 commits into
Untrivial-ai:mainfrom
Hendrixx-RE:fix/cloud-pr-tracking-and-workspace-checkout

Conversation

@Hendrixx-RE

Copy link
Copy Markdown
Collaborator

Summary

Two confirmed, separate bugs found while diagnosing cloud worker failures reported against this deployment:

1. PR tracking failed with SCM_BROKER_UNAVAILABLE even with a valid PAT configured
workerRaisePullRequest and workerClaimPullRequest (cloud/internal/httpapi/worker_handlers.go) gated the entire handler on s.checkoutBroker == nil before ever attempting the configured user PAT — unlike their sibling workerCheckoutGrant/workerPushGrant, which correctly try the PAT first and only fall back to (or fail without) the broker. Their own inline comments ("PAT-first, mirroring...") described behavior the code didn't actually implement. On a deployment with no GitHub App configured (checkoutBroker stays nil), PR raise/claim — and by extension ao claim-pr, auto-invoked by the worker's gh pr create wrapper — always failed with a 503, regardless of PAT. Reordered both handlers to match the working PAT-first pattern.

2. Fresh worker sandboxes could start their first turn against an empty, uncloned workspace
ao-worker/main.go ran prepareWorkspace (git checkout) and startInteractiveAgent (the coding-agent process) in independent, unsynchronized goroutines. HoldAgentInputUntilWorkspaceReady was meant to prevent this, but it only holds terminal input for turns after the first — the session's initial prompt is baked directly into the agent's launch command (BuildInteractiveagentruntime.BuildLaunchCommand), so it never passed through that hold. A failed or slow checkout let the agent boot and start working in an empty workspace, with the checkout failure logged only worker-side and invisible to the session/user. Reproduced twice against this deployment. startInteractiveAgent now blocks on the checkout goroutine's result before building or launching the agent command.

Test plan

  • go build ./... (cloud module)
  • go vet ./... (cloud module)
  • go test ./... (cloud module) — all packages pass
  • New tests in worker_pat_write_test.go reproduce the exact reported bug: PAT works with checkoutBroker == nil (previously would 503); no-PAT/no-broker fails closed with SCM_BROKER_UNAVAILABLE instead of panicking, for both raise and claim

Two independent bugs:

1. workerRaisePullRequest and workerClaimPullRequest gated the whole
   handler on s.checkoutBroker == nil before ever trying the configured
   PAT, unlike their sibling workerCheckoutGrant/workerPushGrant (which
   correctly try the PAT first). On any deployment without a GitHub App
   configured, PR raise/claim always failed with SCM_BROKER_UNAVAILABLE
   even with a fully valid PAT wired up. Reorder both handlers to match
   the PAT-first pattern already used for checkout/push grants.

2. ao-worker's prepareWorkspace (repository checkout) and
   startInteractiveAgent (the coding-agent process) ran in independent,
   unsynchronized goroutines. The transport supervisor's
   HoldAgentInputUntilWorkspaceReady only holds terminal input for turns
   after the first — the session's initial prompt is baked directly into
   the agent's launch argv (BuildInteractive), so it was never subject to
   that hold. A failed or slow checkout let the agent start its first
   turn against an empty workspace with no signal anywhere that checkout
   had failed (only a worker-side log line). startInteractiveAgent now
   blocks on the checkout goroutine's result before building/launching
   the agent command.
… string

The pull-request bridge's push-grant and raise-pull-request failure
branches logged the real error (which already carries the control
plane's actual HTTP status and response body, via client.doMethod's
"%s returned %d: %s" wrap) but returned an identical generic string to
the agent regardless of cause. Every failure mode -- broker
unavailable, GitHub API rejection, auth failure, a genuine outage --
looked exactly like every other one from inside the sandbox, making
it impossible to tell from a worker transcript alone whether a given
502 was the SCM_BROKER_UNAVAILABLE bug, a GitHub-side rejection, or
something else. Include err.Error() in the response, matching the
push-failure branch which already did this.
@Hendrixx-RE
Hendrixx-RE marked this pull request as draft September 14, 2026 16:19
@Hendrixx-RE
Hendrixx-RE marked this pull request as ready for review September 14, 2026 17:47
@i-trytoohard i-trytoohard added bug Something isn't working comp/daemon Go daemon, process lifecycle, and backend control plane. labels Sep 16, 2026
@i-trytoohard i-trytoohard added this to the Reliability milestone Sep 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working comp/daemon Go daemon, process lifecycle, and backend control plane.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants