From agentic-development-workflow
Archives OpenSpec changes, commits them, and removes the workspace after a PR is merged. The final step in the feature lifecycle.
How this skill is triggered — by the user, by Claude, or both
Slash command
/agentic-development-workflow:wrapThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Post-merge archive and workspace cleanup. Run this on the **integration branch** (`$BASE` — `main` in single-branch mode, `develop` in two-branch mode; see [git-ref](../git-ref/SKILL.md) → "Integration Branch") after the PR merges to archive the OpenSpec change and clean up the workspace.
Post-merge archive and workspace cleanup. Run this on the integration branch ($BASE — main in single-branch mode, develop in two-branch mode; see git-ref → "Integration Branch") after the PR merges to archive the OpenSpec change and clean up the workspace.
Where this fits:
/aep-onboard → /aep-scaffold → [ /aep-design → /aep-launch → /aep-build → /aep-wrap ]
▲ you are here
Session: Main session, post-merge Input: Merged PR notification Output: Archived OpenSpec change, cleaned up workspace
Run on the integration branch (
$BASE) after the PR merges. Do not run from the workspace.
# Resolve $BASE — see git-ref "Integration Branch" (override → develop → main)
BASE=$(git config --get aep.integration-branch 2>/dev/null || true)
[ -z "$BASE" ] && { git show-ref --verify --quiet refs/heads/develop \
|| git show-ref --verify --quiet refs/remotes/origin/develop; } && BASE=develop
BASE=${BASE:-main}
git fetch origin
git checkout "$BASE"
git pull --ff-only origin "$BASE"
git status
Update the local integration branch to include the merged workspace PR.
--ff-onlyis intentional — if it fails because$BASEhas unpushed local commits, push or rebase those first.After this checkout you are on the integration branch; later steps recover its name with
BASE=$(git branch --show-current)(HEAD persists across shells, so this is safe even in a fresh command).Check for lost OpenSpec changes: If the dispatch commit included OpenSpec changes that are now missing (common if the dispatch commit wasn't pushed before launching), recover them from the original dispatch commit:
# Find the original dispatch commit git log --oneline -n 20 # Restore OpenSpec files from the dispatch commit git restore --source=<dispatch-commit-sha> -- openspec/Verify the workspace is clean before archiving. If
git statusshows unexpected modified files (code inapps/,packages/, etc.), investigate before proceeding. Onlyopenspec/andproduct-context.yamlfiles should change during the wrap step.
source .feature-workspaces/<name>/.dev-workflow/ports.env 2>/dev/null
lsof -ti :$SERVER_PORT | xargs kill 2>/dev/null
lsof -ti :$WEB_PORT | xargs kill 2>/dev/null
/opsx:archive <change-name>
BASE=$(git branch --show-current) # integration branch, checked out in step 1
git add openspec/
git commit -m "chore: archive <change-name>"
git pull --ff-only origin "$BASE"
git push origin "$BASE"
Standalone mode: If
product-context.yamldoesn't exist, skip this step and proceed to step 6.
If product-context.yaml exists and this feature was a dispatched story, read the workspace signals and cross-check against the actual PR state:
# Read completion data from workspace signals
cat .feature-workspaces/<name>/.dev-workflow/signals/status.json
# Cross-check: verify actual PR state (signals can be stale)
gh pr view <pr-number> --json state,mergedAt
Signal validation: Workspace agents don't always update their signal files after merge (they may show
in_revieweven though the PR is already merged). Always cross-check the signal'sstory_statusagainst the actual PR state viagh pr view. If the PR is merged but the signal saysin_review, treat the story ascompleted.
From the signal file (corrected by PR state if needed), extract story_status, pr_url, cost_usd, completed_at, and failure_log (if present). Update the story in product-context.yaml:
# Update the matching story:
status: completed # from signal story_status
completed_at: <timestamp> # from signal completed_at
pr_url: <url> # from signal pr_url
cost_usd: <cost> # from signal cost_usd
If story_status is failed, update with failure data instead:
status: failed
failure_logs:
- <structured failure_log from signal>
After updating the story, check if any pending stories should transition to ready (all dependencies now completed). Validate and commit all transitions atomically:
# Validate YAML before committing (see product-context references/yaml-guardrails.md)
npx js-yaml product-context.yaml > /dev/null && echo "YAML OK"
BASE=$(git branch --show-current) # integration branch, checked out in step 1
git add product-context.yaml
git commit -m "chore: update story <id> status to completed"
git pull --ff-only origin "$BASE"
git push origin "$BASE"
Concurrency protocol: This is the only place where story completion status enters
product-context.yaml. Workspace agents write to signals;/aep-wrap(running on the integration branch) reads signals and writes to YAML.
Before forgetting the workspace, check for lessons captured during the build:
LESSONS=".feature-workspaces/<name>/.dev-workflow/lessons.md"
if [ -f "$LESSONS" ] && [ "$(wc -l < "$LESSONS")" -gt 12 ]; then
# File has content beyond the template header
mkdir -p lessons-learned
cp "$LESSONS" "lessons-learned/<change-name>.md"
BASE=$(git branch --show-current) # integration branch, checked out in step 1
git add lessons-learned/<change-name>.md
git commit -m "docs: archive lessons from <change-name>"
git pull --ff-only origin "$BASE"
git push origin "$BASE"
fi
Why before worktree removal: Once
git worktree removeruns, the worktree directory and its.dev-workflow/lessons.mdare gone. This is the only chance to extract them.
If the lessons file contains only the template header (no Solutions, Errors, Missing, or Summary entries), skip — don't archive empty ceremony.
executor.teardown())Stop the workspace's worker before removing the worktree — otherwise an
OS-bound worker keeps running against a deleted directory, and these orphans
accumulate across an autopilot run. The stop step is per launch mode (recorded
as backend/agent_id in autopilot state, or evident from how you launched):
# Mode-specific worker stop (each is a no-op for the other modes):
# native-bg-subagent → TaskStop(<bare-hex bg-subagent id>) (session-bound, no team)
# claude-bg → claude stop <agent_id>; claude rm <agent_id>
# codex-subagent → close_agent(<agent_id>) if still running
# codex-exec → nothing to kill (the exec process exited with the build)
# legacy → tmux kill-session -t <name> 2>/dev/null || true
git worktree remove .feature-workspaces/<name> \
|| git worktree remove --force .feature-workspaces/<name> # --force only if leftover files block removal
git worktree prune
git branch -d feat/<name> # PR was merged → branch is reachable from the integration branch, safe to delete
If git branch -d warns the branch isn't fully merged (e.g., 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.
/opsx:archive from a workspace — it writes to openspec/specs/ and causes conflicts when parallel workspaces are active. Archive always runs on the integration branch ($BASE).git fetch && git pull --ff-only, run git status to verify no unexpected files are modified.openspec/changes/<name>/ is missing, the dispatch commit may have been lost. Recover from the original dispatch commit using git restore --source=<sha> before running archive.in_review after PR is merged). Always verify via gh pr view before updating product-context.yaml.git worktree remove runs, the worktree directory, signal files, and lessons.md are gone. Extract all needed data (status, lessons) first.Standalone mode: If
product-context.yamldoesn't exist, skip the layer gate check. You can still run/aep-reflectif you want to classify observations.
After archiving, check the product context:
If product-context.yaml exists and this feature was a dispatched story:
# Check: was this the last story in the current layer?
# Read product-context.yaml and check if all stories in the active layer are completed
If all stories in the current layer are completed:
layer_gates section of the YAML)layer_gates[layer].status: passed and completed_at/aep-dispatch will advance to the next layerConsider running /aep-reflect to classify observations from this feature and update the product context:
/aep-reflect
This closes the feedback loop — bugs, refinements, and discoveries get routed back to the right phase.
Pick the next story from the dispatch queue:
/aep-dispatch
Or classify feedback from the feature you just shipped:
/aep-reflect
npx claudepluginhub memorysaver/agentic-engineering-patterns --plugin patternsResolves merged workflows to completed state: verifies PR merge status, backfills metadata, forces review resolution, and cleans up worktrees/branches. Run via /cleanup or user request.
Merges a PR, removes a Git worktree, and deletes the associated state file. Final phase of a multi-step local branch/PR workflow.
Autonomously implements features in an isolated git worktree: initializes harness, implements tasks linearly with one commit per task, reviews, tests, creates PR, handles feedback, and merges.