| name | exp-start |
| description | Create or join an experiment branch. Experiments run on short-lived `exp/<slug>` branches in a dedicated worktree. They never merge to `main` and neve |
exp-start command
Apply this command workflow. Treat any text after its invocation as the command input.
Create or join an experiment branch. Experiments run on short-lived exp/<slug> branches in a dedicated worktree. They never merge to main and never get PRs or issues. Keepers are distilled onto gh-* branches via /gh-issue-push + /gh-issue-pop.
Two modes:
- Local-only (default): the experiment lives entirely on this repository's
origin.
- Remote-backed (
--remote): the experiment additionally syncs two ways with a separate repository through git subtree — remote content lives under a deterministic prefix inside the experiment branch, and local changes to that prefix can be pushed back.
Input
$ARGUMENTS
Syntax: <slug> [--remote <url-or-owner/repo>] [--branch <upstream-branch>] [--prefix <path>]
The slug is kebab-case (e.g. transformer-perf). --remote accepts a full Git URL or owner/repo shorthand (normalized to https://github.com/owner/repo.git). --branch names the upstream branch to sync with (default: the remote's default branch). --prefix sets the subtree path (default: experiments/<slug>). If no slug is given, ask the user for one.
Phase 1 -- Validate
- Validate the slug is kebab-case (
[a-z0-9]+(-[a-z0-9]+)*). Reject anything else.
- If
--remote is given, normalize it: owner/repo becomes https://github.com/owner/repo.git; URLs pass through. Record REMOTE_URL.
- Confirm we are in the main repo directory, not inside a worktree, and the current branch is
main:
git branch --show-current
If not on main, warn the user and stop. Experiment branches are created from the main repo only.
Phase 2 -- Create or join
git fetch origin
git ls-remote --heads origin exp/<slug>
Join (the branch exists): git worktree add ../exp-<slug> exp/<slug>. If
the worktree holds .exp-sync.yaml, it is remote-backed — read it and rebuild
the sync remote on this machine from its remote_name and remote_url
(Phase 3 step 2). Report the join and print the Phase 4 sync commands.
Create (it does not): branch, init beads, commit the marker.
git worktree add ../exp-<slug> -b exp/<slug>
cd ../exp-<slug>
bd init
git add -A
git commit -m "exp/<slug>: initialize experiment
Skill: exp-start
Called-by: <invoking skill, or 'user'>"
git push -u origin exp/<slug>
Phase 3 -- Remote-Backed Setup (only with --remote)
All commands run inside the worktree at ../exp-<slug>.
-
Determine the upstream branch. If --branch was given, use it. Otherwise detect the remote's default branch:
git ls-remote --symref <REMOTE_URL> HEAD | sed -n 's|^ref: refs/heads/\(.*\)\tHEAD|\1|p'
Record UPSTREAM_BRANCH.
-
Add the sync remote under a deterministic name and fetch it:
git remote add exp-<slug>-remote <REMOTE_URL> 2>/dev/null || true
git fetch exp-<slug>-remote
-
Add the subtree under the prefix (default experiments/<slug>):
git subtree add --prefix=<prefix> exp-<slug>-remote <UPSTREAM_BRANCH> --squash
-
Record the sync metadata so any machine can rejoin and continue syncing. Write .exp-sync.yaml at the worktree root:
slug: <slug>
remote_name: exp-<slug>-remote
remote_url: <REMOTE_URL>
upstream_branch: <UPSTREAM_BRANCH>
prefix: <prefix>
push_branch: exp/<slug>
The push_branch is the branch created in the remote repository when local subtree changes are pushed back; it follows the same never-merge convention there.
-
Commit and push the metadata and initial subtree state:
git add .exp-sync.yaml
git commit -m "exp/<slug>: configure subtree sync with <REMOTE_URL>
Skill: exp-start
Called-by: user"
git push
Phase 4 -- Ongoing Two-Way Sync (remote-backed)
Subtree operations always name the remote and branch explicitly, so plain git push/git pull stay unambiguous: they talk only to this repository's origin and the exp/<slug> branch, never to the sync remote.
Pull remote updates into the prefix:
cd ../exp-<slug>
git fetch exp-<slug>-remote
git subtree pull --prefix=<prefix> exp-<slug>-remote <UPSTREAM_BRANCH> --squash
git push
Push local subtree changes back to the remote repository:
cd ../exp-<slug>
git subtree push --prefix=<prefix> exp-<slug>-remote exp/<slug>
This creates or updates branch exp/<slug> in the remote repository. It never touches the remote's default branch; landing changes there is the remote repo's own review flow, out of scope here.
Phase 5 -- Report
Print the worktree path and the convention reminders:
Experiment ready at ../exp-<slug> (branch exp/<slug>).
<if remote-backed:>
Syncing with <REMOTE_URL> (<UPSTREAM_BRANCH>) under <prefix>/.
pull: git subtree pull --prefix=<prefix> exp-<slug>-remote <UPSTREAM_BRANCH> --squash
push: git subtree push --prefix=<prefix> exp-<slug>-remote exp/<slug>
Reminders:
- Never merge exp/* to main. Never open a PR or GitHub issue from it.
- Treat every push as potentially permanent — no secrets or sensitive data.
Remote-backed: subtree pushes land in the OTHER repository and survive
local cleanup.
- To keep a result, distill it onto a gh-* branch via /gh-issue-push + /gh-issue-pop.
- To conclude the experiment, run /exp-stop <slug>.
- On other machines, run /exp-start <slug> to join this experiment
(remote-backed sync reconstructs itself from .exp-sync.yaml).