SuperAPI AI Directory

aep-git-ref

Coding & development

View repository

Updated 10-16 00:28

About this skill

Git + Worktree Reference (AEP)

/aep-launch, /aep-build, and /aep-wrap operate on a plain git repository plus git worktree for parallel agent isolation — when these skills say "commit", they mean git commit. This skill is the canonical home for AEP's git conventions: every other skill resolves $BASE, creates/removes worktrees, and opens PRs by pointing here, never by re-inlining these blocks. Rationale for plain git (agent training data, universal tooling): docs/decisions/migrate-from-jj-to-git.md.


Integration Branch

Every AEP git operation targets the integration branch — the branch feature worktrees are created from, rebased onto, PR'd into, merged into, and where control-plane commits (/aep-dispatch, /aep-design, the /aep-wrap archive) land. Throughout this skill and the bash blocks in /aep-launch, /aep-build, /aep-wrap, and the product-context skills, this branch is referred to as $BASE.

AEP auto-detects one of two modes — no configuration required for the common cases:

Mode Condition Integration ($BASE) Production
single-branch (default) no develop branch main main (same branch)
two-branch develop exists develop (staging) main (promote-only)

A project grows from single- to two-branch mode simply by creating a develop branch — no reconfiguration. In two-branch mode AEP never touches the production branch (main): promotion from integration to production (develop → main) is the user's concern, exactly like deployment, which is already outside AEP's scope.

Resolving $BASE

Every git-touching skill resolves the integration branch at the top of its first bash block:

# Resolve AEP integration branch ($BASE): override → auto-detect develop → main
BASE=$(git config --get aep.integration-branch 2>/dev/null || true)
if [ -z "$BASE" ]; then
  if git show-ref --verify --quiet refs/heads/develop \
     || git show-ref --verify --quiet refs/remotes/origin/develop; then
    BASE=develop
  else
    BASE=main
  fi
fi

Resolution order:

  1. Explicit override — git config --get aep.integration-branch. This is an override only, for a non-standard integration branch name (e.g. staging, integration). You do not set it for the standard main/develop cases — those are auto-detected. /aep-onboard reports the detected mode and only writes this key when you use a non-standard name (git config aep.integration-branch <name>; unset with git config --unset aep.integration-branch).
  2. Auto-detect — develop if it exists locally or on origin.
  3. Default — main (single-branch mode).

Because the standard cases are auto-detected (not pinned), a project grows from single- to two-branch mode with no reconfiguration: create develop and every skill resolves $BASE to it on the next run. (If you had pinned aep.integration-branch=main, that override would win and suppress the upgrade — which is why setup does not pin the default.)

git config --get aep.integration-branch is repo-local and shared across all worktrees via the common .git/config, so $BASE resolves identically in the main session and inside any .feature-workspaces/<name> worktree.


Worktree Lifecycle

Create

# Resolve $BASE first — see "Resolving $BASE" above
mkdir -p .feature-workspaces
git worktree add -b feat/<name> .feature-workspaces/<name> "$BASE"
  • Path is always under .feature-workspaces/<name> (kept gitignored).
  • Branch is always feat/<name> — corresponding to the OpenSpec change name or story id.
  • Base is always the integration branch $BASE.

The worktree shares .git/objects with the main repo, so creating it is fast and history is not duplicated. Only the working tree files are duplicated on disk.

Inspect

git worktree list                            # all worktrees, branches, paths
git -C .feature-workspaces/<name> status     # status inside a specific worktree
git -C .feature-workspaces/<name> log --oneline "$BASE"..HEAD   # commits unique to the feature branch

Remove (/aep-wrap step 6)

git worktree remove .feature-workspaces/<name>
git branch -d feat/<name>

If git branch -d warns the branch isn't fully merged (likely because the PR was squash-merged so commit SHAs differ), force with git branch -D feat/<name> after confirming via gh pr view <number> --json state that the PR is MERGED.

Recover from a corrupt worktree directory

If .feature-workspaces/<name>/ was deleted manually:

git worktree prune                           # forget the orphaned registration
git branch -D feat/<name>                    # delete the orphan branch (if needed)

If .git/worktrees/<name>/ still references a missing path (e.g. you moved the parent directory):

git worktree repair .feature-workspaces/<name>

Branch Naming

Branch Pattern Created by
Feature work feat/<short-name> /aep-launch (one per story)
Migration / chore chore/<short-name> manually, when applicable
Hotfix off main fix/<short-name> manually
Migration project migration/<topic> manually

Rules:

  • Use kebab-case after the slash. No spaces, no underscores.
  • Keep names short (≤ 30 chars) — they appear in tab labels, signal files, and PR titles.
  • Reuse a deleted branch name once its worktree is fully removed (git worktree prune clears the dangling refs).

The One-Commit-per-Task Pattern (Phase 4 of /aep-build)

What

tasks.md lists N tasks. The feature branch ends up with N commits — one per task — in the same order. Conventional-commit format. Workspace agents implement linearly, committing after each task, so the commit count matches the task count:

# Implement task 1
# ... edit files ...
git add -A
git commit -m "feat(auth): extract auth service"

# Implement task 2
# ... edit files ...
git add -A
git commit -m "feat(auth): add token refresh flow"

# ... etc.

After each commit, record the short SHA in .dev-workflow/feature-verification.json against the matching task entry's commit_sha field.

Why this shape

  • The PR's commit list reads like a table of contents matching tasks.md.
  • Each commit is a self-contained unit that the evaluator (in Phase 5) and reviewers (in Phase 11) can git show <sha> independently.
  • We squash-merge at PR-merge time, so per-commit hygiene only matters for review readability — the integration branch's history stays clean automatically.
  • It removes any need to rewrite history mid-stream, which is where agents most often go off the rails.

What to do when something goes wrong

Situation Action
Forgot a file in the just-committed task git add <file> && git commit --amend --no-edit (only safe before the next task's commit)
Realized the previous task is broken, several commits ago Add a new commit: fix(<scope>): correct <issue from task N> — the history stays append-only
Review feedback or eval-loop FAIL Add a follow-up commit: fix(<scope>): address review on <topic>
Need to update against new origin/$BASE git fetch origin && git rebase origin/"$BASE" && git push --force-with-lease origin feat/<name>
Conflicts during rebase Resolve in working tree, git add <files> && git rebase --continue. If hopelessly tangled, git rebase --abort and surface to the orchestrator via signal file.

Push a rewritten branch with --force-with-lease rather than a bare --force: the lease variant fails safely when someone else has pushed since your last fetch.


Publishing & PR Conventions

Push the feature branch

# First push (after Phase 9 cleanup):
git push -u origin feat/<name>

# Subsequent pushes (review fixes, rebases):
git push origin feat/<name>
# or after a rebase:
git push --force-with-lease origin feat/<name>

Open the PR — always set base explicitly

# $BASE is the integration branch — resolve it first (see "Integration Branch" above)
gh pr create --base "$BASE" --title "<title>" --body "<body>"

The --base "$BASE" flag is mandatory: without it gh infers the base from local branch state (a dispatch commit can look like a recent base), the PR merges into a non-integration branch, and the code never lands on $BASE. In two-branch mode an inferred base could be production main, which AEP never merges feature work into.

Merge the PR

gh pr merge <number> --squash --delete-branch

We always squash-merge. The feature branch's per-task commits collapse into one commit on the integration branch ($BASE), which keeps its log readable while preserving the per-task review trail in the PR's "Commits" tab.


Control-Plane Commits (on the integration branch)

/aep-design, /aep-dispatch, /aep-envision, /aep-map, /aep-calibrate, /aep-validate, /aep-reflect, and the /aep-wrap archive step all commit directly to the integration branch ($BASE). The pattern is identical:

# Resolve $BASE first — see "Resolving $BASE" above
git pull --ff-only origin "$BASE"       # fail-fast if the integration branch has diverged
git add <specific-files>                # never -A on the integration branch; be explicit
git commit -m "<conventional message>"
git push origin "$BASE"

The --ff-only is intentional — it refuses a non-fast-forward pull, which means concurrent pushes get caught instead of silently merged. If the pull fails because someone else pushed first, fetch, rebase your work, and try again.


Recovery

Lost a commit (committed to wrong branch, or amend'd away)

git reflog                              # find the orphaned commit's SHA
git cherry-pick <sha>                   # bring it onto the current branch
# or
git reset --hard <sha>                  # if you want to rewind to that point

Lost an entire branch (deleted with -D)

git reflog                              # the deleted branch's tip is in here
git branch <name> <sha>                 # recreate the branch at that tip

Lost work in a worktree directory you removed

If you ran rm -rf .feature-workspaces/<name> without committing first, the work is gone — git worktree only tracks committed history. .dev-workflow/ files (signals, lessons, progress) are also gone since they're gitignored. Use git worktree remove instead of rm -rf next time; it refuses if the worktree has uncommitted changes.

OpenSpec files missing after rebase

If the dispatch commit's OpenSpec files disappeared during a rebase or merge:

git log --oneline -n 30                 # find the dispatch commit SHA
git restore --source=<sha> -- openspec/

This restores openspec/ to the state of the dispatch commit without touching anything else.


Disk Budget

Each worktree adds approximately one full working-tree copy on disk. .git/objects is shared across all worktrees, so commit history isn't duplicated. For a typical AEP repo (~100 MB working tree, multi-GB .git/objects after years of history), three concurrent agents adds ~300 MB on top of one shared .git/.

Monitor with:

du -sh .feature-workspaces/             # working-tree footprint
du -sh .git/objects                     # shared history

If disk pressure hits before agents finish, prefer pausing new /aep-launch invocations over forcibly removing in-flight worktrees.


Cheat Sheet

You want to… Run
Create a feature worktree git worktree add -b feat/<n> .feature-workspaces/<n> "$BASE"
List all worktrees git worktree list
See feature-branch commits git log --oneline "$BASE"..HEAD
Sync against latest $BASE git fetch origin && git rebase origin/"$BASE"
Push after rebase git push --force-with-lease origin feat/<n>
Open PR gh pr create --base "$BASE"
Merge PR gh pr merge <#> --squash --delete-branch
Remove worktree post-merge git worktree remove .feature-workspaces/<n> && git branch -d feat/<n>
Recover deleted commit git reflog then git cherry-pick <sha>
Restore lost openspec/ files git restore --source=<sha> -- openspec/