# CI Monitor Lifecycle in no-mistakes: Idle Timeout, Re-arming, and Termination

> Understand the no-mistakes CI monitor lifecycle. Learn about idle timeout, auto re-arming on branch updates, and termination triggers for efficient pull request management.

- Repository: [Kun Chen/no-mistakes](https://github.com/kunchenguid/no-mistakes)
- Tags: internals
- Published: 2026-07-25

---

**The CI monitor in no-mistakes watches pull requests with a configurable idle timeout that automatically re-arms whenever the base branch advances, terminating only when the PR merges, closes, or the timeout finally expires.**

The `no-mistakes` repository implements a sophisticated CI monitor that manages the lifecycle of automated runs against open pull requests. Understanding how this component handles idle timeouts and re-arming logic is essential for configuring reliable CI pipelines that don't prematurely abort during long-running reviews or rebase operations.

## Initialization and Timeout Configuration

The CI monitor lifecycle begins with configuration parsing in [`internal/config/config.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/config/config.go). The system reads the `ci_timeout` value from the merged configuration via `Config.CITimeout`. If this value is absent, the monitor falls back to `config.DefaultCITimeout`, which is hardcoded to **7 days**.

The `parseCITimeout` function in [`internal/config/config.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/config/config.go) (lines 78-95) implements the idle timeout semantics. It recognizes several keywords and non-positive durations as **CITimeoutUnlimited**:

- Negative values
- The strings **"unlimited"**, **"none"**, **"off"**, or **"never"**
- Any non-positive duration

When any of these are specified, the monitor enters an infinite waiting mode and never self-terminates due to idle time. Positive durations are interpreted as idle timeouts measured from a specific anchor point.

## The Polling Loop and Base Branch Detection

Once initialized, the monitor enters its polling phase in `CIStep.Execute` within [`internal/pipeline/steps/ci.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/pipeline/steps/ci.go) (lines 70-78). This function implements the main monitoring loop that repeatedly queries the CI provider for the pull request's check status.

The monitor uses a `baseBranchTip` resolver to track the upstream default branch. During each iteration, it compares the current base branch SHA against previous values to detect advancement. The idle timeout is always measured from a `timeoutAnchor` variable rather than a fixed start time, which enables the re-arming behavior.

## Re-arming Mechanics: Resetting the Timer on Base Branch Changes

The re-arming logic represents a critical aspect of the CI monitor lifecycle. When `baseBranchTip` reports a **new SHA** indicating the base branch has moved, the monitor executes the re-arming sequence at lines 94-99 of [`internal/pipeline/steps/ci.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/pipeline/steps/ci.go).

Specifically, the code resets `timeoutAnchor` to the current time (`now()`). This operation shifts the measurement reference point without modifying the timeout duration itself. Consequently, a pull request that is actively rebased against an advancing default branch **retains its monitoring session indefinitely**, preventing premature timeout during legitimate development workflows.

The timeout duration itself remains constant; only the anchor point moves forward in time.

## Auto-Fix Attempts and Termination Conditions

During each poll cycle, the monitor may attempt to automatically resolve failing checks. The `CIStep` struct tracks these attempts through the `ciFixAttempts` field, with the total allowed attempts governed by `Config.AutoFix.CI` as defined in [`config.go`](https://github.com/kunchenguid/no-mistakes/blob/main/config.go).

The CI monitor terminates when **any** of the following conditions occur:

1. **PR Merged**: The run is marked as *merged* and monitoring stops immediately via `CIStep.ReconcileApprovalGate`
2. **PR Closed**: The run is marked as *closed* and the monitor exits
3. **Idle Timeout Elapsed**: The system executes `no-mistakes axi abort --run <id>` to abort the run

These termination checks are implemented in `CIStep.ReconcileApprovalGate` and the timeout validation logic within `CIStep.Execute`.

## Configuration Examples

Configure the CI timeout globally in `~/.no-mistakes/config.yaml`:

```yaml

# ~/.no-mistakes/config.yaml

ci_timeout: "48h"   # monitor for up to 48 hours of inactivity

```

Repository-level overrides can be specified in [`.no-mistakes.yaml`](https://github.com/kunchenguid/no-mistakes/blob/main/.no-mistakes.yaml) for per-project timeouts.

Start a monitored run manually:

```bash

# Start a run that will monitor CI for PR #42

no-mistakes axi run --pr 42

```

During operation, the monitor indicates re-arming events in the output:

```text
base branch advanced, re-arming CI timeout

```

## Summary

- The CI monitor defaults to a **7-day idle timeout** configurable via `ci_timeout` in [`internal/config/config.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/config/config.go).
- Special keywords including **"unlimited"**, **"none"**, **"off"**, and **"never"** disable the idle timeout entirely via `CITimeoutUnlimited`.
- The monitor **re-arms** by resetting `timeoutAnchor` to the current time whenever `baseBranchTip` detects a new base branch SHA at lines 94-99 of [`internal/pipeline/steps/ci.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/pipeline/steps/ci.go).
- Auto-fix attempts are limited by `Config.AutoFix.CI` and tracked in the `ciFixAttempts` field.
- Termination occurs only upon PR merge, PR closure, or idle timeout expiration, handled in `CIStep.Execute` and `ReconcileApprovalGate`.

## Frequently Asked Questions

### What happens when the CI timeout is set to "unlimited"?

When the `ci_timeout` configuration contains "unlimited", "none", "off", "never", or any negative duration, the `parseCITimeout` function in [`internal/config/config.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/config/config.go) returns `CITimeoutUnlimited`. In this mode, the CI monitor never self-terminates due to idle time and will continue polling indefinitely until the pull request merges or closes.

### How does rebasing affect the CI monitor timer?

Rebasing triggers the re-arming mechanism in [`internal/pipeline/steps/ci.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/pipeline/steps/ci.go). When the `baseBranchTip` resolver detects that the default branch has advanced to a new SHA, the code resets the `timeoutAnchor` variable to the current time at lines 94-99. This effectively extends the monitoring window by moving the idle timeout measurement reference point forward, ensuring actively maintained pull requests do not expire.

### Where is the idle timeout duration defined in the source code?

The default idle timeout is defined as `DefaultCITimeout` (7 days) in [`internal/config/config.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/config/config.go). The active timeout for a specific run is retrieved from `Config.CITimeout`, with parsing logic handled by the `parseCITimeout` function in the same file. The effective timeout is applied in `CIStep.Execute` within [`internal/pipeline/steps/ci.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/pipeline/steps/ci.go).

### What triggers the CI monitor to stop watching a pull request?

According to the implementation in [`internal/pipeline/steps/ci.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/pipeline/steps/ci.go), the monitor terminates when three specific conditions occur: the pull request merges (marking the run as *merged*), the pull request closes (marking the run as *closed*), or the idle timeout expires relative to the last `timeoutAnchor` (triggering an abort command). These checks execute within `CIStep.Execute` and `ReconcileApprovalGate`.