Git ref
Skill memorysaver/agentic-engineering-patterns/skills/agentic-development-workflow/git-ref
Canonical AEP git reference: base resolution, worktree lifecycle, naming, task commits, PRs, recovery. Use when a git convention is needed.From its SKILL.md
npx -y skills add memorysaver/agentic-engineering-patterns --skill git-refAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 17 stars17 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
14.3 KB, as published. Nobody here has run it
Git + Worktree Reference (AEP)
/aep-launch, /aep-build, and /aep-wrap operate on a plain git repository plus git worktree for parallel agent isolation. There is no separate VCS, no colocated mode, no special wrapper — 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.
AEP migrated from Jujutsu to pure git in 2026-04 (agent training data, universal tooling); rationale in 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:
- 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 standardmain/developcases — those are auto-detected./aep-onboardreports the detected mode and only writes this key when you use a non-standard name (git config aep.integration-branch <name>; unset withgit config --unset aep.integration-branch). - Auto-detect —
developif it exists locally or onorigin. - 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 pruneclears the dangling refs).
The One-Commit-per-Task Pattern (Phase 4 of /aep-build)
This is the largest AEP-specific convention.
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. |
Never use git push --force (without --force-with-lease). 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 and can target the wrong branch (especially when a dispatch commit looked like a recent base). PRs targeting the wrong base merge into a non-integration branch, and the code never lands on the integration branch even after a successful merge. In two-branch mode this is doubly important — an inferred base could be production main, which AEP must never merge feature work into directly.
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/ |