How CI Monitor Timeout Re-Arms on Base Branch Advances in no-mistakes

The CI monitor in no-mistakes restarts its idle timeout countdown by updating the timeoutAnchor to the current time whenever the base branch tip advances, allowing green PRs to remain monitored indefinitely while stalling ones eventually time out.

The no-mistakes repository implements a sophisticated CI monitor (the CIStep) that babysits open pull requests until they merge, close, or hit an idle timeout. Understanding how the CI monitor timeout re-arms when the base branch advances is essential for maintaining long-running green PRs that track active development branches without premature termination.

CITimeout Configuration and Initial Anchor Setup

The timeout duration is controlled by Config.CITimeout, which parses the ci_timeout entry from the global configuration file. In internal/config/config.go (lines 22‑28), this duration is loaded and validated, with support for standard duration strings or the keyword "unlimited".

When the CIStep initializes, it establishes two critical timestamps in internal/pipeline/steps/ci.go (lines 25‑30):

started := now()
timeoutAnchor := started

The started value remains fixed for the lifetime of the step, providing a stable reference for poll intervals and grace-period pacing. The timeoutAnchor, however, is the mutable reference point that determines when the idle timeout fires.

How Base Branch Advances Trigger Timeout Re-Arming

The re-arming logic operates on a polling cycle, checking the upstream default branch for new commits that would invalidate the current merge base.

Resolving the Base Branch Tip

On every poll (default 30‑second window), the monitor calls baseBranchTip to resolve the current SHA of the upstream default branch. This function returns the latest tip along with a resolution status, allowing the monitor to detect any movement in the base branch since the last check.

Detecting Advances and Updating the Anchor

In internal/pipeline/steps/ci.go (lines 56‑74), the monitor compares the resolved tip against lastBaseTip. When the SHA differs, indicating the base branch has advanced, the code logs the advance and immediately updates the timeout reference:

timeoutAnchor = now()

This assignment effectively re-arms the timeout, restarting the idle countdown from zero. Only base branch advances trigger this update; other activities such as CI checks re-running or status updates do not affect the anchor.

The Idle Timeout Check

Before each poll and after the base-branch validation, the monitor evaluates whether the idle limit has been exceeded:

if now().Sub(timeoutAnchor) >= timeout {
    // terminate with timeout outcome
}

Because the anchor moves forward only when the base branch advances, a PR that continuously rebases onto an active default branch can remain monitored indefinitely (or until the configured limit if not set to "unlimited"). Conversely, a PR whose base has stalled will eventually trigger the timeout and terminate.

Configuring the CI Timeout

You can configure the re-arming behavior through the global configuration file. To set a 7‑day idle timeout:


# ~/.no-mistakes/config.yaml

ci_timeout: "168h"

To disable the timeout entirely and allow the monitor to run forever:

ci_timeout: "unlimited"

When testing the re-arming logic, you can override the poll interval and stub the base branch resolver. In internal/pipeline/steps/ci_test.go, the test suite validates this behavior by simulating base branch advances and verifying that timeoutAnchor updates only when the tip changes:

step := &steps.CIStep{
    pollIntervalOverride: 5 * time.Second,
    // baseBranchTip can be stubbed to simulate advances
}

Summary

  • CITimeout is parsed from ci_timeout in internal/config/config.go (lines 22‑28) and controls the idle duration.
  • The timeoutAnchor initializes to the step start time in internal/pipeline/steps/ci.go (lines 25‑30) but mutates independently of the fixed started timestamp.
  • Base branch advances trigger re-arming by updating timeoutAnchor to now() in internal/pipeline/steps/ci.go (lines 56‑74).
  • Only tip changes restart the countdown; CI check re-runs do not affect the timeout.
  • The monitor polls every 30 seconds by default, checking now().Sub(timeoutAnchor) >= timeout to determine termination.

Frequently Asked Questions

What triggers the re-arming of the CITimeout?

The timeout re-arms exclusively when the base branch tip advances. When the SHA returned by baseBranchTip differs from the previously recorded tip, the monitor updates timeoutAnchor to the current time, effectively resetting the idle countdown to zero.

How do I configure an unlimited CI timeout?

Set ci_timeout: "unlimited" in your ~/.no-mistakes/config.yaml, or use any non‑positive duration value. This configuration prevents the idle timeout from firing regardless of how long the base branch remains static, allowing the monitor to run indefinitely.

Does the timeout re-arm when CI checks re-run?

No. The re-arming logic specifically ignores CI check activity and only responds to base branch tip changes. As implemented in internal/pipeline/steps/ci.go, the anchor update occurs only when the default branch SHA moves forward, ensuring that transient CI failures or re-runs do not extend the monitoring window.

Where is the re-arming logic implemented?

The core re-arming implementation resides in internal/pipeline/steps/ci.go (lines 56‑74), which contains the comparison between lastBaseTip and the current resolved tip, along with the timeoutAnchor update. The timeout duration configuration is defined in internal/config/config.go (lines 22‑28), and comprehensive tests validating the behavior are located in internal/pipeline/steps/ci_test.go.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →