You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: workflows/autoloop.md
+60-7Lines changed: 60 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -362,24 +362,76 @@ Each run executes **one iteration for the single selected program**:
362
362
363
363
### Step 5: Accept or Reject
364
364
365
-
**If the metric improved** (or this is the first run establishing a baseline):
365
+
The sandbox-computed metric is necessary but **not sufficient** for acceptance. The agent's sandbox cannot reliably install many project toolchains (e.g., `bun`, `tsc`, `cargo`, `go`, `pytest`) due to network restrictions on asset hosts, so a "metric improved" signal from the sandbox can mask broken commits (e.g., type-check or test failures the sandbox couldn't observe). Acceptance must therefore be gated on **CI green** for the pushed HEAD commit. If CI fails, attempt to fix-and-retry within the same iteration rather than reverting — reverting throws away mostly-correct work and creates `commit→revert→commit` churn on the branch.
366
+
367
+
The accept path is split into three sub-steps: **5a (push and wait for CI)**, **5b (fix loop)**, **5c (accept)**.
368
+
369
+
**If the metric did not improve**, jump straight to the "metric did not improve" path below — no push, no CI gate.
370
+
371
+
#### Step 5a: Push and wait for CI
372
+
373
+
**Only entered if the metric improved** (or this is the first run establishing a baseline).
374
+
366
375
1. Commit the changes to the long-running branch `autoloop/{program-name}` with a commit message referencing the actions run:
- Commit message body (after a blank line): `Run: {run_url}` referencing the GitHub Actions run URL.
369
378
2. Push the commit to the long-running branch.
370
-
3. If a draft PR does not already exist for this branch, create one:
379
+
3. If a draft PR does not already exist for this branch, create it now (see Step 5c for the title/body format). The PR is needed so that CI runs and so `gh pr checks` has a target.
380
+
4. Wait for CI on the new HEAD and reduce all check-runs to a single status — `success`, `failure`, or `pending`:
381
+
382
+
```bash
383
+
PR=${EXISTING_PR:-$(gh pr list --head autoloop/{program-name} --json number -q '.[0].number')}
/^(FAILURE|CANCELLED|TIMED_OUT|ACTION_REQUIRED|STARTUP_FAILURE|STALE)$/ { r = "failure" }
389
+
/^(PENDING|QUEUED|IN_PROGRESS|WAITING|REQUESTED)$/ { if (r == "success") r = "pending" }
390
+
END { print r }')
391
+
```
392
+
393
+
Three outcomes: `success`, `failure`, or `pending`. `pending` should be rare given `--watch`, but the awk fallback is defensive — never accept on `pending`. Treat `pending` as a non-terminal state: re-run the `gh pr checks --watch` step (it does not consume a fix attempt and the per-attempt `--watch` time still counts toward the 60-min wall-clock cap from Step 5b). If `pending` persists past the wall-clock cap, fall through to the `ci-timeout` handling in Step 5b.7.
394
+
395
+
5. If `status == "success"`, proceed to **Step 5c**. If `status == "failure"`, proceed to **Step 5b**. If `status == "pending"`, re-run this step (subject to the wall-clock cap defined in Step 5b.7).
396
+
397
+
#### Step 5b: Fix loop (up to 5 attempts per iteration)
398
+
399
+
If `status == "failure"`, **fix and retry — do not revert, do not accept**:
400
+
401
+
1.**Fetch the failing check-run logs** for the pushed SHA via `gh run view --log` or the Checks API.
402
+
2.**Extract a structured failure summary**:
403
+
- Failing job names and the first error line for each.
404
+
-**A failure signature** — a stable, normalized fingerprint of the failures (e.g., sorted failing-test names + the top error code, like `TS2339:fromArrays:tests/stats/eval_query.test.ts`). The signature is what the no-progress guard compares.
405
+
406
+
*(The shared failure-signature extractor lives in the scheduler helper module — see issue #34 for the implementation.)*
407
+
3.**No-progress guard**: if this attempt's failure signature exactly matches the previous attempt's signature, **stop**. The agent is stuck in a repeat-loop. Set `paused: true` on the state file with `pause_reason: "stuck in CI fix loop: <signature>"`, append `"ci-fix-exhausted"` to `recent_statuses`, comment on the program issue with the signature and the three most recent attempts, and end the iteration.
408
+
4.**Attempt the fix**: feed the structured failure summary back to the agent as the next sub-task (e.g., "CI failed on `<sha>`. Here are the failures: `<…>`. Fix them and push again."). The agent commits the fix and pushes.
409
+
5.**Loop back to Step 5a** with the new HEAD.
410
+
6.**Budget: 5 fix attempts per iteration.** If the 5th attempt still leaves CI red, set `paused: true` with `pause_reason: "ci-fix-exhausted: <signature>"`, append `"ci-fix-exhausted"` to `recent_statuses`, comment on the program issue, and end the iteration.
411
+
7.**Wall-clock cap: 60 min per iteration** including all CI waits across attempts. If exceeded mid-fix, set `paused: true` with `pause_reason: "ci-timeout"`, append `"ci-fix-exhausted"` to `recent_statuses`, leave the current branch state in place, and end the iteration.
412
+
413
+
#### Step 5c: Accept
414
+
415
+
**Only entered when `status == "success"`** from Step 5a (possibly after one or more fix attempts in Step 5b).
416
+
417
+
1. The commit(s) are already on the long-running branch (pushed in Step 5a / 5b). No further pushing needed.
418
+
2. If a draft PR does not already exist for this branch, create one:
371
419
- Title: `[Autoloop: {program-name}]`
372
420
- Body includes: a summary of the program goal, link to the program issue, the current best metric, and AI disclosure: `🤖 *This PR is maintained by Autoloop. Each accepted iteration adds a commit to this branch.*`
373
-
If a draft PR already exists, update the PR body with the latest metric and a summary of the most recent accepted iteration. Add a comment to the PR summarizing the iteration: what changed, old metric, new metric, improvement delta, and a link to the actions run.
421
+
If a draft PR already exists, update the PR body with the latest metric and a summary of the most recent accepted iteration. Add a comment to the PR summarizing the iteration: what changed, old metric, new metric, improvement delta, the **fix-attempt count** if `> 0`, and a link to the actions run.
374
422
4. Ensure the program issue exists (see [Program Issue](#program-issue) below) — for file-based programs that have no program issue yet (`selected_issue` is null in `/tmp/gh-aw/autoloop.json`), create one and record its number in the state file's `Issue` field.
375
423
5. Update the state file `{program-name}.md` in the repo-memory folder:
376
424
- Update the **⚙️ Machine State** table: reset `consecutive_errors` to 0, set `best_metric`, increment `iteration_count`, set `last_run` to current UTC timestamp, append `"accepted"` to `recent_statuses` (keep last 10), set `paused` to false.
377
-
- Prepend an entry to **📊 Iteration History** (newest first) with status ✅, metric, PR link, and a one-line summary of what changed and why it worked.
425
+
- Prepend an entry to **📊 Iteration History** (newest first) with status ✅, metric, PR link, the fix-attempt count if `> 0`, and a one-line summary of what changed and why it worked.
378
426
- Update **📚 Lessons Learned** if this iteration revealed something new about the problem or what works.
379
427
- Update **🔭 Future Directions** if this iteration opened new promising paths.
380
-
6.**Update the program issue**: edit the status comment and post a per-iteration comment on the program issue (see [Program Issue](#program-issue)).
428
+
6.**Update the program issue**: edit the status comment and post a per-iteration comment on the program issue (see [Program Issue](#program-issue)). Note the fix-attempt count in the per-iteration comment if `> 0`.
381
429
7.**Check halting condition** (see [Halting Condition](#halting-condition)): If the program has a `target-metric` in its frontmatter and the new `best_metric` meets or surpasses the target, mark the program as completed.
382
430
431
+
#### Coordination with PR-health-keeper workflows
432
+
433
+
If a repo ships a companion PR-health-keeper workflow (e.g., an "Evergreen" workflow that fixes failing CI on open PRs), it should be able to pick up paused Autoloop PRs using the same rules as human-authored PRs. The handoff is via the `pause_reason` field — `ci-fix-exhausted: <signature>`, `stuck in CI fix loop: <signature>`, and `ci-timeout` are all signals that the branch is red and needs an external nudge. Absent such a workflow, the loud pause + structured reason gives a human enough signal to intervene.
434
+
383
435
**If the metric did not improve**:
384
436
1. Discard the code changes (do not commit them to the long-running branch).
385
437
2. Update the state file `{program-name}.md` in the repo-memory folder:
@@ -662,11 +714,11 @@ All iterations in reverse chronological order (newest first).
662
714
| PR |`#number` or `—`| Draft PR number for this program |
663
715
| Issue |`#number` or `—`| The single program issue (`[Autoloop: {program-name}]`) for this program. Hosts the status comment, per-iteration comments, and human steering comments. |
664
716
| Paused |`true` or `false`| Whether the program is paused |
665
-
| Pause Reason | text or `—`| Why it is paused (if applicable) |
717
+
| Pause Reason | text or `—`| Why it is paused (if applicable). Common values include `manual`, `consecutive errors`, `ci-fix-exhausted: <signature>` (5 fix attempts didn't fix CI), `stuck in CI fix loop: <signature>` (no-progress guard tripped — same failure signature twice in a row), and `ci-timeout` (60-min wall-clock cap hit).|
666
718
| Completed |`true` or `false`| Whether the program has reached its target metric |
667
719
| Completed Reason | text or `—`| Why it completed (e.g., `target metric 0.95 reached with value 0.97`) |
| Recent Statuses | comma-separated words | Last 10 outcomes: `accepted`, `rejected`, or `error`|
721
+
| Recent Statuses | comma-separated words | Last 10 outcomes: `accepted`, `rejected`, `error`, or `ci-fix-exhausted`. The `ci-fix-exhausted` value is the coarse bucket for *any* iteration that ended because the CI gate could not be made green within the per-iteration budget — including no-progress-guard trips, 5-attempt budget exhaustion, and `ci-timeout`. The fine-grained reason is in `pause_reason`.|
670
722
671
723
### Iteration History Entry Format
672
724
@@ -679,6 +731,7 @@ After each iteration, prepend an entry to the **📊 Iteration History** section
679
731
-**Change**: {one-line description of what was tried}
0 commit comments