aep-wrap
Helps AI agents finish up their work cleanly and prepare results to hand back to you.
Installation
Paste this into Claude Code, Cursor, or any agent that can run commands.
SKILL.mdShow the author's original SKILL.md
---
name: aep-wrap
description: >-
Archives OpenSpec artifacts and removes the workspace after a PR merges.
Use for "wrap up", "archive", or post-merge cleanup.
---
# Wrap
Post-merge archive and workspace cleanup. Run on the **integration branch** (`$BASE`) after the PR merges — resolve `$BASE` per /aep-git-ref "Integration Branch". This archives the OpenSpec change, converges the build's runtime signal, and removes 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, converged execution records, cleaned-up workspace
---
## Phase 13: Archive & Cleanup on the Integration Branch
> **Hard guardrail:** `/opsx:archive` runs from the **main checkout on the integration branch (`$BASE`)** — never from a workspace, where it writes `openspec/specs/` and collides with parallel worktrees.
### 1. Fetch merged state and fast-forward the integration branch
Resolve `$BASE` per /aep-git-ref "Integration Branch" (override → auto-detect `develop` → `main`), then update the local integration branch to include the merged PR:
```bash
git fetch origin
git checkout "$BASE"
git pull --ff-only origin "$BASE"
git status
```
`--ff-only` is intentional — if it fails because `$BASE` has 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)`.
**Postcondition:** HEAD is on `$BASE` and `git status` is clean — only `openspec/` and `product-context.yaml` may differ during wrap. If code under `apps/`/`packages/` is modified, investigate first. If `openspec/changes/<name>/` is missing (a dispatch commit lost before launch), recover per /aep-git-ref "Recovery" → "OpenSpec files missing after rebase" (`git restore --source=<dispatch-sha> -- openspec/`) before proceeding.
### 2. Stop the dev server (from the workspace, if still running)
```bash
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
```
### 2.5. Convergence Gather — gather execution records (before archive)
Converge the workspace's build-time runtime signal into the **pre-archive change dir** (`openspec/changes/<change-name>/convergence/`) so the archive `mv` in step 3 carries it in one commit. Run the gather commands and write `execution-record.yaml` per the producer contract — copy list, manifest field list, and schema — in [references/convergence.md](references/convergence.md) §1. The gather is **best-effort**: a missing source becomes an explicit `null` or an absent copy, never a failed wrap.
> **Gate (ordering invariant — gather before archive):** placing files in `openspec/changes/<change-name>/` _before_ the archive lets them ride the archive `mv` in one commit; gathering after step 3 needs a separate commit and races teardown — a silent-loss window.
**Postcondition:** `convergence/execution-record.yaml` exists in the pre-archive change dir.
### 3. Run archive
```
/opsx:archive <change-name>
```
### 4. Commit and push the archive
Use the **control-plane commit** — the fast-forward commit pattern shared by steps 4, 5, and 5.5 (the integration branch was checked out in step 1):
```bash
BASE=$(git branch --show-current) # integration branch, from step 1
git add openspec/ # stages the convergence/ records from step 2.5 — the archive commit carries them, no extra commit
git commit -m "chore: archive <change-name>"
git pull --ff-only origin "$BASE"
git push origin "$BASE"
```
### 5. Sync story status from workspace signals (Product-Cycle Mode Only)
> **Standalone mode:** If `product-context.yaml` doesn't exist, skip to step 6.
If this feature was a dispatched story, read the workspace signals and cross-check against actual PR state — signals can be stale (a workspace may still show `in_review` after merge):
```bash
cat .feature-workspaces/<name>/.dev-workflow/signals/status.json
gh pr view <pr-number> --json state,mergedAt
```
If the PR is merged but the signal says `in_review`, treat the story as `completed`. From the (PR-corrected) signal, update the matching story in `product-context.yaml`:
```yaml
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`, set `status: failed` and record the structured `failure_log` under `failure_logs:` instead. Then transition any `pending` story whose dependencies are now all `completed` to `ready`. Validate YAML (`npx js-yaml product-context.yaml > /dev/null`; `/aep-validate` carries the guardrails and common fixes), then commit all transitions atomically via the **control-plane commit** (step 4) — `git add product-context.yaml`, message `chore: update story <id> status to completed`.
> **Concurrency protocol:** this is the only place story completion status enters `product-context.yaml` — workspace agents write signals; `/aep-wrap` (on the integration branch) reads signals and writes YAML.
### 5.5. Archive lessons learned
Before worktree removal — the last chance, since `git worktree remove` deletes `.dev-workflow/lessons.md` — copy any real lessons:
```bash
LESSONS=".feature-workspaces/<name>/.dev-workflow/lessons.md"
if [ -f "$LESSONS" ] && [ "$(wc -l < "$LESSONS")" -gt 12 ]; then # >12 = content beyond the template header
mkdir -p lessons-learned
cp "$LESSONS" "lessons-learned/<change-name>.md"
fi
```
If a file was copied, commit it via the **control-plane commit** (step 4): `git add lessons-learned/<change-name>.md`, message `docs: archive lessons from <change-name>`. The archived `convergence/` dir is the full per-change record; `lessons-learned/` stays the fast-path index `/aep-reflect` Step 1 reads.
### 6. Tear down the worker + worktree (`executor.teardown()`)
Stop the workspace's worker **before** removing the worktree — an OS-bound worker left running against a deleted directory orphans and accumulates across an autopilot run. The stop is per launch mode (recorded as `backend`/`agent_id` in autopilot state, or evident from how you launched):
```bash
# 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
```
Then remove the worktree and delete the merged feature branch per /aep-git-ref "Worktree Lifecycle" → "Remove (`/aep-wrap` step 6)" (`git worktree remove` + `git branch -d feat/<name>`; force-delete only after `gh pr view <number> --json state` confirms `MERGED`).
---
## Guardrails
> **Ordering invariant (world-derived postconditions).** The wrap step chain is
> mechanical — each step's completion is observable from the world, so an
> interrupted wrap resumes by checking, not re-doing: **gathered** =
> `convergence/execution-record.yaml` exists (pre- or post-archive location) →
> **archived** = `openspec/changes/<name>/` gone AND an `archive/*<name>*` dir
> exists → **committed** = `git status --porcelain` clean over `openspec/` →
> **status-flipped** = story shows `completed` with its completion fields set →
> **lessons-copied** = `lessons-learned/<change-name>.md` exists → **torn down** =
> worktree path gone (cross-check `git worktree list`). Steps whose postcondition
> already holds are skipped, never repeated. This is the pattern
> `/aep-autopilot` references/deterministic-orchestration.md generalizes.
---
## Reflect and Advance (Product-Cycle Mode)
> **Standalone mode:** If `product-context.yaml` doesn't exist, skip the layer gate; you may still run `/aep-reflect` to classify observations.
**At layer completion** — when `product-context.yaml` exists and every story in the active layer is `completed` — read [references/layer-advance.md](references/layer-advance.md) for the two-phase **Layer Gate Check** (run the gate, record evidence, flip `scripted_passed → passed` on covered) and **Layer Distillation** (the isolated, proposal-only synthesis). Advancing to the next layer's design is a **human-confirmed** step, recorded before the next `/aep-dispatch`.
### Feedback Loop
Run `/aep-reflect` to classify observations from this feature — bugs, refinements, and discoveries route back to the right phase, closing the loop.
---
## Next Step
Pick the next story from the dispatch queue (`/aep-dispatch`), or classify feedback from what you just shipped (`/aep-reflect`).
Ships with 2 supporting files:
- references/convergence.md
- references/layer-advance.md
Mirrored from the author's public source. Install counts from the open skills registry.