Understanding the CI Monitor Timeout in no-mistakes: Configuration and Behavior

The CI monitor timeout in no-mistakes defaults to 7 days of idle time, automatically re-arms when the base branch advances to prevent premature termination, and supports an unlimited mode that runs until the PR is merged, closed, or manually aborted.

The no-mistakes repository provides automation for managing pull requests through continuous integration monitoring. Understanding the CI monitor timeout behavior is essential for configuring pipeline durations that accommodate long-running tests or active development cycles without interrupting active monitoring sessions.

Configuration Constants and Default Values

The timeout behavior is governed by constants defined in internal/config/config.go and parsed from user configuration files.

Default Timeout Duration

The DefaultCITimeout constant is set to 7 days (168 hours) as defined in internal/config/config.go at line 31. This value applies when the user does not explicitly configure a ci_timeout value in either the global configuration (~/.no-mistakes/config.yaml) or the repository configuration (.no-mistakes.yaml).

Unlimited Mode Sentinel

The CITimeoutUnlimited constant acts as a sentinel value (-1) defined in internal/config/config.go at line 33. When this value is active, the monitor bypasses all idle timeout checks and runs indefinitely until external termination conditions are met.

How the Timeout Logic Executes

The CI step implements timeout resolution logic in internal/pipeline/steps/ci.go (lines 101–108). The code evaluates the configured duration at runtime:

timeout := sctx.Config.CITimeout          // user-provided or default
unlimited := timeout < 0                  // <0 → “unlimited”
if timeout == 0 {                         // not set → use default
    timeout = config.DefaultCITimeout
}

If unlimited evaluates to true, the monitor never self-terminates based on time. Instead, it only stops when the PR is merged, closed, or the user aborts the run via no-mistakes axi abort --run <id>. For finite timeouts (values greater than 0), the monitor tracks idle time against a moving anchor point.

Re-arming Mechanism on Base Branch Advancement

The CI monitor employs an idle timeout strategy that resets whenever the upstream default branch advances. This prevents premature termination during active development cycles where the base branch receives frequent updates.

The implementation maintains two critical timestamps in internal/pipeline/steps/ci.go:

  • started: Fixed timestamp when the monitor began
  • timeoutAnchor: The reference point for idle time calculations, updated dynamically

The re-arming logic (lines 126–134) resolves the default branch tip and compares it against the previously stored tip:

if !unlimited {
    resolveWindow := defaultBaseBranchTipResolveWindow
    tipCtx, cancel := context.WithTimeout(ctx, resolveWindow)
    tip, resolved := baseBranchTip(tipCtx)
    cancel()
    if resolved && tip != "" {
        if lastBaseTip == "" {
            lastBaseTip = tip
        } else if tip != lastBaseTip {
            sctx.Log(fmt.Sprintf("base branch advanced (%s..%s), re‑arming CI monitor timeout",
                shortSHA(lastBaseTip), shortSHA(tip)))
            timeoutAnchor = now()                // reset idle timer
            lastBaseTip = tip
        }
    }
}

Before each poll cycle (lines 52–55 and 152–155), the logic checks now().Sub(timeoutAnchor) against the configured timeout. When the base branch tip changes, timeoutAnchor resets to the current time, effectively extending the allowed monitoring period. The poll interval and grace periods operate independently of this re-arming mechanism.

Termination Outcomes When Timeout Fires

If the idle timeout elapses without re-arming, the timeoutOutcome() function (lines 36–44) returns one of three distinct outcomes:

  • CI timeout with failures: The run ends with failure status, preserving the list of still-failing checks
  • Merge-ability timeout: The PR’s mergeability status could not be resolved before the timeout expired
  • Pure monitoring timeout: No failures were observed; the run ends cleanly via ciMonitoringTimeoutOutcome()

Configuring CI Monitor Timeouts

Setting a Custom Duration via Global Configuration

Create or edit ~/.no-mistakes/config.yaml to specify a custom idle timeout:


# ~/.no-mistakes/config.yaml

ci_timeout: "48h"      # 2 days idle timeout (default is 168h = 7 days)

The parseCITimeout function in config.go accepts standard duration strings. Any non-positive duration (including "0", "-5m") or the keywords "unlimited", "none", "off", and "never" resolve to CITimeoutUnlimited.

Enabling Unlimited Mode

To disable timeout enforcement entirely:

ci_timeout: "unlimited"

In this mode, the monitor continues until the PR is merged, closed, or manually aborted.

Environment Variable Override

For ad-hoc executions or CI pipelines requiring temporary timeout adjustments:

NO_MISTAKES_CI_TIMEOUT=5m no-mistakes run .

The environment variable overrides both global and repository configuration values.

Observing Re-arming in Logs

When the base branch advances, the monitor emits a log entry from the re-arming logic in ci.go:


2026-06-27T12:34:56Z monitoring CI for PR #42 (timeout: 48h)...
2026-06-28T09:00:12Z base branch advanced (a1b2c3d..d4e5f6g), re‑arming CI monitor timeout
2026-06-28T09:00:12Z CI checks passed – waiting for next poll…

Summary

  • The default CI monitor timeout is 7 days (DefaultCITimeout), configurable via ci_timeout in YAML or the NO_MISTAKES_CI_TIMEOUT environment variable
  • Set ci_timeout to "unlimited" or -1 to enable CITimeoutUnlimited mode, which runs until the PR is merged, closed, or aborted
  • The idle timeout resets (re-arms) automatically when the base branch advances, tracked via timeoutAnchor in internal/pipeline/steps/ci.go
  • Timeout expiration triggers one of three outcomes: failure with preserved check statuses, merge-ability timeout, or clean monitoring timeout
  • Configuration parsing and validation occur in internal/config/config.go, while monitoring logic resides in internal/pipeline/steps/ci.go

Frequently Asked Questions

What is the default CI monitor timeout in no-mistakes?

The default timeout is 7 days (168 hours), defined as the DefaultCITimeout constant in internal/config/config.go. This applies when no ci_timeout value is specified in configuration files or environment variables.

How does the re-arming mechanism work when the base branch advances?

When the CI monitor detects that the default branch tip has changed (comparing tip against lastBaseTip in internal/pipeline/steps/ci.go lines 126–134), it updates timeoutAnchor to the current time. This resets the idle timer to zero, effectively extending the monitoring period by the full configured duration from the moment of advancement.

What is the difference between a finite timeout and unlimited mode?

A finite timeout (positive duration) causes the monitor to terminate after the specified idle period unless the base branch advances to re-arm the timer. Unlimited mode (activated by setting ci_timeout to "unlimited" or any negative value) disables the idle timer entirely; the monitor continues running until the PR is merged, closed, or manually aborted via the CLI.

How can I override the CI timeout for a single run without changing configuration files?

Set the NO_MISTAKES_CI_TIMEOUT environment variable before invoking the run command. For example, NO_MISTAKES_CI_TIMEOUT=30m no-mistakes run . applies a 30-minute timeout for that specific execution, overriding values from both global (~/.no-mistakes/config.yaml) and repository-level (.no-mistakes.yaml) configurations.

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 →