Contenuto dal repository con titoli, esempi, codice, tabelle, link e immagini preservati.
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 hereSession: 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:archiveruns from the main checkout on the integration branch (`$BASE`) — never from a workspace, where it writesopenspec/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:
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)
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/null2.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 §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 inopenspec/changes/<change-name>/before the archive lets them ride the archivemvin 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):
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):
cat .feature-workspaces/<name>/.dev-workflow/signals/status.json
gh pr view <pr-number> --json state,mergedAtIf 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:
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_usdIf 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 entersproduct-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:
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"
fiIf 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):
# 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 || trueThen 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.yamlexists (pre- or post-archive location) → archived =openspec/changes/<name>/gone AND anarchive/*<name>*dir exists → committed =git status --porcelainclean overopenspec/→ status-flipped = story showscompletedwith its completion fields set → lessons-copied =lessons-learned/<change-name>.mdexists → torn down = worktree path gone (cross-checkgit worktree list). Steps whose postcondition already holds are skipped, never repeated. This is the pattern/aep-autopilotreferences/deterministic-orchestration.md generalizes.
Reflect and Advance (Product-Cycle Mode)
Standalone mode: Ifproduct-context.yamldoesn't exist, skip the layer gate; you may still run/aep-reflectto classify observations.
At layer completion — when product-context.yaml exists and every story in the active layer is completed — read 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).

