Skip to content
Merged
20 changes: 16 additions & 4 deletions .github/workflows/autopilot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,12 @@ name: Autopilot
# dropped: one round, within the same cap, only when a
# check failed in its last merge group, from the failed
# jobs' log (`autopilot.py queue-failed`, .dx-io/queue.md).
# Called too when a gate of the pipeline failed at the
# head, or the review ended without a verdict (#741): a
# check round, within the same cap, from the failed jobs'
# log of the calling run (`autopilot.py checks-failed`,
# .dx-io/checks.md); a review that blocks with its verdict
# is a review round, from its threads.
#
# The agent holds no key (#287). A shell in a job that holds a secret can
# read it out of the runner's memory, whatever the token's scope, so the
Expand Down Expand Up @@ -612,9 +618,12 @@ jobs:
max=$(python3 .dx-central/scripts/autonomy.py policy | jq -r '."autopilot-rounds" // 3')
if [ "$ROUND" -gt "$max" ]; then
gh pr edit "$PR" --repo "$GITHUB_REPOSITORY" --add-label needs-human >/dev/null || true
gh pr comment "$PR" --repo "$GITHUB_REPOSITORY" --body "The autopilot spent its $max rounds and the review still blocks: a person takes it from here. The review's open threads say what is left.

🤖 AI-generated · Claude Opus 5.5 (Anthropic)" >/dev/null
# #741: on a check round (a gate that failed at the head, a
# review with no verdict, the merge queue's checks) the
# comment says its checks still fail, not that the review blocks.
checks=true
[ "${QUEUE:-}" = true ] || python3 .dx-central/scripts/autopilot.py checks-failed >/dev/null || checks=false
gh pr comment "$PR" --repo "$GITHUB_REPOSITORY" --body "$(MAX="$max" CHECKS="$checks" python3 .dx-central/scripts/autopilot.py rounds-spent)" >/dev/null
[ -z "${ISSUE:-}" ] || [ -z "${OPEN_TO_CLAIM:-}" ] || gh issue comment "$ISSUE" --repo "$GITHUB_REPOSITORY" --body "${OPEN_TO_CLAIM:-}" >/dev/null || true
echo "go=false" >> "$GITHUB_OUTPUT"; exit 0
fi
Expand Down Expand Up @@ -707,7 +716,10 @@ jobs:
# The default branch as GitHub says it, whatever the event (#585).
BASE: ${{ steps.when.outputs.default }}
# #479: a round for a pull request the queue dropped reads the
# failed jobs' log instead of the review.
# failed jobs' log instead of the review; so does one for a gate
# of the pipeline that failed at the head, or a review that ended
# without a verdict (#741: `inputs fix` reads the calling run's
# jobs, and writes checks.md, which `prompt fix` points at).
QUEUE: ${{ github.event_name == 'pull_request' && github.event.action == 'dequeued' }}
RUN_URL: ${{ format('{0}/{1}/actions/runs/{2}', github.server_url, github.repository, github.run_id) }}
run: |
Expand Down
17 changes: 11 additions & 6 deletions .github/workflows/org-pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -148,13 +148,18 @@ jobs:

# An autopilot pull request whose review blocks: one round of fixes by the
# autopilot (#83), which pushes and so runs this pipeline again; after its
# rounds it hands the pull request to a person.
# rounds it hands the pull request to a person. So does one whose
# conventions fail, or whose review ends without a verdict (#741): a round
# from the failed jobs' log.
autopilot-fix:
needs: review
# Only dilux-bot's own pull requests, and only for a new commit (an edit
# of the description keeps the review's verdict and must not spend a
# round).
if: always() && github.event_name == 'pull_request' && github.repository != 'DiluxOne/.github' && needs.review.outputs.blocking == 'true' && contains(github.event.pull_request.labels.*.name, 'autopilot') && github.event.pull_request.user.login == vars.DX_BOT_LOGIN && github.event.action != 'edited'
# #741: after the conventions too, so their failure (the review is then
# skipped) starts a round; and on a review that failed before its
# verdict (no outputs) as on one that blocks.
needs: [conventions, review]
# Only dilux-bot's own pull requests, only once ready (a draft's own run
# checks its work), and only for a new commit (an edit of the
# description keeps the review's verdict and must not spend a round).
if: always() && github.event_name == 'pull_request' && github.repository != 'DiluxOne/.github' && (needs.review.outputs.blocking == 'true' || needs.review.result == 'failure' || needs.conventions.result == 'failure') && !github.event.pull_request.draft && contains(github.event.pull_request.labels.*.name, 'autopilot') && github.event.pull_request.user.login == vars.DX_BOT_LOGIN && github.event.action != 'edited'
# The agent's jobs read the repository with the job's own token (#287).
permissions:
contents: read
Expand Down
22 changes: 14 additions & 8 deletions .github/workflows/pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ name: Pull request
# rewrite the rules that review it. The conventions script comes from the pull
# request itself: it can change conventions.yml anyway. An autopilot pull
# request whose review blocks gets its fix round here (autopilot-fix, #504),
# as org-pull-request.yml gives it everywhere else. A merge into main
# as org-pull-request.yml gives it everywhere else, and so does one whose
# gate fails or whose review ends without a verdict (#741). A merge into main
# brings the open autopilot pull requests in step with it from issues.yml's
# `autopilot-sweep` job, here as in every repository (#561, #669).
# After the review, whom the pull request waits on goes on its board, with
Expand Down Expand Up @@ -276,18 +277,23 @@ jobs:
enabled: ${{ needs.review.outputs.auto-merge }}
secrets: inherit

# An autopilot pull request here whose review blocks: one round of fixes
# (#504), as org-pull-request.yml runs it in every other repository, with
# An autopilot pull request here whose review blocks, or one of whose
# gates fails (#741): one round of fixes (#504), as org-pull-request.yml runs it in every other repository, with
# the same condition but the repository (that one excludes this one, so
# the round never runs twice; scripts/autopilot.py --test keeps the two
# the same). Only dilux-bot's own branch matches, never a fork's. The
# scripts are read from main, like the review's.
autopilot-fix:
needs: review
# Only dilux-bot's own pull requests, and only for a new commit (an edit
# of the description keeps the review's verdict and must not spend a
# round).
if: always() && github.event_name == 'pull_request' && github.repository == 'DiluxOne/.github' && needs.review.outputs.blocking == 'true' && contains(github.event.pull_request.labels.*.name, 'autopilot') && github.event.pull_request.user.login == vars.DX_BOT_LOGIN && github.event.action != 'edited'
# #741: after every gate the review waits on, so a gate that fails (the
# review is then skipped) starts a check round too, from the failed
# jobs' log, rather than leave the pull request to stall; so does a
# review that ended without a verdict, whether its verdict step said so
# (blocking, "did not finish") or the job died before it (no outputs).
needs: [conventions, actionlint, scripts, review]
# Only dilux-bot's own pull requests, only once ready (a draft's own run
# checks its work), and only for a new commit (an edit of the
# description keeps the review's verdict and must not spend a round).
if: always() && github.event_name == 'pull_request' && github.repository == 'DiluxOne/.github' && (needs.review.outputs.blocking == 'true' || needs.review.result == 'failure' || needs.conventions.result == 'failure' || needs.actionlint.result == 'failure' || needs.scripts.result == 'failure') && !github.event.pull_request.draft && contains(github.event.pull_request.labels.*.name, 'autopilot') && github.event.pull_request.user.login == vars.DX_BOT_LOGIN && github.event.action != 'edited'
# The agent's jobs read the repository with the job's own token (#287).
permissions:
contents: read
Expand Down
4 changes: 3 additions & 1 deletion docs/pull-requests.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,9 @@ The path every change takes in a DiluxOne repository, step by step. The short ve
failed before; the autopilot ran it only in the sandbox, where it must
pass before the pull request is ready, and the pull request's own checks
run it too); it is labelled `merged-by:policy`. While the review blocks,
the autopilot fixes and pushes, up to five rounds, then hands it to a
or a gate of the pipeline fails or the review ends without a verdict
(#741: a round from the failed jobs' log), the autopilot fixes and
pushes, up to five rounds, then hands it to a
person (`needs-human`). A sync run's pull request (lane `org-sync`)
merges by policy too ([`scripts/autonomy.py`](../scripts/autonomy.py)
`lane-merge`) when the lane accepts its issue and every file it changes
Expand Down
Loading
Loading