diff --git a/commands/gsd/undo.md b/commands/gsd/undo.md
new file mode 100644
index 000000000..001b86855
--- /dev/null
+++ b/commands/gsd/undo.md
@@ -0,0 +1,34 @@
+---
+name: gsd:undo
+description: "Safe git revert. Roll back phase or plan commits using the phase manifest with dependency checks."
+argument-hint: "--last N | --phase NN | --plan NN-MM"
+allowed-tools:
+ - Read
+ - Bash
+ - Glob
+ - Grep
+ - AskUserQuestion
+---
+
+
+Safe git revert — roll back GSD phase or plan commits using the phase manifest, with dependency checks and a confirmation gate before execution.
+
+Three modes:
+- **--last N**: Show recent GSD commits for interactive selection
+- **--phase NN**: Revert all commits for a phase (manifest + git log fallback)
+- **--plan NN-MM**: Revert all commits for a specific plan
+
+
+
+@~/.claude/get-shit-done/workflows/undo.md
+@~/.claude/get-shit-done/references/ui-brand.md
+@~/.claude/get-shit-done/references/gate-prompts.md
+
+
+
+$ARGUMENTS
+
+
+
+Execute the undo workflow from @~/.claude/get-shit-done/workflows/undo.md end-to-end.
+
diff --git a/get-shit-done/workflows/undo.md b/get-shit-done/workflows/undo.md
new file mode 100644
index 000000000..b78b01fee
--- /dev/null
+++ b/get-shit-done/workflows/undo.md
@@ -0,0 +1,312 @@
+
+Safe git revert workflow. Rolls back GSD phase or plan commits using the phase manifest with dependency checks and a confirmation gate. Uses git revert --no-commit (NEVER git reset) to preserve history.
+
+
+
+@~/.claude/get-shit-done/references/ui-brand.md
+@~/.claude/get-shit-done/references/gate-prompts.md
+
+
+
+
+
+Display the stage banner:
+
+```
+━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+ GSD ► UNDO
+━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+```
+
+
+
+Parse $ARGUMENTS for the undo mode:
+
+- `--last N` → MODE=last, COUNT=N (integer, default 10 if N missing)
+- `--phase NN` → MODE=phase, TARGET_PHASE=NN (two-digit phase number)
+- `--plan NN-MM` → MODE=plan, TARGET_PLAN=NN-MM (phase-plan ID)
+
+If no valid argument is provided, display usage and exit:
+
+```
+Usage: /gsd-undo --last N | --phase NN | --plan NN-MM
+
+Modes:
+ --last N Show last N GSD commits for interactive selection
+ --phase NN Revert all commits for phase NN
+ --plan NN-MM Revert all commits for plan NN-MM
+
+Examples:
+ /gsd-undo --last 5
+ /gsd-undo --phase 03
+ /gsd-undo --plan 03-02
+```
+
+
+
+Based on MODE, gather candidate commits.
+
+**MODE=last:**
+
+Run:
+```bash
+git log --oneline --no-merges -${COUNT}
+```
+
+Filter for GSD conventional commits matching `type(scope): message` pattern (e.g., `feat(04-01):`, `docs(03):`, `fix(02-03):`).
+
+Display a numbered list of matching commits:
+```
+Recent GSD commits:
+ 1. abc1234 feat(04-01): implement auth endpoint
+ 2. def5678 docs(03-02): complete plan summary
+ 3. ghi9012 fix(02-03): correct validation logic
+```
+
+Use AskUserQuestion to ask:
+- question: "Which commits to revert? Enter numbers (e.g., 1,3) or 'all'"
+- header: "Select"
+
+Parse the user's selection into COMMITS list.
+
+---
+
+**MODE=phase:**
+
+Read `.planning/.phase-manifest.json` if it exists.
+
+If the file exists and `manifest.phases?.[TARGET_PHASE]?.commits` is a non-empty array:
+ - Use `manifest.phases[TARGET_PHASE].commits` entries as COMMITS (each entry is a commit hash)
+
+If the file does not exist, or `manifest.phases?.[TARGET_PHASE]` is missing:
+ - Display: "Manifest has no entry for phase ${TARGET_PHASE} (or file missing), falling back to git log search"
+ - Fallback: run git log and filter for the target phase scope:
+ ```bash
+ git log --oneline --no-merges --all | grep -E "\(0*${TARGET_PHASE}(-[0-9]+)?\):" | head -50
+ ```
+ - Use matching commits as COMMITS
+
+---
+
+**MODE=plan:**
+
+Run:
+```bash
+git log --oneline --no-merges --all | grep -E "\(${TARGET_PLAN}\)" | head -50
+```
+
+Use matching commits as COMMITS.
+
+---
+
+**Empty check:**
+
+If COMMITS is empty after gathering:
+```
+No commits found for ${MODE} ${TARGET}. Nothing to revert.
+```
+Exit cleanly.
+
+
+
+**Applies when MODE=phase or MODE=plan.**
+
+Skip this step entirely for MODE=last.
+
+---
+
+**MODE=phase:**
+
+Read `.planning/ROADMAP.md` inline.
+
+Search for phases that list a dependency on the target phase. Look for patterns like:
+- "Depends on: Phase ${TARGET_PHASE}"
+- "Depends on: ${TARGET_PHASE}"
+- "depends_on: [${TARGET_PHASE}]"
+
+For each dependent phase N found:
+1. Check if `.planning/phases/${N}-*/` directory exists
+2. If directory exists, check for any PLAN.md or SUMMARY.md files inside it
+
+If any downstream phase has started work, collect warnings:
+```
+⚠ Downstream dependency detected:
+ Phase ${N} depends on Phase ${TARGET_PHASE} and has started work.
+```
+
+---
+
+**MODE=plan:**
+
+Extract the phase number from TARGET_PLAN (the NN part of NN-MM). Extract the plan number (the MM part).
+
+Look for later plans in the same phase directory (`.planning/phases/${NN}-*/`). For each later plan (plans with number > MM):
+1. Read the later plan's PLAN.md
+2. Check if its `` sections or `consumes` fields reference outputs from the target plan
+
+If any later plan references the target plan's outputs, collect warnings:
+```
+⚠ Intra-phase dependency detected:
+ Plan ${LATER_PLAN} in phase ${NN} references outputs from plan ${TARGET_PLAN}.
+```
+
+---
+
+If any warnings exist (from either mode):
+- Display all warnings
+- Use AskUserQuestion with approve-revise-abort pattern:
+ - question: "Downstream work depends on the target being reverted. Proceed anyway?"
+ - header: "Confirm"
+ - options: Proceed | Abort
+
+If user selects "Abort": exit with "Revert cancelled. No changes made."
+
+
+
+Display the confirmation gate using approve-revise-abort pattern from gate-prompts.md.
+
+Show:
+```
+The following commits will be reverted (in reverse chronological order):
+
+ {hash} — {message}
+ {hash} — {message}
+ ...
+
+Total: {N} commit(s) to revert
+```
+
+Use AskUserQuestion:
+- question: "Proceed with revert?"
+- header: "Approve?"
+- options: Approve | Abort
+
+If "Abort": display "Revert cancelled. No changes made." and exit.
+If "Approve": ask for a reason:
+
+```
+AskUserQuestion(
+ header: "Reason",
+ question: "Brief reason for the revert (used in commit message):",
+ options: []
+)
+```
+
+Store the response as REVERT_REASON. Continue to execute_revert.
+
+
+
+**HARD CONSTRAINT: Use git revert --no-commit. NEVER use git reset (except for conflict cleanup as documented below).**
+
+**Dirty-tree guard (run first, before any revert):**
+
+Run `git status --porcelain`. If the output is non-empty, display the dirty files and abort:
+```
+Working tree has uncommitted changes. Commit or stash them before running /gsd-undo.
+```
+Exit immediately — do not proceed to any revert operations.
+
+---
+
+Sort COMMITS in reverse chronological order (newest first). If commits came from git log (already newest-first), they are already in correct order.
+
+For each commit hash in COMMITS:
+```bash
+git revert --no-commit ${HASH}
+```
+
+If any revert fails (merge conflict or error):
+1. Display the error message
+2. Run cleanup — handle both first-call and mid-sequence cases:
+ ```bash
+ # Try git revert --abort first (works if this is the first failed revert)
+ git revert --abort 2>/dev/null
+ # If prior --no-commit reverts already staged cleanly before this failure,
+ # revert --abort may be a no-op. Clean up staged and working tree changes:
+ git reset HEAD 2>/dev/null
+ git restore . 2>/dev/null
+ ```
+3. Display:
+ ```
+ ╔══════════════════════════════════════════════════════════════╗
+ ║ ERROR ║
+ ╚══════════════════════════════════════════════════════════════╝
+
+ Revert failed on commit ${HASH}.
+ Likely cause: merge conflict with subsequent changes.
+
+ **To fix:** Resolve the conflict manually or revert commits individually.
+ All pending reverts have been aborted — working tree is clean.
+ ```
+4. Exit with error.
+
+After all reverts are staged successfully, create a single commit:
+
+For MODE=phase:
+```bash
+git commit -m "revert(${TARGET_PHASE}): undo phase ${TARGET_PHASE} — ${REVERT_REASON}"
+```
+
+For MODE=plan:
+```bash
+git commit -m "revert(${TARGET_PLAN}): undo plan ${TARGET_PLAN} — ${REVERT_REASON}"
+```
+
+For MODE=last:
+```bash
+git commit -m "revert: undo ${N} selected commits — ${REVERT_REASON}"
+```
+
+
+
+Display the completion banner:
+
+```
+━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+ GSD ► UNDO COMPLETE ✓
+━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+```
+
+Show summary:
+```
+ ✓ ${N} commit(s) reverted
+ ✓ Single revert commit created: ${REVERT_HASH}
+```
+
+Show next steps:
+```
+───────────────────────────────────────────────────────────────
+
+## ▶ Next Up
+
+**Review state** — verify project is in expected state after revert
+
+/clear then:
+
+/gsd-progress
+
+───────────────────────────────────────────────────────────────
+
+**Also available:**
+- `/gsd-execute-phase ${PHASE}` — re-execute if needed
+- `/gsd-undo --last 1` — undo the revert itself if something went wrong
+
+───────────────────────────────────────────────────────────────
+```
+
+
+
+
+
+- [ ] Arguments parsed correctly for all three modes
+- [ ] --phase mode reads .planning/.phase-manifest.json using manifest.phases[TARGET_PHASE].commits
+- [ ] --phase mode falls back to git log if manifest entry missing
+- [ ] Dependency check warns when downstream phases have started (MODE=phase)
+- [ ] Dependency check warns when later plans reference target plan outputs (MODE=plan)
+- [ ] Dirty-tree guard aborts if working tree has uncommitted changes
+- [ ] Confirmation gate shown before any revert execution
+- [ ] Reverts use git revert --no-commit in reverse chronological order
+- [ ] Single commit created after all reverts staged
+- [ ] Error handling cleans up both first-call and mid-sequence conflict cases
+- [ ] git reset --hard is NEVER used anywhere in this workflow
+