Skip to content

feat(ci): implement auto-trigger agent on CI failure#1500

Closed
singhvibhanshu wants to merge 1 commit intogeneralaction:mainfrom
singhvibhanshu:fix/ci-failure
Closed

feat(ci): implement auto-trigger agent on CI failure#1500
singhvibhanshu wants to merge 1 commit intogeneralaction:mainfrom
singhvibhanshu:fix/ci-failure

Conversation

@singhvibhanshu
Copy link
Contributor

Summary

This PR introduces the Auto-trigger agent on CI failure feature. It automatically monitors active task branches for GitHub Actions workflow failures. When a failure occurs, Emdash fetches and smartly truncates the error logs, spins up an agent in the task's worktree, and allows the AI to attempt a fix—closing the feedback loop without manual context switching.

Key Implementation Details:

  • Modular Architecture: Added dedicated CI modules for configuration, monitoring (polling-based), log parsing, and orchestration.
  • Safety Controls: Implemented a state tracker to prevent infinite retry loops, worktree lock files, and local/remote HEAD checks to prevent race conditions during git operations.
  • Context Protection: Built a pure-function log parser that strips ANSI codes and truncates massive logs from the top, ensuring LLM context limits are respected while preserving the actionable tail-end stack traces.
  • In-App Configuration: Added a CiAutoFixSettingsCard to the existing Settings UI so users can easily toggle modes (auto vs. review), set retry limits, and define trigger filters (e.g., test/lint only) without manually editing .emdash.json.

Fixes

Fixes #1375

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • Chore (refactoring code, technical debt, workflow improvements)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Refactor (does not change functionality, e.g. code style improvements, linting)
  • This change requires a documentation update

Mandatory Tasks

  • I have self-reviewed the code
  • A decent size PR without self-review might be rejected

Checklist

  • I have read the contributing guide
  • My code follows the style guidelines of this project (pnpm run format)
  • I have commented my code, particularly in hard-to-understand areas
  • I have checked if my PR needs changes to the documentation
  • I have checked if my changes generate no new warnings (pnpm run lint)
  • I have added tests that prove my fix is effective or that my feature works
  • I haven't checked if new and existing unit tests pass locally with my changes

@vercel
Copy link

vercel bot commented Mar 16, 2026

@singhvibhanshu is attempting to deploy a commit to the General Action Team on Vercel.

A member of the Team first needs to authorize it.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 16, 2026

Greptile Summary

This PR adds an auto-trigger agent on CI failure feature that polls GitHub Actions for failed workflows on active task branches, fetches and truncates error logs, and spawns an AI agent to attempt a fix. It includes a settings UI card, per-branch retry state tracking, worktree locking, and local/remote HEAD drift guards.

  • Architecture: Clean modular split across types, config, logParser, stateTracker, monitor, and orchestrator under src/main/services/ci/.
  • Monitor always polls: The polling loop runs unconditionally even when the feature is globally disabled, making unnecessary database and gh API calls on every interval tick.
  • Auto-mode prompt is contradictory: Both auto and review modes tell the agent "do not commit or push", but in auto mode the orchestrator commits (git add -A) and pushes on the agent's behalf. The agent receives essentially identical instructions in both modes.
  • Remote-drift safety bypass: When the initial getRemoteHeadSha returns null (network error, unpushed branch), finalizeAutoMode silently skips the remote-changed check and pushes anyway.
  • Test coverage is thin: Only one test each for log parser and orchestrator; the auto-mode commit/push path, review-mode notification path, and lock contention scenarios are untested.

Confidence Score: 2/5

  • The core polling and auto-push paths have logic gaps that could cause unnecessary API usage and unsafe pushes; recommend fixing before merge.
  • Score of 2 reflects three concrete issues: (1) the monitor polls GitHub and the DB every 2 minutes even when the feature is disabled, (2) the auto-mode agent prompt contradicts the orchestrator's actual commit+push behavior making agent output unpredictable, and (3) a null remote HEAD silently disables the race-condition guard before pushing. Test coverage for the most critical paths (auto commit/push, lock contention) is also lacking.
  • src/main/services/ci/orchestrator.ts (prompt logic and remote-drift guard) and src/main/services/ci/monitor.ts (unconditional polling)

Important Files Changed

Filename Overview
src/main/services/ci/types.ts Clean type definitions for CI auto-fix config, retry state, failure candidates, and parsed logs. No issues.
src/main/services/ci/config.ts Well-structured config resolution with proper normalization, bounds checking, and project-level override support. No issues.
src/main/services/ci/logParser.ts Log parsing with ANSI stripping and tail-based truncation. Step-scoped filtering relies on tab-delimited gh log format which is reasonable.
src/main/services/ci/stateTracker.ts Retry state tracking with manual-commit reset detection and async persistence queue. Solid design for preventing infinite retry loops.
src/main/services/ci/monitor.ts Polling-based CI failure monitor. Issue: polls database and GitHub API unconditionally even when feature is globally disabled, wasting resources.
src/main/services/ci/orchestrator.ts Core orchestrator with lock management, race condition guards, and agent spawning. Issues: auto-mode prompt contradicts actual commit/push behavior; remote-drift safety check silently bypassed when initial remote HEAD is null.
src/main/main.ts Clean integration: starts orchestrator on app ready, stops on before-quit. Follows existing service lifecycle patterns.
src/main/settings.ts Adds ciAutoFix to AppSettings with proper normalization. Duplicates validation logic from config.ts but follows existing settings patterns.
src/renderer/components/CiAutoFixSettingsCard.tsx Settings UI card with toggle, dropdowns, and numeric inputs. Follows existing component patterns. Sub-controls not disabled when feature toggle is off (minor UX).
src/renderer/components/SettingsPage.tsx Minimal change adding CI auto-fix card to the repository settings tab. No issues.
src/test/main/ci/logParser.test.ts Single test for sanitize+truncate. Coverage is minimal — no tests for fetchAndParseFailedLog, extractLinesForFailedSteps, or collectFailedStepNames.
src/test/main/ci/orchestrator.test.ts Tests the head-mismatch abort path. Good but only covers one scenario; no tests for auto-mode commit/push flow, review mode, or lock contention.
src/test/main/ci/stateTracker.test.ts Good coverage of retry counting, halting, and manual-commit reset. Well-structured tests with temp directory cleanup.

Sequence Diagram

sequenceDiagram
    participant Main as main.ts
    participant Orch as Orchestrator
    participant Mon as Monitor
    participant DB as DatabaseService
    participant GH as gh CLI
    participant Agent as AI Agent
    participant Git as git

    Main->>Orch: start()
    Orch->>Mon: start(onFailure)
    loop Every pollIntervalMs
        Mon->>DB: getTasks() + getProjects()
        Mon->>GH: gh run list --branch <branch>
        alt Failed run found & passes filters
            Mon->>Orch: onFailure(candidate, config)
            Orch->>Git: rev-parse HEAD (local)
            Orch->>Git: ls-remote origin (remote)
            alt Heads match & lock acquired
                Orch->>GH: gh run view <runId> --log-failed
                Orch->>Agent: spawn(cli, prompt)
                Agent-->>Orch: exit(0)
                alt auto mode & files changed
                    Orch->>Git: add -A + commit + push
                else review mode
                    Orch-->>Main: notify user
                end
            end
        end
    end
    Main->>Orch: stop()
    Orch->>Mon: stop()
Loading

Last reviewed commit: cd914b6

Comment on lines +169 to +176
const loop = async () => {
if (this.isPolling) {
return;
}
this.isPolling = true;

try {
const tasks = await databaseService.getTasks();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Monitor polls even when feature is disabled

The CiFailureMonitorService.start() is called unconditionally from main.ts, and the polling loop fires every pollIntervalMs regardless of the global enabled flag. The enabled check only happens inside processTask after the loop has already fetched all tasks and projects from the database, and then calls gh run list for every active task before checking the per-project config.

Consider adding an early-exit at the top of the loop function:

Suggested change
const loop = async () => {
if (this.isPolling) {
return;
}
this.isPolling = true;
try {
const tasks = await databaseService.getTasks();
const loop = async () => {
if (this.isPolling) {
return;
}
this.isPolling = true;
try {
const globalConfig = getGlobalCiAutoFixConfig();
if (!globalConfig.enabled) {
return;
}
const tasks = await databaseService.getTasks();

This avoids unnecessary database and GitHub API calls when the user has not enabled the feature.

Comment on lines +52 to +55
const modeInstruction =
mode === 'auto'
? 'Do not commit or push by yourself. Only modify files and explain what you changed.'
: 'Do not commit or push. Only modify files in the working tree so a human can review.';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Auto-mode prompt contradicts actual behavior

In auto mode the prompt tells the agent "Do not commit or push by yourself. Only modify files and explain what you changed." — but finalizeAutoMode (line 308) then commits with git add -A and git push origin HEAD:... on behalf of the agent. Meanwhile in review mode the instruction is nearly identical: "Do not commit or push. Only modify files..."

The auto mode instruction should make it clearer that the orchestrator will handle committing and pushing, not the agent. Right now both modes give virtually the same instruction, making auto mode indistinguishable to the agent.

return;
}

await this.finalizeAutoMode(candidate, branchKey, localHeadSha, remoteHeadAtStart);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remote-drift safety check silently skipped when remoteHeadAtStart is null

If getRemoteHeadSha returns null at the start (e.g. network error, new branch not yet pushed), remoteHeadAtStart is null. Then in finalizeAutoMode (line 315), the condition if (remoteHeadAtStart && remoteHeadBeforePush && ...) short-circuits and the remote-drift check is entirely bypassed. This means the push proceeds without any verification that the remote hasn't changed.

Consider logging a warning or treating a null remote head at start as an unknown state that should block auto-push, rather than silently skipping the safety check.

@singhvibhanshu
Copy link
Contributor Author

will apply the suggested changes and logic gaps soon!

@singhvibhanshu singhvibhanshu marked this pull request as draft March 16, 2026 17:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature: Auto-trigger agent on CI failure

1 participant