---
name: "mergemaster should detect no-commits-ahead-of-master and fail loudly instead of exiting silently"
---

# Story 519: mergemaster should detect no-commits-ahead-of-master and fail loudly instead of exiting silently

## User Story

**Depends on story 520** (typed pipeline state machine). Once 520 lands, this story largely *evaporates*: `Stage::Merge` is defined as `Merge { feature_branch: BranchName, commits_ahead: NonZeroU32 }`, so a merge state with zero commits ahead is **structurally unrepresentable**. The transition `Current → Merge` (or `Qa → Merge`) is required to provide a NonZeroU32 — the type system enforces it. This story remains useful as a *defensive runtime check* during the migration window before 520 lands; afterwards, it should be closed as redundant.

---

**Note:** content stuffed into user_story per bug 509 workaround.

## Context

When mergemaster runs on a story whose worktree has **zero commits ahead of master** (e.g. because `create_worktree` always creates from master and the original feature branch was never checked out into the worktree), it currently:

1. Spawns its claude session
2. Runs `merge_agent_work` MCP tool
3. Finds nothing to merge
4. Exits cleanly with `[agent:N:mergemaster] Done. Session: ...`
5. **Does not log any error or warning**
6. **Spends real money** on the empty session — we observed `cost=$0.82` for one such no-op run

The user has no signal that the merge didn't actually happen. The matrix bot fires a "QA → Merge" stage notification (because the story did move stages internally), then nothing — no `🎉 Merge → Done` notification follows. Master is unchanged.

## Real user story

As a user watching the pipeline, I want mergemaster to detect "this worktree has no commits ahead of master" *before* spending money on a Claude session, and fail loudly with a clear error so I know to investigate the upstream cause (probably the worktree got reset to master).

## Observed 2026-04-09

Around 18:31:51, mergemaster spawned for 478 in a worktree that had been reset to master by the orphan cleanup logic at 18:29:54. By the time mergemaster ran, the worktree was on master with zero commits ahead. It ran a session, spent $0.82, exited "Done", and didn't merge anything. We didn't notice for several minutes because the failure was completely silent. We had to manually `git log master..feature/story-478_…` to confirm there was no merge commit on master.

## Fix

In mergemaster's startup sequence (probably in advance.rs or wherever the mergemaster session is spawned), add a pre-flight check:

```rust
let commits_ahead = git_commits_ahead(worktree_path, "master")?;
if commits_ahead == 0 {
    slog_error!(
        "[mergemaster] worktree {worktree_path} has no commits ahead of master; \
         refusing to spawn merge session. Likely cause: worktree was reset to \
         master after the feature branch's commits were created. Investigate the \
         worktree's git state before retrying."
    );
    return Err("no commits to merge".into());
}
```

This costs ~milliseconds (one git command) and saves the cost of an entire Claude session per false-positive.

## Context

When mergemaster runs on a story whose worktree has **zero commits ahead of master** (e.g. because `create_worktree` always creates from master and the original feature branch was never checked out into the worktree), it currently:

1. Spawns its claude session
2. Runs `merge_agent_work` MCP tool
3. Finds nothing to merge
4. Exits cleanly with `[agent:N:mergemaster] Done. Session: ...`
5. **Does not log any error or warning**
6. **Spends real money** on the empty session — we observed `cost=$0.82` for one such no-op run

The user has no signal that the merge didn't actually happen. The matrix bot fires a "QA → Merge" stage notification (because the story did move stages internally), then nothing — no `🎉 Merge → Done` notification follows. Master is unchanged.

## Real user story

As a user watching the pipeline, I want mergemaster to detect "this worktree has no commits ahead of master" *before* spending money on a Claude session, and fail loudly with a clear error so I know to investigate the upstream cause (probably the worktree got reset to master).

## Observed 2026-04-09

Around 18:31:51, mergemaster spawned for 478 in a worktree that had been reset to master by the orphan cleanup logic at 18:29:54. By the time mergemaster ran, the worktree was on master with zero commits ahead. It ran a session, spent $0.82, exited "Done", and didn't merge anything. We didn't notice for several minutes because the failure was completely silent. We had to manually `git log master..feature/story-478_…` to confirm there was no merge commit on master.

## Fix

In mergemaster's startup sequence (probably in advance.rs or wherever the mergemaster session is spawned), add a pre-flight check:

```rust
let commits_ahead = git_commits_ahead(worktree_path, "master")?;
if commits_ahead == 0 {
    slog_error!(
        "[mergemaster] worktree {worktree_path} has no commits ahead of master; \
         refusing to spawn merge session. Likely cause: worktree was reset to \
         master after the feature branch's commits were created. Investigate the \
         worktree's git state before retrying."
    );
    return Err("no commits to merge".into());
}
```

This costs ~milliseconds (one git command) and saves the cost of an entire Claude session per false-positive.

## Acceptance Criteria

- [ ] Before mergemaster spawns its Claude session, it runs `git log master..HEAD --oneline` (or equivalent) on the worktree
- [ ] If the result is empty (zero commits ahead), mergemaster exits early with an ERROR log line and does NOT spawn the session
- [ ] The error message is specific enough that the user can diagnose the upstream cause (e.g. mentions 'worktree was reset' and suggests checking the worktree's branch)
- [ ] The matrix bot sends a clear failure notification (NOT a successful 🎉 emoji) when this happens
- [ ] The story does not advance to a 'done' state when mergemaster exits this way; it stays in 4_merge with a clear blocked status
- [ ] Regression test: create a worktree on master (no feature commits), invoke mergemaster, assert the early exit happens and no Claude session is spawned
- [ ] Cost saving observed in the 2026-04-09 incident ($0.82 per no-op session) is documented in the test as the motivation

## Out of Scope

- TBD
