Adopt a v2.x ai-sdlc-harness workspace into v3.0 — config carries over, run history stays archived in place. USER-ENTRY and HUMAN-ONLY — invoke only when the user explicitly runs /migrate-workspace; never autonomously, never from a subagent (guard-enforced).
Installs into .claude/skills of the current project.
Are you the author of Migrate Workspace?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/mostashraf-migrate-workspace)
---
name: migrate-workspace
description: >
Adopt a v2.x ai-sdlc-harness workspace into v3.0 — config carries over,
run history stays archived in place. USER-ENTRY and HUMAN-ONLY — invoke
only when the user explicitly runs /migrate-workspace; never autonomously,
never from a subagent (guard-enforced).
---
# migrate-workspace — adopt a v2.x workspace
**The fork seam:** this file and `harness/migrate.py` are the replaceable
unit — forks ship different adoption logic by swapping them and nothing
else. Everything that WRITES goes through the same owned path as
`/init-workspace` (`init-section` → `init-verify` → `init-finalize`);
migrate verbs are read-only proposals.
Human-only, same invocation rules as every skill here: always the full
`${CLAUDE_PLUGIN_ROOT}/bin/harness <verb>` path. First run
`/init-workspace`'s **step 0 environment bootstrap** exactly as written
there (that file owns the procedure — don't re-derive it).
## 1 · Detect (stop conditions first)
```
${CLAUDE_PLUGIN_ROOT}/bin/harness migrate-detect
```
- Surface `warnings` verbatim whenever present — e.g. an unreadable
`overrides.yaml` reports as bootstrapped *fail-closed*, and the
remediation lives in the warning, not here.
- `already_bootstrapped: true` → this workspace already runs v3.0. If
`inventory.legacy_context_files` is non-empty, a prior migration stopped
before archiving — offer the **archive-only finish** (steps 5–6; the
report on this path records `inventory` only, since `migrate-extract`
rightly refuses post-bootstrap — the carried-config record lives in the
earlier attempt's confirmations); otherwise **stop** and point the user
at `/workspace-config`.
- `legacy: null` → **stop**: nothing to adopt; point at `/init-workspace`.
- Otherwise show the `evidence` list (why this looks like v2.x), then the
`inventory`:
- **`in_flight` runs get a hard boundary, stated plainly:** run history
is never converted — v3.0 state is sealed evidence (red-proofs,
ledgers) that v2.x never produced, so migrating a half-done run would
fabricate its audit trail. **Recommend finishing or abandoning those
stories on v2.x before migrating**: step 5 archives the very config
files v2.x resumes from, so proceeding now means a later v2.x session
must first hand-restore them from `legacy-2.1/`. Proceeding anyway is
the user's call — record the choice in the step-5 report.
- Old `ai/` run dirs and `aborted` leftovers stay exactly where they are
as readable archives; v3.0 run discovery ignores them.
## 2 · Extract & confirm (propose-only)
```
${CLAUDE_PLUGIN_ROOT}/bin/harness migrate-extract
```
Present the proposal per section — `provider`, `repos`, `language` — next
to every `notes` entry (missing repo paths, unverifiable stories_dir) and
the `unmapped` list (what v2.x config has no v3.0 home, and why). The user
confirms or corrects **each section**; anything extract could not fill
falls through to the matching `/init-workspace` interview question — never
guess a value extraction didn't find.
**Re-entry:** if a target section file already exists under
`.claude/context/` (a prior attempt got that far), read it and present its
current value next to the legacy-derived proposal — never silently
re-propose stale legacy data over a correction the user already confirmed.
`optional_overrides.naming` is **opt-in, default NO**: translated v2.1
naming templates are usually the v2.1 defaults the user never chose, and
v3.0's `{type}/{id}-{slug}` handles change-types better than a frozen
`feature/` prefix. Offer it only as "did you deliberately customize
naming in v2.x?".
## 3 · Apply through the owned init path
Write each confirmed section with `init-section` exactly as
`/init-workspace` step 1 documents (self-nested payloads; `overrides`
flat). Then fill the gaps the same way that interview does:
- No `language` proposal for a repo → `discover --repo <path>` and confirm
(its dirty-repo/default-branch cautions live in `/init-workspace` step 2).
**`discover` switches a clean repo back to its default branch** — if
`inventory.in_flight` names a story whose v2.x feature branch may be
checked out in that repo, say so and get explicit confirmation first.
- MCP-transport providers (`jira`, `zoho`, `ado-mcp`) → the
model-in-the-loop MCP checklist from the interview applies unchanged.
## 4 · Verify + finalize (the same real gate)
`/init-workspace` step 4, verbatim: `init-verify` must pass (fix failures,
re-run), then `init-finalize`. A migrated workspace ends in exactly the
state a freshly-interviewed one does — there is no "migrated" flavor to
special-case later.
## 5 · Archive the legacy config
List the v2.x context files (`inventory.legacy_context_files`), confirm
with the user, then move them aside so nobody mistakes them for live
config:
```
mkdir -p .claude/context/legacy-2.1 && mv .claude/context/*.md .claude/context/legacy-2.1/
```
Write `.claude/context/legacy-2.1/migration-report.md`: what carried over
(per section), the full `unmapped` and `notes` lists, and any in-flight
v2.x stories with the finish-on-v2.x guidance. The old `ai/` dirs are the
run archive; this report is the config-decision record.
## 6 · Hand over
- `local-markdown` users: existing story files work in place — v2.1
`> Status:` blockquotes are read tolerantly and upgrade to the v3.0
`Status:` form on first write-back.
- Remote-provider users: work items live server-side; nothing moved.
- Offer repo-map generation (`/repo-map-refresh` owns the procedure),
then: `/dev-workflow <work-item-id>` is ready.