Skip to content

Run now on a triage routine releases its stale branch first, like the sweep (#1643) - #1649

Merged
suleimansh merged 1 commit into
mainfrom
b1643-pinned-run-now
Aug 22, 2026
Merged

Run now on a triage routine releases its stale branch first, like the sweep (#1643)#1649
suleimansh merged 1 commit into
mainfrom
b1643-pinned-run-now

Conversation

@suleimansh

@suleimansh suleimansh commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

🤖 automated

Fixes #1643.

What changed

Run now on either triage routine was a plain start() from the card, outside the sweep — so it never reached the release the sweep does before firing a pinned job (releasePinned, #1293). A leftover copy of tf-triage-quick made every click abort in ~19 s as "triage already pending", $0.16–0.18 a time, until someone deleted the branch by hand.

The card now asks the sweep for that one routine, the way the plan click does since #1640:

  • AutoPmOnly gains { pinned: <branch> }. The card sends { only: { pinned: job.pinnedBranch }, projectId } for any routine that declares pinnedBranch; 'drain' and 'plan' are unchanged. The three routings now live in one narrowedSweep(job, projectId) in RoutineWork.tsx, decided by what the job declares (drains / fansOut / pinnedBranch).
  • The sweep resolves { pinned } against the enabled rotation by pinnedBranch, so an unticked box stands the click down (<label> is switched off); a branch no job pins at all is told apart (no routine is pinned to <branch>), since that is a dashboard older than its daemon rather than a setting.
  • The job a click named — plan or pinned — takes the tick outright (named): mode is pm however full the queue is, it outranks the maintenance calendar and the rotation index, and it does not advance the rotation. That is the same set of rules Run now on the planning routine spends the concurrency, not one agent (#1204) #1640 gave the plan click, now expressed once rather than per kind.
  • The concurrency cap, the quota boundary and live agents still hold; the cooldown is skipped for on-demand sweeps already (A routine's Run now is not held back by the sweep's cooldown (#1642) #1645).

Why this direction

#1643 offered two fixes: call releasePinned from the card, or route the click through the narrowed sweep. This is the second, for the reason the issue gives — it keeps one start path, and it is where #1640 already went. It also fixes the rest of what a card-fired triage skipped (the concurrency and quota gates, the stand-down reasons on the card) rather than only the release.

Keyed on the branch rather than the job's name, following the no-name-matching reasoning on AutoPmJob.drains / fansOut / pinnedBranch: the card sends whatever the job it renders declares, and the sweep matches on that same declared datum, so no string anywhere has to agree with a name and a rename cannot unhook it. A 'pinned' literal would not do — two jobs pin a branch — and a name would have widened only into "run any rotation job through the sweep", which is a different (and bigger) change than this issue asks for.

What this does not do. releaseStalePinnedBranch is deliberately conservative (#1293): it deletes only when the branch has PR history and none is open. A branch that never had a PR — exactly the dogfood repro on #1643 (tf-triage-quick at main, zero commits, no PR, held by the first triage's retained worktree) — comes back unproven and is kept, so on that branch the click now reaches the release and the release still declines. Whether "no PR, no commits past main" should count as stale is a data-safety call for the seam, not this PR; and git branch -D refuses anyway while a worktree has the branch checked out. (The empty tf-triage-quick a misfired click had pushed to this repo's origin was deleted by hand at 01:55, so a gemstack quick triage is no longer pre-blocked.)

Spec changes

  • src/auto-pm.SPEC.md — Flows: a new sentence that the two triage routines work on one fixed branch and that a leftover copy whose PR is closed/merged is deleted before firing, from the schedule or from Run now (a branch with an open PR or no PR history is kept). Rationales: a triage's Run now goes through the pass to get the same release the schedule gets.
  • dashboard/components/RoutineWork.SPEC.md — the Run now flow now says the two triage routines ask the sweep too, and why; "every other routine's Run now is one agent started directly".
  • FEATURES-SPEC.md — "Release a pinned routine branch left behind by a closed PR — before the schedule fires the routine, and before its 'Run now' does".
  • Both test SPECs updated to cover the new tests.

Verified

  • New tests: auto-pm.test.ts — a sweep narrowed to a pinned job releases the branch then starts exactly one agent (with the rotation on another job's turn and concurrency 3); a switched-off pinned routine stands the click down, an unknown branch is named as such, and the cap still holds; a narrowed sweep never falls through to the drain or another rotation job, and the next scheduled tick still gets the job it was owed. RoutineWork.test.tsx — each pinned routine's Run now calls sendAutoPmSweep({ only: { pinned }, projectId }) and never start(). quota.test.ts — the object form travels to the loop untouched.
  • Broke the fix on purpose (dropped the pinned lookup in tick and the pinned line in narrowedSweep, tests kept): exactly the 3 new node tests and the 1 new card test failed, everything else stayed green. Restored from the commit.
  • pnpm typecheck exit 0; pnpm test with the bundle built: node 1547/1547, dashboard 797/797. (A fresh worktree without dist/ fails 8 daemon.test.ts cases with "the dashboard bundle is not installed" — unrelated; they pass after pnpm build.)
  • Not dogfooded — it spends real Opus runs. The rig at ~/Projects/tf-1334-dogfood has the stale-branch condition ready if wanted.

… sweep (#1643)

Run now on either triage routine was a plain start outside the sweep, so it
never reached the release the sweep does before firing a pinned job. A leftover
copy of the triage branch made every click abort in seconds as "triage already
pending", until someone deleted the branch by hand.

The card now asks the sweep for that one routine — `only: { pinned: <branch> }`,
scoped to the picked project — the way the plan click does since #1640. The
sweep finds the job by the branch it declares, never by name; a switched-off
routine stands the click down with a reason; the named job takes the tick
outright, so neither a full queue nor the rotation index borrows it, and the
rotation keeps its turn.
@suleimansh

Copy link
Copy Markdown
Contributor Author

🤖 automated

Dogfooded on the rig (~/Projects/tf-1334-dogfood) against this branch's build, 2026-08-23 02:17–02:21 IDT, Opus, local. The rig was in the #1643 condition: tf-triage-quick existed locally at main with zero commits, checked out in the first triage's retained worktree, and the branch name had closed PR history (PR #6) — so the release seam could say released once the worktree stopped holding it. I removed that retained worktree by hand first; nothing else was changed.

1. Switched-off click stands down (free). With "Add quick-win work to AI Queue" unticked and the rig picked, Run now started nothing. Card note and daemon log both said Add quick-win work to AI Queue is switched off. Before this PR the same click was a plain start that ignored the box.

2. The click releases the stale branch, then starts (one run, $0.65). Ticked the routine back on via the card's own checkbox, clicked Run now again:

  • daemon: auto PM: Add quick-win work to AI Queue in /Users/suleiman/Projects/tf-1334-dogfood
  • git branch --list tf-triage-* in the rig: empty right after the click — the stale branch was deleted before the start
  • one run worktree appeared (tf-agent-2026-08-22T23-18-29-281Z, from main), session claude-code (opus), local, handoff armed
  • the agent's first step, verbatim: "I'll start by checking whether the session branch already exists … No existing branch — proceeding." — the exact point where the two earlier clicks aborted in 19 s
  • it then pinned its session to triage-quick and did real triage work: closed the two queue entries whose tickets are already merged (Design: @gemstack/ai-autopilot (orchestration / autonomy) #9/ci: add Changesets release workflow #10), bumped help-flag to priority 7, pushed as 7267cc6 on tf-data. ✓ finished, 1 turn, no code touched — Nothing committed — no PR to open, handoff skipped, as a triage should.

Net: the card reaches the same release-then-start as the sweep, the concurrency/quota/opt-out gates hold, and the sweep's note lands on the card instead of a navigation.

Two things the dogfood shows that this PR does not change (noted, not fixed):

  • A finished triage leaves a commitless tf-triage-quick behind again (the run's worktree was reclaimed; the branch stays). That is fine when the name has PR history — the seam releases it next click, which is what just happened — but a triage that pushes straight to tf-data never opens a PR on its own branch, so on a repo where the name never had one the seam keeps saying unproven and the click keeps dying. Same caveat as in the PR body; it is a policy call for the seam, not the card.
  • The rig's daemon keeps every retained worktree ("main is not on the remote" on this rig), and a retained worktree holding the branch makes git branch -D refuse even after a release. On gemstack the worktree sweep reclaims normally, so this is rig-specific, but it is the combination that made Run now on a pinned routine dies on its own stale branch: the card never releases it #1643 look permanent.

Restored afterwards: triage-quick opted out again, concurrency 2, daemon back on the main build from the main checkout.

@suleimansh
suleimansh merged commit d874944 into main Aug 22, 2026
2 checks passed
@suleimansh
suleimansh deleted the b1643-pinned-run-now branch August 22, 2026 23:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Run now on a pinned routine dies on its own stale branch: the card never releases it

1 participant