Skip to content

feat: long-running worktrees for /fresh + autopilot sequence mode#26

Merged
iceglober merged 1 commit into
mainfrom
wt-260420-164647-omg
Apr 21, 2026
Merged

feat: long-running worktrees for /fresh + autopilot sequence mode#26
iceglober merged 1 commit into
mainfrom
wt-260420-164647-omg

Conversation

@iceglober

Copy link
Copy Markdown
Owner

Summary

Reshapes /fresh around the long-running-worktree model and extends /autopilot with a sequence-of-issues mode. Companion plugin changes let the autopilot plugin detect /fresh re-key transitions and nudge the agent to pick up the new task from a handoff brief.

Motivation

The previous /fresh created a new worktree directory every invocation. In practice, engineers keep 7-10 terminal tabs open — one per persistent worktree — and want /fresh to transition the current tab to a new task without creating a new directory, killing the session, or touching sibling worktrees' Postgres/docker/dev-server state.

Two concrete inefficiencies in the old flow drove this:

  1. Pivoting to a new issue mid-session tore down the just-provisioned worktree and recreated it — ~30s of pnpm install + Postgres slot reshuffle for what should be a branch rename.
  2. The old flow ended with "cd to your new worktree" — impossible inside a running OpenCode TUI (research confirms this is a platform limitation, not a fixable agent behavior). The long-running-worktree model sidesteps the problem entirely: the session is already where the work happens.

What changed

home/.claude/commands/fresh.md — full rewrite

  • New model: /fresh re-keys the current worktree. No gsag wt new. No cd hint.
  • Pipeline: verify worktree context → parse ref / derive branch → safety check on dirty tree → git fetch origin <base>git checkout -b <new-branch> origin/<base> → invoke .glorious/hooks/fresh-reset → write .agent/fresh-handoff.md → reset autopilot state → summary.
  • New --yes flag for non-interactive invocation (autopilot/orchestrator callers). Deterministic decision matrix:
    • gitignored debris → discard silently
    • tracked changes → abort with file list
    • untracked non-gitignored → abort with file list
    • empty args → abort
  • New repo hook contract: .glorious/hooks/fresh-reset receives WORKTREE_DIR, WORKTREE_NAME, OLD_BRANCH, NEW_BRANCH, BASE_BRANCH + pass-through args. Hook is responsible for scoping cleanup to this worktree only (never touches siblings).
  • Writes .agent/fresh-handoff.md with tracker context, branch info, hook output — the canonical artifact the agent reads to pick up the new task.
  • Atomically zeros .agent/autopilot-state.json iteration counters so the plugin treats the next idle event as a fresh start.

home/.claude/commands/autopilot.md — sequence mode added

  • New classification branch: "Issue-tracker queue / project reference" (Linear project URLs, GitHub milestones, next N issues in <project> phrases).
  • New § 3a "Sequence loop":
    • Pre-flight must be inside a worktree with a clean tree.
    • Resolve queue to an ordered list, cache to .agent/autopilot-queue.json for mid-sequence resumption.
    • Per issue: pop ref → PR pre-check (skip if shipped) → /fresh <ref> --yes → orchestrator arc → log result → repeat.
    • Never auto-ships. The /ship gate stays human-driven per-PR.
    • Hard stops (halt whole sequence): /fresh --yes aborts, MAX_ITERATIONS hit, plan-reviewer rejects 3+ times, circular failure.
    • Soft stops (skip current, continue): PR pre-check shows ref is shipped, issue closed between resolve and pop.
  • Updated reporting with a sequence-mode handoff format listing completed/skipped/halted refs and a ready-to-ship command block.

home/.config/opencode/plugins/autopilot.ts — /fresh transition detection

  • Added lastHandoffMtime field to SessionAutopilot state.
  • Added getHandoffMtime() helper.
  • First-encounter seeding: when the plugin sees a session for the first time, records current handoff mtime so pre-existing briefs from prior sessions don't misfire a fresh-transition nudge.
  • New fresh-transition branch: when brief mtime is newer than last-seen AND iterations === 0 (just reset by /fresh), injects a "new task — read .agent/fresh-handoff.md" nudge instead of the normal "continue the plan" nudge.
  • Preserves lastHandoffMtime across every state-write path (MAX_ITERATIONS reset, plan-completion reset, chat.message reset) to prevent misfires.

Test plan

  • Manual syntax check of the plugin via bun --print "import(...)" — passes.
  • No automated tests ship in this repo; the install.sh dry-run was not re-exercised because no new files were added and no opencode.json entries changed.
  • End-to-end validation is user-driven: run /fresh GEN-1128 --yes inside a worktree, confirm branch switches, handoff brief is written, autopilot state is zeroed. Run /autopilot "Linear project X" (once the autopilot sequence-mode classifier lands in practice) and observe the loop.

Out of scope

  • Plan-file artifact for this change. This work was done ad-hoc via direct conversation (user-requested investigation + rewrite), not via /deep-plan. No retroactive plan is written.
  • install.sh prune-on-rename. A future nice-to-have but not needed for this change — the new files replace existing files at the same paths.
  • OpenCode PR #23360 integration. Upstream work that would eventually allow true mid-session CWD pivot; when it merges, /fresh can be simplified further. Not needed now — the long-running-worktree model sidesteps the need.

Risk

Low. The old /fresh was a user-invoked slash command with no downstream callers that would break. The autopilot plugin's additions are all guarded (null-check on handoffMtime, first-encounter seeding, preserve-across-writes). The plugin backward-compatibly reads old state entries (missing lastHandoffMtime = undefined = 0 = never-seen), so existing state files work without migration.

Commit

`7ba0f80 feat: long-running worktrees for /fresh + autopilot sequence mode`

Rewrite /fresh from a "create a new worktree" command to a "re-key the current
worktree to a new task" command, matching the long-running-worktree workflow
where each terminal tab owns a persistent worktree directory. /fresh now
discards working-tree state, fetches origin/<default>, creates a new branch,
and delegates repo-specific process/container cleanup to a new
.glorious/hooks/fresh-reset hook (scoped to this worktree only, never touches
siblings). Adds --yes flag for non-interactive invocation by autopilot loops,
with a deterministic decision matrix that aborts on tracked/intentional
changes and only auto-discards gitignored debris. Writes .agent/fresh-handoff.md
with tracker context + atomically resets autopilot-state.json iteration
counters so the autopilot plugin can pick up the new task.

Extends /autopilot with a sequence-of-issues mode: when given a Linear project,
GitHub milestone, or "next N issues in <project>" phrase, autopilot loops
through issues, pre-checks PR state (skips already-shipped refs), invokes
/fresh <ref> --yes between each, and logs results to
.agent/autopilot-sequence-log.md. Hard stops on dirty tracked trees,
MAX_ITERATIONS hits, or repeated plan rejections; soft-skips already-shipped
refs. Never auto-ships — the human /ship gate remains per-PR.

Updates the autopilot plugin to detect /fresh re-key transitions via
fresh-handoff.md mtime tracking + iteration-counter reset, injecting a new-task
nudge that tells the agent to read the handoff brief and ignore prior plans in
the same session. Preserves lastHandoffMtime across every state-write path
(MAX_ITERATIONS reset, plan-completion reset, user-message reset) to prevent
misfiring on stale briefs from prior sessions.
@iceglober iceglober merged commit d4c7b7d into main Apr 21, 2026
2 checks passed
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.

1 participant