Repository navigation
Description
What happened?
An autopilot pull request in DiluxOne/.github whose own self-tests fail is never fixed and waits forever. In .github/workflows/pull-request.yml the review job needs [conventions, actionlint, scripts], so it is skipped when scripts fails; autopilot-fix runs only when needs.review.outputs.blocking == 'true', which a skipped review never sets. Meanwhile scripts/waiting.py puts "the autopilot: fix the failed check" on the board, so nobody is told it is stuck. #738 is in that state now (three self-tests fail after the agent marked it ready; review SKIPPED; board "Waits on the autopilot: fix the failed check"), and #689 sat the same way from 2026-11-07 to 2026-11-14, holding the engine's core and every issue queued behind it. A review that crashes instead of answering (#723: usage: review-type.sh …, exit 2) ends the same way. The organisation's other repositories (org-pull-request.yml) have the same autopilot-fix condition.
Steps to reproduce
- Open an autopilot pull request (label
autopilot, author dilux-bot) whose diff makes one--testof the scripts job fail. scriptsfails,reviewis skipped,autopilot-fixis skipped.- No later event starts a fix round; the board says it waits on the autopilot.
Version
main at the merge of #737.
Logs or screenshots
#738 (review SKIPPED, Scripts (self-tests) failing), https://github.com/DiluxOne/.github/actions/runs/43104985993 (#723's review exiting 2).
Proposed solution
A failed required check on an autopilot pull request, or a review that ends without a verdict, starts a fix round with the failed jobs' log, within the same autopilot-rounds cap, the way autopilot-queue already does on dequeued (autopilot.py queue-failed), in pull-request.yml and org-pull-request.yml alike. When the cap is reached it hands the pull request to a person with needs-human, so the board never says the autopilot is fixing something that nothing will fix.
Problem
Autopilot pull requests stall without anyone being told, for days, and one that touches the engine's core blocks every issue queued behind it.
Scope
The fix trigger in pull-request.yml and org-pull-request.yml (a failed required check, a review that ends without a verdict), autopilot.yml's fix mode reading the failed jobs' log, waiting.py's board for that state, their tests, and docs/workflows.md.
Out of scope
Why a given check failed. The review's own crash in #723 (#720).
Acceptance criteria
C1: Given an autopilot pull request whose scripts job fails, when its checks complete, then a fix round starts with the failed jobs' log, without a person.
C2: Given an autopilot pull request whose review ends without a verdict, when its checks complete, then a fix round starts the same way.
C3: Given an autopilot pull request whose rounds reached autopilot-rounds, when a check fails again, then no round starts and it is handed to a person with needs-human.
C4: Given either state, when the board is written, then it names the fix round that is running, or the person, never a fix nothing will start.
C5: Given a repository of the organisation other than DiluxOne/.github, when the same happens there (org-pull-request.yml), then C1 to C4 hold too.
Criticality
Critical
Who works on it
- I want to work on this myself
🤖 AI-generated · Claude Opus 5.5 (Anthropic)
Spec: approach
The PRD passes G1: the problem is clear and C1 to C5 can each be tested from the pipelines' YAML and the scripts' pure functions.
-
Trigger (C1, C2, C5). Each pipeline's
autopilot-fixgetsneeds:on every job the review waits on (pull-request.yml:[conventions, actionlint, scripts, review]; org-pull-request.yml:[conventions, review]). Itsif:keeps every guard it has now:always(),pull_request, notedited, theautopilotlabel, dilux-bot as author, and the repository clause. Only the trigger part becomesneeds.review.outputs.blocking == 'true' || needs.review.result == 'failure' || needs.<gate>.result == 'failure'(one clause for each gate). That covers a gate that failed (the review is then skipped and has no outputs) and a review that ended without a verdict, whether its verdict step said so (blocking=true, "did not finish") or the job died before that step (fix: the bug reproduced from #720 #723: no outputs). The two conditions stay the same apart from the repository and the gate list, and the existing fix: the autopilot answers a review in the organisation's repository #504 test checks this. -
Fix mode reads the failed jobs' log (C1, C2). No new input: org-pull-request.yml calls
autopilot.yml@v7, and the queue round (ci: test before the pull request, a faster queue, an unstuck autopilot #479) avoided a new input for the same reason. In fix mode,autopilot.pygets achecks-failedcommand andinputs fixgets a matching branch. They read the jobs of the calling run (GITHUB_RUN_ID; a reusable workflow's jobs belong to it). Two cases make a check round: a failed job other than the review's, or a failed review job with no verdict at the head (the bot'sdx-reviewsummary says "did not finish", or its record is not at the head SHA). The failed jobs'gh run view --job <id> --log-failedgoes into.dx-io/checks.md, scrubbed and cut likequeue.md:queue_log_textis generalised and keeps the queue's text.prompt_fix(..., checks=True)points the agent at that file instead ofreview.md, and the round writes no review ids. A review that blocks with a verdict keeps today's review round. A round that fails as the round before did still stops on the existingdx-prepr-sig. -
Cap (C3). The rounds check in "May it start" stays where it is (
ROUND > autopilot-rounds→needs-human, a comment, and the issue opened to claims). Its comment now comes from a purerounds_spent(max, checks)in autopilot.py, so it says "its checks still fail" on a check round instead of always "the review still blocks".next_stepgets achecks_failedstate, so the sweep and the board's next step agree with the trigger: one round below the cap, the person at the cap. -
Board (C4).
waiting.py blocker: an autopilot pull request whose failed checks include at least one the pipeline fixes (check-run namesconventions /,review /,actionlint,Scripts (self-tests)) waits on "the autopilot's fix round", with rounds left. Rounds left means the highestautopilot:round-Nlabel is belowautopilot-rounds, read from policy/autonomy.yml. At the cap, or when only other checks failed (a repository's own workflow, which nothing re-runs), it waits on the maintainer, with the action to take it over (needs-humanalready comes first). The board then never tells the autopilot to "fix the failed check".
Options a maintainer may care about:
- (A, recommended, within scope) The round starts only on the jobs of the organisation's pipeline (conventions, the review and, here, actionlint and scripts). On the board, a repository's own failing workflow goes to the maintainer.
- (B) Also start rounds on a repository's own required workflows, such as a WordPress plugin's suites. That needs a new
workflow_run/check_suitetrigger outside the two pipelines, which is beyond the PRD's scope, so it is listed as a finding instead.
What this widens: a fix round (the agent's job, with the Claude subscription and a read-only token) now also starts on a failed gate or a review without a verdict. It stays within the same autopilot-rounds and runs caps. No new permission or secret is needed: reading the run's jobs needs actions: read, which autopilot.yml's prep job already holds or will be given, and the pull request says which. In repositories other than this one it takes effect once v7 is moved by a release.
Spec: files
- .github/workflows/pull-request.yml
- .github/workflows/org-pull-request.yml
- .github/workflows/autopilot.yml
- scripts/autopilot.py
- scripts/waiting.py
- scripts/fixtures/waiting.json
- docs/workflows.md
Spec: tests
- C1 → scripts/autopilot.py (
--test: pull-request.yml's autopilot-fix needs the gates and fires onneeds.scripts.result == 'failure';checks-failed/checks_log_textselect the failed jobs and their log;prompt_fix(checks=True)reads.dx-io/checks.md) - C2 → scripts/autopilot.py (
--test: the condition fires onneeds.review.result == 'failure'and on a blocking review; a review job that failed without a verdict at the head is a check round, one with a verdict a review round) - C3 → scripts/autopilot.py (
--test:next_stepwithchecks_failedpastautopilot-roundswaits on a person;rounds_spenttext; the fix branch of "May it start" addsneeds-humanabove the cap) - C4 → scripts/waiting.py (
--testwith scripts/fixtures/waiting.json: an autopilot pull request whose scripts or review check failed with rounds left waits on the autopilot's fix round; at the cap, or with only a repository's own check failed, it waits on the maintainer; never "fix the failed check" for the autopilot) - C5 → scripts/autopilot.py (
--test: org-pull-request.yml's autopilot-fix carries the same trigger as pull-request.yml's apart from the repository and its gate list, which is conventions and review)
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsDone
Activity
dilux-bot commentedon Nov 14, 2026
The issue's board
13fd4e90eee7receipts/issues/741/merged-2026-11-14T05:06:35Z.json🤖 AI-generated · Claude Opus 5.5 (Anthropic)
Seal #741's PRD
dilux-bot commentedon Nov 14, 2026
You wrote this Bug with the maintain or admin role, so it is accepted with its writing: no triage classifies it and no reproduction runs, and its PRD is sealed in your name. The autopilot codes it now in the organisation's runners, starting from a failing test.
🤖 AI-generated · Claude Opus 5.5 (Anthropic)
dilux-bot commentedon Nov 14, 2026
The autopilot waits for #736: that open autopilot pull request already changes the engine's core (org.yml
core), which this issue's Spec touches too, and two changes to the core at once keep putting each other in conflict. It starts this one on its own once #736 merges or closes.🤖 AI-generated · Claude Opus 5.5 (Anthropic)
Seal #741's Spec
soydiloreto commentedon Nov 14, 2026
For the next run: the first attempt (#744, closed) was blocked by its review with one major. Avoid it from the start: (1) a capped review with a stale blocking verdict must not be read as a fresh one (scripts/autopilot.py, around
run_end/the review state); (2)checks_failedmust be carried by the state the production code builds (autopilot.yml'swhatstep andsweep_step), not only by the tests; (3) when the failed jobs cannot be read, it must not stop with an empty list ("could not read which checks failed ()"), which is how #744 stopped itself; (4)--paginatewhen listing a run's jobs; (5) docs/configuration.md (autopilot-rounds) and docs/pull-requests.md say rounds also start from a failed check.🤖 AI-assisted · Claude Opus 5.5 (Anthropic)
dilux-bot commentedon Nov 14, 2026
Issue ready (G1): the PRD has every section of the schema (5 acceptance criteria).
🤖 AI-generated · Claude Opus 5.5 (Anthropic)
Seal #741's PRD
Seal #741's Spec
Receipt for #741