Reflection follow-up: heartbeat-daily rebase of CONFLICTING PRs via bin/rebase-stale-prs.sh
failedFrom 2026-06-21 reflection: workspace hit 3 CONFLICTING PRs tonight at 50 open PRs and 0 merges in 24h: #12530 (DS-fix for #12499 ALL-1957), #12529 (ALL-1953 debounced re-geocoding), #9996 (old OEM tesla command.executed). Yesterday's 6/20 watch-for: 'if next reflection finds 3+ CONFLICTING branches, the right structural fix is a bin/rebase-stale-prs.sh script the heartbeat runs once per day to keep merge-ability fresh.' We hit 3 tonight. Without an automated rebase pass, drift compounds at the 50+ open-PR scale where Will batches every 5-7 days.
Script spec (bin/rebase-stale-prs.sh): (1) source gh-fleet-token.sh to refresh GH_TOKEN. (2) List CONFLICTING PRs authored by app/texture-fleet-agent via gh pr list with --json number,title,mergeable,headRefName --jq '.[] | select(.mergeable=="CONFLICTING")'. (3) Skip if branch is in DO_NOT_REBASE_BRANCHES allowlist OR has an active sub-agent claim (file lock / fleet-task in_progress with this branch) OR PR is draft / non-bot CHANGES_REQUESTED. (4) For each remaining: git fetch && worktree add && git rebase origin/main. Clean rebase => push --force-with-lease and re-verify CI. Conflicts requiring judgment => log and skip (do NOT auto-resolve semantic conflicts). (5) Emit summary: N attempted, M succeeded, K skipped/conflicts.
Heartbeat wiring: call once per heartbeat day at a quiet hour (e.g. 05:00 UTC, after reflection-cron's 04:05 UTC slot). Daily; quiet-day detection optional.
Acceptance: next reflection finds CONFLICTING count flat-or-falling vs. tonight's 3 (if Will did not merge anything overnight). If still 3+ and the script ran clean, residual CONFLICTING PRs are semantic-conflict cases that need cognitive intervention and get flagged in the reflection.
Priority 3 (drift management at scale; not blocking).
---
**AMENDMENT 2026-06-22 from reflection-cron:** before this script runs, a triage gate is required. Last night the workspace was claimed to have 3 CONFLICTING PRs; tonight the real count is **27** (the original 3 estimate was a gh CLI --limit 30 truncation artifact). At 27 CONFLICTING with the oldest PR (#9583, ALL-571) from 2026-04-22 (2+ months old), rebase-everything is the wrong default. Many of the oldest CONFLICTING PRs have likely been superseded by other work, may reference deleted code paths, or have never received a review since they were opened.
**Triage gate (run BEFORE any rebase attempt):**
1. List all CONFLICTING PRs authored by app/texture-fleet-agent with `gh pr list --limit 200 --json number,title,createdAt,reviewDecision,headRefName --jq ".[]|select(.mergeable==\"CONFLICTING\")"`.
2. Partition by age:
- **RECENT** (createdAt within last 30 days): candidate for clean rebase. Proceed to rebase logic.
- **STALE** (createdAt 30-60 days old): list and surface to Will as a separate triage list — do not auto-rebase. Many of these may still be salvageable but a human glance is cheap.
- **DERELICT** (createdAt 60+ days old): list and emit a recommendation "consider close-as-superseded" to Will. Do not auto-rebase, do not auto-close. Surface only.
3. For each RECENT CONFLICTING PR, proceed with the original rebase logic (skip allowlists, worktree add, rebase origin/main, push --force-with-lease, re-verify CI).
4. Emit summary: `RECENT=N attempted, M succeeded, K skipped/conflicts; STALE=N surfaced for triage; DERELICT=N surfaced for close-as-superseded review.`
**Output format for STALE/DERELICT lists:** machine-readable JSONL into ~/agents/will-engineer/triage-conflicting-prs.jsonl, one row per PR with {number, title, createdAt, ageDays, suggestion}. The next reflection can read this file and decide whether to DM Will a batched triage-list, or whether to silently let the file accumulate until Will asks.
This amendment matters because at 27 CONFLICTING with the oldest 2 months old, blindly rebasing all of them would (a) consume significant heartbeat compute on rebases that will fail semantic conflicts, (b) potentially force-push branches whose owners may have intended to abandon them, (c) generate a lot of CI noise. Triage-then-rebase is the right shape; rebase-all is not.
Acceptance unchanged in spirit but extended: next reflection finds (RECENT CONFLICTING count flat-or-falling vs. tonights count of RECENT-CONFLICTING) AND has the STALE/DERELICT triage list available for review.
Event Timeline
created
status_change
queued → in_progress
failed
lease expired — re-queued for retry
in_progress → queued
status_change
queued → in_progress
failed
lease expired — re-queued for retry
in_progress → queued
status_change
queued → in_progress
failed
lease expired — max retries reached, marking failed (poison pill)
in_progress → failed