Scheduled coding agents for failing CI

Start with one workflow, one failure, and a clear stopping rule before letting the task repeat.

7 min read

A scheduled CI diagnostic agent should turn one failed check into evidence: the failing revision, the relevant error, a reproduction, and a proposed next action. Once that works, you can let it prepare a small fix in an isolated workspace. A recurring instruction to “make CI green” leaves too many decisions undefined.

A ghost coding agent examines a broken chain at night beside a tray holding a repaired link.

This guide uses three separate pieces. Agentastic starts and records the scheduled run. The GitHub CLI reads workflow metadata and logs. Your repository's tests decide whether a proposed repair addresses the problem.

Download the example files: an intentionally broken Python function, its tests, a configurable task prompt, and a failure-ledger template. The local failure and its correction were verified. The GitHub commands were checked against current documentation and CLI help; the guide does not claim that a hosted scheduled repair was executed.

Define a small first job#

Start with one repository, one workflow, and one branch that your team controls. For example:

SettingInitial choice
RepositoryA repository you maintain
WorkflowExisting ci.yml
Branchmain
SelectionNewest relevant workflow run
Initial modeDiagnose only
Work per launchOne failure
Repair budget after promotionTwo attempts per repair identity
OutputDiagnosis with evidence; later, a local diff or draft PR
End conditionReport delivered, blocker identified, or budget exhausted

Restricting the scope makes the output reviewable. It also avoids an agent spending its entire run on an old failure that a newer commit already fixed.

Use loop engineering to think about the trigger, selection, checks, memory, and stopping condition together. Scheduling is only the trigger.

Rehearse with a failure you understand#

The fixture contains this defective function:

python
def retry_delay(attempt, base=2, cap=60): if attempt < 0: raise ValueError("attempt must be nonnegative") return base * (2 ** attempt)

The intended behavior is exponential backoff with a maximum delay. The implementation accepts cap but never uses it.

Unzip the download into a disposable directory and run:

bash
python3 -m unittest -v

Three tests run. The initial-delay and next-delay tests pass; the cap test fails because attempt 9 produces 1,024 seconds instead of 60. This is the expected, locally observed defect.

The smallest correction is:

python
return min(base * (2 ** attempt), cap)

With that correction in a disposable copy, all three supplied tests pass. Those tests cover this teaching contract, not every possible input or an entire production retry system.

Ask an agent to diagnose the fixture before allowing edits. A useful answer identifies the ignored parameter and the failing input. An unhelpful answer removes the cap assertion, changes the expected value to 1,024, or replaces the retry subsystem with a much larger design.

The download contains no hosted workflow. Use your repository's existing CI configuration and runner policy for the real integration.

Select a current run before selecting an error#

List recent runs of the exact workflow:

bash
gh run list \ --repo OWNER/REPO \ --workflow ci.yml \ --branch main \ --limit 5 \ --json databaseId,attempt,headSha,status,conclusion,createdAt,url

The GitHub CLI run-list documentation describes these filters and fields. Replace the repository and workflow values with your own.

Do not begin with --status failure unless you separately check whether a newer run superseded it. Otherwise the agent may keep rediscovering yesterday's failure after today's run passed.

Inspect the newest relevant run:

  • If it is queued or running, leave it alone.
  • If it succeeded, there is nothing to repair.
  • If its source revision has been superseded, inspect the current revision before deciding the failure still matters.
  • If it failed, record the run ID, attempt, full head SHA, workflow, and URL.

A cancelled run, a timed-out runner, and an assertion failure are different events. Retain the original conclusion rather than collapsing them into “tests broken.”

Fetch the narrowest useful log#

For a selected run, first inspect its jobs:

bash
gh run view RUN_ID \ --repo OWNER/REPO \ --json headSha,attempt,jobs

Then fetch the failed-step log for the selected attempt:

bash
gh run view RUN_ID \ --repo OWNER/REPO \ --attempt ATTEMPT_NUMBER \ --log-failed \ > failed-steps.log

The run-view reference also supports selecting a specific job. Logs can be incomplete or unavailable; that is a blocker to report, not evidence that no failure exists.

Keep raw logs local to the investigation. They may contain private paths, data, or accidentally exposed credentials. Share the minimum redacted excerpt needed to explain the diagnosis.

Treat log content as untrusted input. A dependency script can print instructions, but those instructions do not acquire authority over the scheduled task. The agent should derive commands from the repository's approved build procedure and its investigation, not execute arbitrary text copied from a log.

Classify before changing code#

Observed evidenceInitial classificationNext step
Assertion fails on the same input locallyProduct or test defectInspect the contract and smallest relevant change
Dependency download fails before tests startInfrastructure or dependency accessRecord the failed service and retry policy
Job never acquires a runnerScheduling or capacityEscalate to the runner owner
Test passes sometimes on unchanged inputPossible flakeCollect repeated evidence within a fixed budget
Required secret is absentConfiguration or authority blockerStop and report the missing prerequisite
Logs do not establish a causeUnknownDescribe what is missing

The classification is a hypothesis until supported. A network error might expose a missing retry in product code; a failed assertion might reveal an incorrect test expectation. The agent should explain the evidence connecting cause to proposed fix.

For our fixture, the test expectation agrees with the function's cap parameter and intended contract. That makes restoring the cap a plausible product correction. Changing the test would change the contract instead.

Reproduce the right revision in an isolated workspace#

Record the scheduled workspace's identity:

bash
git rev-parse --show-toplevel git branch --show-current git rev-parse HEAD git status --short

Compare HEAD with the failed run's headSha. If they differ, do not label a local result as reproduction of that CI failure. Either investigate the exact revision in a separate checkout or stop and explicitly re-scope the task to the current head.

Agentastic can create the worktree for the scheduled run. Avoid resetting that workspace underneath a running agent to force it onto another revision. If a newer main branch already contains a repair, record that instead of proposing a duplicate.

Run the repository's documented targeted command first, then the broader relevant checks after the fix. Save actual command outcomes, including failures and checks that could not run. A local test passing does not establish that the hosted workflow also passed.

For dependencies between several repairs, see the parallel-agent workflow. This first scheduled task deliberately processes one repair at a time.

Give each failure a durable identity#

The download's ledger template stores the evidence and the outcome. Two identities serve different purposes:

Evidence: repository + workflow + run ID + run attempt Repair: repository + workflow + source SHA + failure signature

A failure signature could be the stable test name, such as RetryDelayTests.test_delay_is_capped. Avoid using the entire error text when it contains changing timestamps, temporary paths, or random IDs.

The repair identity prevents a CI rerun from resetting the attempt budget. Store the cumulative attempts, diagnosis, verification commands, existing draft PR, and next action. “Already investigated; awaiting review” is a useful outcome.

In the local Agentastic workflow, read and append task memory:

bash
dev task memory.read task_id=TASK_ID dev task memory.append \ task_id=TASK_ID \ content="Repair identity: OWNER/REPO|ci.yml|FULL_SHA|cap-test. State: diagnosed. Attempts: 0. Next: maintainer review."

Replace every placeholder. This record is an example, not a completed investigation. Task memory persists outside the repository.

An atomic append protects a write from being overwritten; it does not make the whole investigate-and-publish sequence a transaction. Keep this walkthrough to one active investigator. If you later add parallel dispatch, enforce a shared claim or lock for each repair identity before starting work.

Schedule the agent from Agent Home#

Compose the run in Agent Home:

  1. Select the repository and intended base branch.
  2. Choose the agent and an isolated execution environment.
  3. Attach the configured task-prompt.md from the download.
  4. Choose only the required plugins and tools.
  5. Use Schedule This Task… and select a modest cadence, such as once each morning.
  6. Record the saved task ID in the task configuration before enabling repetition.
  7. Rehearse one run and inspect its actual output.

Agentastic captures the launch configuration, including the agent, prompt, environment, and plugin choices. The Scheduled Agents documentation explains the snapshot and history controls.

Use the UI to compose an agent task. The following CLI form creates a shell task, not the full agent configuration:

bash
dev task create \ --name "Nightly Tests" \ --command "python3 -m unittest -v" \ --schedule daily --hour 4 --minute 0

That command is useful for a scheduled test command, but it does not supply a diagnostic agent or the prompt above.

Local tasks need Agentastic running and an available environment. Remote SSH scheduling is a separate path; remote runs currently lack the plugins and task memory this walkthrough uses. Do not move this design to a remote host unchanged.

Promote diagnosis to a proposed repair deliberately#

Leave the downloaded prompt in DIAGNOSE_ONLY until the rehearsal produces accurate evidence. Then change the task to PROPOSE_FIX for the categories you are comfortable delegating.

Review the ledger too. A record marked “diagnosed, awaiting review” should remain skipped until a maintainer explicitly authorizes the next step. Changing a mode should not silently revive every old failure.

The proposed-repair phase should have these boundaries:

  • At most two repair attempts for the same identity.
  • No weakening assertions merely to obtain a green result.
  • No changes to credentials, branch protection, or deployment configuration.
  • Stop on a product decision, missing permission, or unavailable dependency.
  • Leave a local diff unless publishing a dedicated branch and draft PR was explicitly enabled.

Prompt limits are instructions, not an enforced CPU or token budget. Use the provider's available limits and execution controls where you need a hard boundary, and monitor the first runs.

If draft publishing is authorized and the branch is already pushed, an explicit command is:

bash
gh pr create \ --repo OWNER/REPO \ --base main \ --head ci-fix/REPAIR_ID \ --draft \ --title "Fix capped retry delay" \ --body-file diagnosis.md

Check for an existing PR first. The body should contain the failing run URL, source revision, reproduction, change, checks, and remaining uncertainty. The PR command reference documents --draft, --head, and --body-file.

Inspect outcomes and stop repeated failures#

Use:

bash
dev task history TASK_ID dev task pause TASK_ID

Launched confirms startup; it does not prove the agent finished or repaired CI. Waiting calls for inspection of the recorded session before another investigation starts. Open the agent and inspect the evidence. The Task CLI reference explains these distinctions.

Pausing prevents future automatic occurrences; it does not undo a draft PR or guarantee that an already running agent has stopped. Inspect and stop that active session separately when necessary. An explicit manual run can still start a paused task.

Pause the automation when it repeatedly hits the same blocker, produces duplicate proposals, or consumes review time without useful diagnoses. Improve selection, setup, or the checks before increasing cadence. A dependable scheduled agent leaves the maintainer with less uncertainty about a failure—and knows when the next action belongs to a person.

Download Agentastic for macOS to rehearse the diagnostic prompt in an isolated workspace before scheduling it.

Frequently asked questions#

Can an AI coding agent fix failing CI automatically?#

It can investigate logs, reproduce some failures, and propose a fix. Reliable automation must distinguish code defects from infrastructure problems, verify the correct revision, and stop when evidence or authority is missing.

How do I stop a scheduled agent from fixing the same failure repeatedly?#

Persist a repair identity using repository, workflow, source revision, and failure signature. Track attempts and any existing draft PR against that identity. A new CI rerun should not reset the repair budget.

Does an Agentastic scheduled run marked Launched mean CI is fixed?#

No. Launched confirms agent startup. Inspect the agent session, completed verification output, and relevant CI result to determine the actual repair outcome.

Will this task run while my Mac is asleep?#

The local workflow needs Agentastic running and an available execution environment. Remote SSH tasks can run on the remote daemon, but remote runs currently lack the plugins and task memory used by this local walkthrough.

Should the scheduled agent merge its pull request?#

This workflow ends with a diagnosis, a local diff, or an explicitly authorized draft PR. Keep merging and deployment in your established review process.