What Does "Re-arming" on Base Branch Advancement Mean in no-mistakes CI?

Re-arming resets the idle timeout clock to the current time whenever the base branch advances, giving open pull requests a fresh monitoring window while preserving the original polling schedule.

The kunchenguid/no-mistakes CI monitor watches open pull requests until they merge, close, or hit an idle timeout. When the upstream default branch moves forward while a PR remains open, the monitor "re-arms" to prevent premature termination of healthy but waiting jobs.

The Timeout Anchor Pattern

In internal/pipeline/steps/ci.go, the monitor tracks two distinct time markers. The started variable records when monitoring began, anchoring poll intervals and grace periods. The timeoutAnchor variable determines when the idle timeout fires.

According to the source comments at lines 126-129:

// timeoutAnchor is the point the idle timeout is measured from. It re-arms
// to now() whenever the base branch advances, while started stays fixed
// so poll-interval and grace-period pacing are unaffected by re-arming.
timeoutAnchor := started

This separation allows the monitor to reset its deadline without disrupting the overall timing rhythm.

How Re-Arming Works in internal/pipeline/steps/ci.go

The re-arming logic resides in the polling loop between lines 56-73. During each iteration, the code fetches the current tip of the default branch:

tip, resolved := baseBranchTip(tipCtx)
...
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()          // ← re-arm
        lastBaseTip = tip
    }
}

When tip differs from lastBaseTip, the base branch has advanced. The monitor logs the event and sets timeoutAnchor = now(), effectively giving the PR a fresh timeout window.

Why Re-Arming Matters for CI Stability

Without re-arming, long-running PRs that wait for base branch updates would accumulate idle time and abort unnecessarily. The mechanism distinguishes between:

  • Stagnant PRs: No activity on the PR or base branch (subject to timeout).
  • Active ecosystems: The base branch moves forward, signaling ongoing development that justifies keeping the PR monitor alive.

By resetting only timeoutAnchor and leaving started untouched, the monitor maintains consistent poll intervals while extending the deadline.

Practical Implementation Example

The following Go code illustrates the re-arming logic extracted from the CI step. It simulates a base branch advancing while monitoring a PR:

package main

import (
	"context"
	"fmt"
	"time"
)

func main() {
	// Simulated base-branch tip provider.
	var tips = []string{"aaa111", "bbb222"} // first call returns old tip, second call new tip
	idx := 0
	baseBranchTip := func(ctx context.Context) (string, bool) {
		if idx < len(tips) {
			tip := tips[idx]
			idx++
			return tip, true
		}
		return "", false
	}

	now := time.Now
	started := now()
	timeout := 10 * time.Minute
	timeoutAnchor := started
	lastBaseTip := ""

	// First poll – sees first tip, sets lastBaseTip.
	tip, _ := baseBranchTip(context.Background())
	lastBaseTip = tip
	fmt.Printf("initial tip %s, timeoutAnchor %s\n", tip, timeoutAnchor)

	// Simulate some elapsed time.
	time.Sleep(2 * time.Second)

	// Second poll – sees a different tip, re-arms.
	tip, _ = baseBranchTip(context.Background())
	if tip != "" && tip != lastBaseTip {
		fmt.Printf("base branch advanced (%s → %s), re-arming timeout\n", lastBaseTip, tip)
		timeoutAnchor = now() // re-arm
		lastBaseTip = tip
	}
	fmt.Printf("new timeoutAnchor %s (elapsed %s)\n",
		timeoutAnchor, now().Sub(started))

	// Check if timeout would fire (it won't because we re-armed).
	if now().Sub(timeoutAnchor) >= timeout {
		fmt.Println("timeout reached")
	} else {
		fmt.Println("still within timeout")
	}
}

Running this produces output matching the production logs:


initial tip aaa111, timeoutAnchor 2026-06-27 14:00:00 +0000 UTC
base branch advanced (aaa111 → bbb222), re-arming timeout
new timeoutAnchor 2026-06-27 14:00:02 +0000 UTC (elapsed 2s)
still within timeout

Key Source Files

Summary

  • Re-arming resets the timeoutAnchor to the current time when the base branch advances, preventing premature idle timeouts.
  • The started variable remains fixed to preserve poll-interval pacing, while timeoutAnchor moves forward.
  • The logic detects branch advancement via the baseBranchTip function in internal/pipeline/steps/ci.go.
  • This mechanism ensures PRs waiting for base branch updates are not aborted while maintaining strict timeouts for truly stagnant pull requests.

Frequently Asked Questions

What triggers a re-arm event in no-mistakes CI?

A re-arm event triggers when the monitor detects that the upstream default branch tip has changed since the last poll. Specifically, when tip != lastBaseTip in internal/pipeline/steps/ci.go, the code resets timeoutAnchor to the current time.

Does re-arming affect the poll interval or grace period?

No. Re-arming only modifies timeoutAnchor. The started variable, which governs poll intervals and grace periods, remains unchanged. This ensures the monitoring cadence stays consistent even as the timeout deadline shifts.

How does re-arming differ from restarting the CI monitor?

Re-arming is a lightweight timeout extension that occurs within the same monitoring session. A restart would reset all state and begin fresh tracking. Re-arming preserves the existing started timestamp and only slides the idle timeout window forward.

Where is the idle timeout duration configured?

The timeout duration is defined in internal/config/config.go and loaded into the CI step. This configuration determines how long the monitor waits after timeoutAnchor before considering the PR idle, regardless of how many times re-arming occurs.

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 →