Przeglądaj źródła

feat(skills): add new-lane — one-command isolated worktree+branch for parallel work

The remedy half of the collision guard: when the peer-writer hook fires, move work into its own lane instead of sharing a checkout. new-lane.sh <slug> creates worktree <repo>-<slug> on branch lane/<slug>, carries over gitignored env files (.dev.vars/.env*/.secrets), prints the path on stdout. Model-invocable (skill, not command) so I can act on the guard's advice, not just relay it.

NOTE: not yet registered in .claude-plugin/plugin.json components.skills — that file is sitting with ~3-day-old uncommitted changes, so registration is deferred to avoid entangling with them.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
0xDarkMatter 3 tygodni temu
rodzic
commit
9a25e527dc
2 zmienionych plików z 97 dodań i 0 usunięć
  1. 49 0
      skills/new-lane/SKILL.md
  2. 48 0
      skills/new-lane/scripts/new-lane.sh

+ 49 - 0
skills/new-lane/SKILL.md

@@ -0,0 +1,49 @@
+---
+name: new-lane
+description: "Create an isolated git worktree + branch ('lane') for parallel work in one step, carrying over the gitignored env files a fresh worktree lacks. Use when the peer-writer guard warns that another session is editing this checkout, when you want to parallelise work without two sessions sharing one working tree, or when asked to 'spin up a worktree / lane / isolated branch'. Triggers on: new lane, isolate this work, worktree for X, parallel session without collision, peer-writer advisory, move to a worktree, spin up a lane."
+license: MIT
+allowed-tools: "Bash"
+metadata:
+  author: claude-mods
+  related-skills: "git-ops, fleet-ops"
+---
+
+# New Lane
+
+The one-command remedy for the collision problem: **one writer, one worktree, one branch.** When two
+sessions share a checkout they fight over a single index/HEAD (see
+[worktree-boundaries](../../rules/worktree-boundaries.md) → "Provisioning discipline"). This skill
+spins up an isolated *lane* so parallel work never collides.
+
+Reach for it when:
+- the **peer-writer guard** fires (`session-start-unicode-scan.sh` at boot, or `pre-write-peer-guard.sh`
+  mid-session) — another session is editing this tree; move your work into its own lane;
+- you're about to start a second stream of work and don't want to share the checkout;
+- the user asks to "isolate this", "spin up a worktree/lane", or "branch this off without touching main".
+
+## Use
+
+```bash
+bash scripts/new-lane.sh <slug> [base-branch]
+```
+
+- `<slug>` → branch `lane/<slug>`, worktree at `<repo>/../<repo>-<slug>` (sibling dir — outside the
+  repo, never under `.claude/`, no long-path nesting).
+- `[base-branch]` defaults to the current branch.
+
+It creates the worktree+branch and **carries over gitignored env files** (`.dev.vars`, `.env*`,
+`.secrets`) the fresh worktree would otherwise be missing — the "right settings" that make the lane
+immediately runnable. The new worktree's absolute path is printed on stdout (everything else on
+stderr), so it composes: `cd "$(bash scripts/new-lane.sh hotfix main)"`.
+
+## After
+
+Open a Claude session with its cwd set to the printed worktree path — that session is the lane's sole
+writer. Land the lane back onto the base branch through **[fleet-ops](../fleet-ops/SKILL.md)** (the
+test-gated landing queue), not by sharing the checkout again.
+
+## Boundaries
+
+- Refuses if the branch or worktree path already exists (exit 1) — never clobbers.
+- Does **not** spawn the session or run installs; it provisions the isolated tree, nothing more.
+- Never touches `.claude/worktrees/` or another session's lanes (see worktree-boundaries).

+ 48 - 0
skills/new-lane/scripts/new-lane.sh

@@ -0,0 +1,48 @@
+#!/bin/bash
+# new-lane.sh — create an isolated git worktree + branch ("lane") for parallel work, carrying over
+# the gitignored env files a fresh worktree won't have. The one-command remedy when the peer-writer
+# guard fires, or any time you want to parallelise without two sessions sharing one checkout.
+#
+# Usage:   new-lane.sh <slug> [base-branch]
+#   <slug>        short kebab name -> branch lane/<slug>, worktree <repo>/../<repo>-<slug>
+#   [base-branch] what to branch from (default: the current branch)
+#
+# Output contract (SKILL-RESOURCE-PROTOCOL): the new worktree's absolute path is the ONLY thing on
+# stdout (so a caller can `cd "$(new-lane.sh foo)"`); all human-facing messages go to stderr.
+# Exit: 0 ok | 2 usage/precondition | 1 conflict (branch/path exists).
+#
+# Examples:
+#   new-lane.sh auth-refactor            # lane/auth-refactor off the current branch
+#   new-lane.sh hotfix main              # lane/hotfix off main
+set -euo pipefail
+
+case "${1:-}" in
+   -h|--help|"")
+    sed -n '2,18p' "$0" | sed 's/^# \{0,1\}//' >&2
+    [ -z "${1:-}" ] && exit 2 || exit 0 ;;
+esac
+
+SLUG=$(printf '%s' "$1" | tr '[:upper:] ' '[:lower:]-' | tr -cd 'a-z0-9-')
+[ -n "$SLUG" ] || { echo "new-lane: slug empty after sanitising" >&2; exit 2; }
+
+ROOT=$(git rev-parse --show-toplevel 2>/dev/null) || { echo "new-lane: not inside a git repo" >&2; exit 2; }
+REPO=$(basename "$ROOT")
+BRANCH="lane/$SLUG"
+BASE="${2:-$(git -C "$ROOT" rev-parse --abbrev-ref HEAD 2>/dev/null)}"
+WT="$(dirname "$ROOT")/${REPO}-${SLUG}"
+
+git -C "$ROOT" show-ref --verify --quiet "refs/heads/$BRANCH" && { echo "new-lane: branch $BRANCH already exists" >&2; exit 1; }
+[ -e "$WT" ] && { echo "new-lane: path $WT already exists" >&2; exit 1; }
+
+git -C "$ROOT" worktree add "$WT" -b "$BRANCH" "$BASE" >&2
+
+# Carry over gitignored env/secret files the new worktree won't have (Cloudflare/Node/etc.).
+for f in .dev.vars .env .env.local .env.development .secrets; do
+  if [ -e "$ROOT/$f" ] && [ ! -e "$WT/$f" ]; then
+    cp -r "$ROOT/$f" "$WT/$f" 2>/dev/null && echo "new-lane: carried over $f" >&2
+  fi
+done
+
+echo "new-lane: lane ready — branch $BRANCH (base $BASE)" >&2
+echo "new-lane: open a Claude session with cwd '$WT' — one writer per tree; land via fleet-ops" >&2
+printf '%s\n' "$WT"