feat: detect stale worktree branches when main advances mid-session #136

Closed
opened 2026-03-21 16:07:22 +00:00 by forgejo_admin · 4 comments
Contributor

Type

Feature

Lineage

Discovered scope from plan-pal-e-agency Phase 16.
Siblings #193 (pre-spawn freshness), #194 (post-merge worktree cleanup), #195 (repo list fix) are done.
This ticket covers only the remaining gap not addressed by those siblings.

Repo

forgejo_admin/claude-custom

User Story

As a dev agent working in a worktree branch
I want to know when main has advanced due to external merges (other agents, CI, manual pushes) during my active session
So that I can avoid creating PRs against a stale base and reduce rebase churn

Context

What already exists (DO NOT duplicate):

  • post-merge-rebase.sh — PostToolUse on gh pr merge: fetches and fast-forwards local main after the current agent merges a PR.
  • post-mcp-merge-rebase.sh — PostToolUse on mcp__forgejo__merge_approved_pr: same for Forgejo MCP merges.
  • check-claude-custom-clean.sh — SessionStart: auto-pulls claude-custom main on session start.
  • cleanup-worktrees.sh — SessionStart: removes stale worktrees older than 7 days.
  • check-agent-spawn.sh — PreToolUse on Task: validates agent spawn schema (no freshness logic).

The gap: All existing freshness mechanisms fire at session boundaries (SessionStart) or after the current agent's own merges (PostToolUse). There is no detection when main advances due to external activity (a sibling agent merges, CI pushes, manual push) during an active session. The agent continues working against a stale base, discovers the divergence only at PR creation time, and must then rebase — sometimes with conflicts that waste the entire session.

Note on #193: Board item #507 (pre-spawn freshness) is marked done, but hooks/pre-spawn-freshness.sh does not exist in the repo and no reference exists in settings.json. If that work was never merged, pre-spawn freshness may need a separate ticket to reopen. This ticket does NOT absorb #193's scope — it covers only mid-session detection.

Resolved: Notification-only approach. Flag stale worktrees without touching them. Auto-rebase rejected — risks merge conflicts mid-agent-work.

File Targets

Files the agent should create or modify:

  • hooks/check-branch-freshness.sh — new hook: fetches origin/main, compares merge-base of current branch against origin/main HEAD, emits warning if diverged
  • settings.json — wire the new hook (trigger event TBD based on decision above)

Files the agent should NOT touch:

  • hooks/post-merge-rebase.sh — handles same-session merges, working correctly
  • hooks/post-mcp-merge-rebase.sh — handles MCP merges, working correctly
  • hooks/check-agent-spawn.sh — spawn validation, not freshness
  • plugins/ — config-only directory, not for custom automation

Acceptance Criteria

  • When origin/main has advanced past the current branch's merge-base during an active session, the agent receives a clear warning message indicating the branch is stale and how many commits behind
  • The check does not fire on every tool call (performance) — trigger should be scoped to a meaningful gate (e.g., PreToolUse on gh pr create / mcp__forgejo__submit_pr, or a periodic SessionStart-like cadence)
  • The hook does NOT auto-rebase or modify the working tree (notification-only until the design decision above is resolved)
  • Existing post-merge-rebase hooks continue to function independently — no double-fetch or conflicting logic

Test Expectations

  • Manual test: agent is on a worktree branch, a second terminal pushes a commit to main, agent's next gated action triggers the freshness warning
  • Manual test: agent is on a worktree branch, main has NOT advanced, no warning is emitted
  • Manual test: after agent's own gh pr merge, post-merge-rebase.sh still fires correctly (no interference)

Constraints

  • Follow existing hook patterns: set -euo pipefail, read $INPUT from stdin via jq, emit structured JSON for PreToolUse or plain stderr for notifications
  • Hook must handle both GitHub (origin) and Forgejo (forgejo) remotes — detect the correct remote for main
  • Must work from within worktree directories (not just the main checkout)
  • Keep the fetch lightweight — git fetch origin main --quiet only, not a full fetch

Checklist

  • PR opened
  • Tests pass (manual verification documented in PR)
  • No unrelated changes
  • worktree-workflow SOP updated in pal-e-docs (separate deliverable via Dottie)
  • worktree-workflow SOP in pal-e-docs
  • convention-worktree-isolation in pal-e-docs
  • #193 — pre-spawn freshness (marked done, delivery unverified)
  • #194 — post-merge worktree cleanup (done, delivered)
  • #195 — repo list fix (done)
  • pal-e-platform#188 / board item #418 — cross-repo worktree isolation (complementary, not blocking)
### Type Feature ### Lineage Discovered scope from plan-pal-e-agency Phase 16. Siblings #193 (pre-spawn freshness), #194 (post-merge worktree cleanup), #195 (repo list fix) are **done**. This ticket covers only the **remaining gap** not addressed by those siblings. ### Repo `forgejo_admin/claude-custom` ### User Story As a dev agent working in a worktree branch I want to know when `main` has advanced due to external merges (other agents, CI, manual pushes) during my active session So that I can avoid creating PRs against a stale base and reduce rebase churn ### Context **What already exists (DO NOT duplicate):** - `post-merge-rebase.sh` — PostToolUse on `gh pr merge`: fetches and fast-forwards local main after the *current* agent merges a PR. - `post-mcp-merge-rebase.sh` — PostToolUse on `mcp__forgejo__merge_approved_pr`: same for Forgejo MCP merges. - `check-claude-custom-clean.sh` — SessionStart: auto-pulls claude-custom main on session start. - `cleanup-worktrees.sh` — SessionStart: removes stale worktrees older than 7 days. - `check-agent-spawn.sh` — PreToolUse on Task: validates agent spawn schema (no freshness logic). **The gap:** All existing freshness mechanisms fire at session boundaries (SessionStart) or after the current agent's own merges (PostToolUse). There is **no detection** when `main` advances due to *external* activity (a sibling agent merges, CI pushes, manual push) **during** an active session. The agent continues working against a stale base, discovers the divergence only at PR creation time, and must then rebase — sometimes with conflicts that waste the entire session. **Note on #193:** Board item #507 (pre-spawn freshness) is marked `done`, but `hooks/pre-spawn-freshness.sh` does not exist in the repo and no reference exists in `settings.json`. If that work was never merged, pre-spawn freshness may need a separate ticket to reopen. This ticket does NOT absorb #193's scope — it covers only mid-session detection. **Resolved:** Notification-only approach. Flag stale worktrees without touching them. Auto-rebase rejected — risks merge conflicts mid-agent-work. ### File Targets Files the agent should create or modify: - `hooks/check-branch-freshness.sh` — new hook: fetches origin/main, compares merge-base of current branch against origin/main HEAD, emits warning if diverged - `settings.json` — wire the new hook (trigger event TBD based on decision above) Files the agent should NOT touch: - `hooks/post-merge-rebase.sh` — handles same-session merges, working correctly - `hooks/post-mcp-merge-rebase.sh` — handles MCP merges, working correctly - `hooks/check-agent-spawn.sh` — spawn validation, not freshness - `plugins/` — config-only directory, not for custom automation ### Acceptance Criteria - [ ] When `origin/main` has advanced past the current branch's merge-base during an active session, the agent receives a clear warning message indicating the branch is stale and how many commits behind - [ ] The check does not fire on every tool call (performance) — trigger should be scoped to a meaningful gate (e.g., PreToolUse on `gh pr create` / `mcp__forgejo__submit_pr`, or a periodic SessionStart-like cadence) - [ ] The hook does NOT auto-rebase or modify the working tree (notification-only until the design decision above is resolved) - [ ] Existing post-merge-rebase hooks continue to function independently — no double-fetch or conflicting logic ### Test Expectations - [ ] Manual test: agent is on a worktree branch, a second terminal pushes a commit to main, agent's next gated action triggers the freshness warning - [ ] Manual test: agent is on a worktree branch, main has NOT advanced, no warning is emitted - [ ] Manual test: after agent's own `gh pr merge`, `post-merge-rebase.sh` still fires correctly (no interference) ### Constraints - Follow existing hook patterns: `set -euo pipefail`, read `$INPUT` from stdin via `jq`, emit structured JSON for PreToolUse or plain stderr for notifications - Hook must handle both GitHub (`origin`) and Forgejo (`forgejo`) remotes — detect the correct remote for main - Must work from within worktree directories (not just the main checkout) - Keep the fetch lightweight — `git fetch origin main --quiet` only, not a full fetch ### Checklist - [ ] PR opened - [ ] Tests pass (manual verification documented in PR) - [ ] No unrelated changes - [ ] worktree-workflow SOP updated in pal-e-docs (separate deliverable via Dottie) ### Related - `worktree-workflow` SOP in pal-e-docs - `convention-worktree-isolation` in pal-e-docs - #193 — pre-spawn freshness (marked done, delivery unverified) - #194 — post-merge worktree cleanup (done, delivered) - #195 — repo list fix (done) - pal-e-platform#188 / board item #418 — cross-repo worktree isolation (complementary, not blocking)
Author
Contributor

Scope Review: NEEDS_REFINEMENT

Review note: review-241-2026-03-27

The ticket has all required template sections but needs refinement before it's agent-ready.

Key issues:

  • Missing arch:hooks label on board item #241 — traceability gap
  • File targets are wrong: plugins/worktree-rebase/ is not a valid location (plugins/ is a plugin manager config dir, not custom code). All automation lives in hooks/. Replace with e.g. hooks/check-branch-freshness.sh
  • Context ignores existing infrastructure: post-merge-rebase.sh and post-mcp-merge-rebase.sh already fast-forward local main after merges. The remaining gap is specifically external merges (other agents, CI). The Context section should acknowledge what already works
  • Acceptance criteria are ambiguous: AC1 doesn't specify the trigger event (SessionStart? PreToolUse?). AC3 uses "or" between auto-rebase and notification — these are fundamentally different implementations with different blast radius
  • Auto-rebase blast radius: Rebasing an in-progress branch can corrupt the working tree. Recommendation: notification-only is safer; the ticket should decide

See full review in pal-e-docs: review-241-2026-03-27

## Scope Review: NEEDS_REFINEMENT Review note: `review-241-2026-03-27` The ticket has all required template sections but needs refinement before it's agent-ready. **Key issues:** - **Missing `arch:hooks` label** on board item #241 — traceability gap - **File targets are wrong:** `plugins/worktree-rebase/` is not a valid location (plugins/ is a plugin manager config dir, not custom code). All automation lives in `hooks/`. Replace with e.g. `hooks/check-branch-freshness.sh` - **Context ignores existing infrastructure:** `post-merge-rebase.sh` and `post-mcp-merge-rebase.sh` already fast-forward local main after merges. The remaining gap is specifically external merges (other agents, CI). The Context section should acknowledge what already works - **Acceptance criteria are ambiguous:** AC1 doesn't specify the trigger event (SessionStart? PreToolUse?). AC3 uses "or" between auto-rebase and notification — these are fundamentally different implementations with different blast radius - **Auto-rebase blast radius:** Rebasing an in-progress branch can corrupt the working tree. Recommendation: notification-only is safer; the ticket should decide See full review in pal-e-docs: `review-241-2026-03-27`
Author
Contributor

Issue body updated per scope review corrections.

Issue body updated per scope review corrections.
Author
Contributor

Scope Review: NEEDS_REFINEMENT (v2)

Review note: review-241-2026-03-27-v2

The issue body is the literal string $NEW_BODY — a shell variable that was never expanded during the previous refinement update (comment #9200). The entire ticket spec is destroyed and the ticket is unexecutable.

Blocking issues:

  • Body corrupted. Restore from original content (referenced in review-241-2026-03-27) and apply the 5 refinements from that first review.
  • Scope overlap with completed work. Issues #193 (pre-spawn freshness), #194 (post-merge cleanup), and #184 (worktree enforcement gaps) have all been marked done since #136 was written. The remaining uncovered gap is narrow: mid-session detection of external merges to main. Re-scope needed.
  • #193 completion is suspect. Board item #507 is marked done but hooks/pre-spawn-freshness.sh does not exist in the repo and is not wired in settings.json. Verify whether the PR was actually merged — if not, #136 may need to absorb that scope or #193 should be reopened.
  • Auto-rebase vs. notification still unresolved. Original AC used ambiguous "or" language. Pick one approach before refinement.

Labels are correct (no changes needed).

## Scope Review: NEEDS_REFINEMENT (v2) Review note: `review-241-2026-03-27-v2` The issue body is the literal string `$NEW_BODY` — a shell variable that was never expanded during the previous refinement update (comment #9200). The entire ticket spec is destroyed and the ticket is unexecutable. **Blocking issues:** - **Body corrupted.** Restore from original content (referenced in review-241-2026-03-27) and apply the 5 refinements from that first review. - **Scope overlap with completed work.** Issues #193 (pre-spawn freshness), #194 (post-merge cleanup), and #184 (worktree enforcement gaps) have all been marked done since #136 was written. The remaining uncovered gap is narrow: mid-session detection of external merges to main. Re-scope needed. - **#193 completion is suspect.** Board item #507 is marked done but `hooks/pre-spawn-freshness.sh` does not exist in the repo and is not wired in `settings.json`. Verify whether the PR was actually merged — if not, #136 may need to absorb that scope or #193 should be reopened. - **Auto-rebase vs. notification still unresolved.** Original AC used ambiguous "or" language. Pick one approach before refinement. Labels are correct (no changes needed).
forgejo_admin changed title from Worktree flow: auto-rebase branches when main advances to feat: detect stale worktree branches when main advances mid-session 2026-03-28 05:49:11 +00:00
Author
Contributor

Scope Review: READY (v3)

Review note: review-241-2026-03-27-v3

Rewritten body resolves all prior review findings. All template sections present and complete. Traceability triangle verified (story:dev-execute, arch:hooks, Forgejo issue open). File targets confirmed against codebase. Notification-only decision is resolved. 2 file targets, 1 repo, 4 ACs — well within single-agent scope.

Minor nit (non-blocking):

  • "Note on #193" paragraph says delivery unverified, but #193 was merged (commit 356c83a, hook wired in settings.json on main). Consider updating to avoid confusing the implementing agent. The paragraph already correctly scopes #136 away from #193, so this is informational only.
## Scope Review: READY (v3) Review note: `review-241-2026-03-27-v3` Rewritten body resolves all prior review findings. All template sections present and complete. Traceability triangle verified (story:dev-execute, arch:hooks, Forgejo issue open). File targets confirmed against codebase. Notification-only decision is resolved. 2 file targets, 1 repo, 4 ACs — well within single-agent scope. **Minor nit (non-blocking):** - "Note on #193" paragraph says delivery unverified, but #193 was merged (commit `356c83a`, hook wired in `settings.json` on main). Consider updating to avoid confusing the implementing agent. The paragraph already correctly scopes #136 away from #193, so this is informational only.
forgejo_admin 2026-03-28 11:51:41 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
ldraney/claude-custom#136
No description provided.