| name | rollback |
| description | Safely undo an implementation phase or revert specific tasks, using the plan to map commits to tasks. Use this skill for: /rollback, 'undo the implementation', 'revert the last changes', 'go back to before implement', 'undo task 3', 'reset to pre-implementation'. Reads the plan to understand which commits belong to which tasks, shows what would be reverted, and requires explicit confirmation before acting. |
| model | sonnet |
Rollback
Portable intent doc: quoin/core/skills/rollback.md
You safely undo implementation work by mapping commits to plan tasks and reverting cleanly. You never destroy work without explicit confirmation and always explain exactly what will change.
§0 Model dispatch (FIRST STEP — execute before anything else)
This skill is declared model: sonnet. If the executing agent is running on a model
strictly more expensive than the declared tier, you MUST self-dispatch before doing the
skill's actual work.
Detection:
- Read your current model from the system context ("powered by the model named X").
- Tier order: haiku < sonnet < opus.
- Sentinel parsing: the user's prompt is checked for the
[no-redispatch] family.
- Bare
[no-redispatch] (parent-emit form AND user manual override): skip dispatch, proceed to §1 at the current tier.
- Counter form
[no-redispatch:N] where N is a positive integer ≥ 2: ABORT (see "Abort rule" below).
- Counter form
[no-redispatch:1] is reserved and treated as bare [no-redispatch] for forward-compatibility; do not emit it.
- If current_tier > declared_tier AND prompt does NOT start with any
[no-redispatch] form:
Dispatch reason: cost-guardrail handoff. dispatched-tier: sonnet.
Pre-dispatch 1M check (IVG-90 Layer 1+2):
- Run: python3 QUOIN_HOME/scripts/dispatch_config.py --decide --tier <declared_tier> --verbose
where <declared_tier> is the tier declared for this skill (e.g. "sonnet" or "haiku",
as shown in the dispatched-tier line immediately above).
- If the command returns "safe-path" on line 1:
Read the reason token from line 2 (config|cache|probe).
Emit the one-line advisory (verbatim, substituting with the line-2 token):
[quoin: 1M-unsafe declared-tier per <reason>; running SAFE PATH without dispatch]
Then proceed to §1/§0c at the current tier (treat as if [no-redispatch] were present).
Do NOT call the Agent dispatch. Do NOT call AskUserQuestion.
- If the command returns "dispatch" on line 1, OR if the script is missing / errors:
Continue to the Agent dispatch call below (today's path — fail-OPEN).
Spawn an Agent subagent with the following arguments:
model: "sonnet"
description: "rollback dispatched at sonnet tier"
prompt: "[no-redispatch]\n<original user input verbatim>"
Wait for the subagent.
Cache the safe result (best-effort):
python3 __QUOIN_HOME__/scripts/dispatch_config.py --write-cache --tier <declared_tier> --result safe
(Fail-OPEN: if the script errors or is missing, silently skip and continue.)
Return its output as your final response. STOP.
(Return the subagent's output as your final response.)
Abort rule (recursion guard):
- If the prompt starts with
[no-redispatch:N] AND N ≥ 2: ABORT before any tool calls.
- Print the one-line error:
Quoin self-dispatch hard-cap reached at N=<N> in rollback. This indicates a recursion bug; aborting before any tool calls. Re-invoke with [no-redispatch] (bare) to override.
- Then stop. Do NOT proceed to §1.
Manual kill switch:
- The user can prefix any user-typed slash invocation with bare
[no-redispatch] to skip dispatch entirely (e.g., [no-redispatch] /rollback).
- This is the user-facing escape hatch and intentionally shares syntax with the parent-emit form: a child cannot tell whether the bare sentinel came from the parent or the user, and that is by design — both paths want the same proceed-to-§1 outcome.
- Use this only when intentionally overriding the cost guardrail (e.g., for one-off debugging on a different tier).
Fail-graceful path with error-class triage (per architecture I-01):
-
If the Agent tool returns an error during dispatch, classify the error
message text BEFORE proceeding:
-
Error classification:
- Worktree-class: the error text contains the substring
Cannot create agent worktree, OR (the substring worktree AND
the substring not in a git repository). This is recoverable —
the harness tried to create a git worktree for isolation and the
project root is not a git repo. Continue to Worktree-class branch.
- Other-class: any other tool error, exception, or harness rejection
— skip to Other-class path below (existing fail-OPEN behavior).
-
1M-credit-class: if the error text contains the substring
Usage credits required for 1M context:
This is the 1M-context credit mismatch (IVG-89). The parent session carries
the context-1m-2025-08-07 beta header which propagates to all subagent calls;
the declared-tier model lacks 1M credits. Detection via model-name is impossible;
this post-dispatch error string is the only reliable signal.
Emit (verbatim):
[quoin: 1M-context credit mismatch on <tier> subagent dispatch; proceeding in-session at parent tier — run /model to switch this session to standard context for a permanent fix]
Cache the unsafe result (best-effort):
python3 __QUOIN_HOME__/scripts/dispatch_config.py --write-cache --tier <declared_tier> --result unsafe
(Fail-OPEN: if the script errors or is missing, silently skip and continue.)
Then proceed to §1 at the current tier (treat as if `[no-redispatch]` were present).
Do NOT retry the Agent dispatch. Do NOT call AskUserQuestion.
Source-mutating dispatch — two-phase worktree isolation (D-08):
STEP A0 — Consult the worktree-isolation decider FIRST (default is skip):
Run via Bash:
python3 QUOIN_HOME/scripts/worktree_isolation.py --decide
Isolation is opt-in (D-04): the decider prints skip unless
QUOIN_WORKTREE_ISOLATION=on, the dispatch.json config opts in, or a prior probe
wrote a works sentinel. If the output is skip, DO NOT write the sidecar and
DO NOT dispatch with isolation: "worktree" — skip STEP A / STEP B / STEP C and go
straight to a PLAIN Agent dispatch at the declared cheap-tier model (sonnet), with
no sidecar write and no worktree round-trip. Only when the output is attempt do
STEP A / STEP B / STEP C run.
STEP A — Write the dispatch sidecar BEFORE calling the Agent tool:
Run via Bash:
PROJECT_ROOT="$(python3 QUOIN_HOME/scripts/path_resolve.py --project-root)"
python3 QUOIN_HOME/scripts/dispatch_sidecar.py
--skill
--project-root "$PROJECT_ROOT"
--plan ""
(The WorktreeCreate hook reads this sidecar to resolve the nested git root.)
STEP B — Phase 1: Agent dispatch WITH isolation: "worktree" (normal path):
Call the Agent tool with isolation: "worktree" at the declared cheap-tier
model (sonnet for this skill). The deployed WorktreeCreate hook at
QUOIN_HOME/hooks/worktreecreate.sh reads the sidecar, runs
git_root_for_dispatch.py, and (when a single nested repo resolves)
creates a worktree IN the nested git root and returns its path.
One-time probe (opt-in path only): when the probe sentinel is still unknown,
instruct the child to record its working directory to a marker; after the Agent
returns, compare it to the created worktree path and persist the result exactly
once via
python3 QUOIN_HOME/scripts/worktree_isolation.py --write-probe --result works|broken
STEP C — Phase 2 retry WITHOUT isolation (on Worktree-class error):
If Phase 1 fails with a Worktree-class error (regex above), the hook
either returned skip (no stdout → harness fails) or encountered an error.
Re-dispatch the Agent call WITHOUT isolation: "worktree", at the SAME
declared cheap-tier model (sonnet). Do NOT escalate to parent tier.
Emit one-line audit:
[quoin-stage-1: worktree dispatch skipped; proceeding at sonnet without isolation]
STEP D — Done:
No child-side coordination required. The harness handles cwd correctly:
on Phase 1 success, child sees the worktree as cwd; on Phase 2, child
inherits the parent's session cwd (today's behavior, unchanged).
-
Worktree-class branch: handled by Phase 2 (§0-sidecar block above).
Phase 2 retries at the declared cheap-tier model without isolation.
Do NOT use AskUserQuestion or proceed-current-tier for source-mutating skills.
-
Other-class path (non-worktree Agent errors):
Do NOT abort the user's invocation.
Emit the bare warning (verbatim):
[quoin-stage-1: subagent dispatch unavailable; proceeding at current tier]
If this path was reached via a worktree-class error, ALSO emit the
classification line (second, separate):
[quoin-stage-1: error-class=worktree; user-choice=c; proceeding at current tier]
Then proceed to §1 at the current tier (fail-OPEN per I-01).
Otherwise (already at or below declared tier, OR prompt has [no-redispatch] sentinel, OR dispatch unavailable): proceed to §1 (skill body).
Session bootstrap
On start:
- Read
<task_dir>/current-plan.md to understand task structure. Resolve <task_dir> via python3 __QUOIN_HOME__/scripts/path_resolve.py --task <task-name> [--stage <N-or-name>]. architecture.md: ALWAYS <task-root>/architecture.md. cost-ledger.md: ALWAYS <task-root>/cost-ledger.md (line 2 below — NOT edited per D-03). If exit code 2: display stderr verbatim, fall back to task root, ask user to disambiguate.
- Append your session to the cost ledger:
.workflow_artifacts/<task-name>/cost-ledger.md (see cost tracking rules in CLAUDE.md) — phase: rollback
Core principle
Show before you act. Always display what will be reverted, let the user confirm, then execute. Never auto-revert.
Process
Step 1: Understand the state
Read:
- The plan —
<task_dir>/current-plan.md to understand task structure (where <task_dir> is resolved per Session bootstrap step 1 via python3 __QUOIN_HOME__/scripts/path_resolve.py)
- Git log — map recent commits to plan tasks (by commit message, branch, or scope)
- Current diff — any uncommitted changes that would be affected
- Session state — what phase we're in and what's been completed
Build a commit-to-task map:
Task 1: "Add retry logic to payment service"
→ commit abc1234: feat(payment): add exponential backoff to processRefund
→ commit def5678: test(payment): add retry logic tests
Task 2: "Update API gateway routing"
→ commit ghi9012: feat(gateway): add /v2/payments route
Task 3: "Add monitoring dashboards"
→ (no commits — not yet implemented)
Uncommitted:
→ src/payment/config.ts (modified, unstaged)
Step 2: Determine rollback scope
Use AskUserQuestion to ask the user what they want to undo:
AskUserQuestion(
question="What would you like to roll back?",
options=[
{label: "Full phase rollback", description: "Revert everything from /implement to the pre-implementation state. Cleanest option."},
{label: "Selective task rollback", description: "Revert specific tasks only; dependencies will be flagged."},
{label: "Last commit only", description: "Quick undo of the most recent commit."}
]
)
Full phase rollback — revert everything from /implement:
- Find the commit or branch point where implementation started
- This is the cleanest option
Selective task rollback — revert specific tasks:
- Use the commit-to-task map to identify which commits to revert
- Warn about dependencies ("Task 3 depends on Task 2 — reverting Task 2 will also require reverting Task 3")
Last commit only — quick undo of the most recent change
Step 3: Show the impact
Present exactly what will happen:
# Rollback preview
## Scope: <full phase / tasks 2-3 / last commit>
### Commits to revert (newest first)
1. `ghi9012` feat(gateway): add /v2/payments route
- Removes: src/gateway/routes/v2-payments.ts
- Modifies: src/gateway/routes/index.ts (route registration removed)
2. `def5678` test(payment): add retry logic tests
- Removes: test/payment/retry.test.ts
3. `abc1234` feat(payment): add exponential backoff
- Modifies: src/payment/service.ts (processRefund reverted to original)
- Removes: src/payment/retry-config.ts
### Uncommitted changes that will be affected
- src/payment/config.ts — ⚠️ has unsaved changes, will stash first
### After rollback
- Branch `feat/payment-retry` will be at commit <hash> (pre-implementation state)
- Plan tasks 1-2 will need re-implementation
- Task 3 was never started — unaffected
### Dependencies to check
- No other branches depend on these commits
- No open PRs reference these commits
Step 4: Confirm and execute
Use AskUserQuestion to get explicit user confirmation before proceeding:
AskUserQuestion(
question="Proceed with the rollback as shown above?",
options=[
{label: "Confirm rollback", description: "Execute the rollback as previewed above."},
{label: "Cancel", description: "Abort; no changes will be made."}
]
)
If the user selects "Cancel": STOP. Tell the user: "Rollback cancelled. No changes were made."
Then (after "Confirm rollback"):
For full phase rollback:
git stash push -m "rollback-stash-$(date +%Y%m%d-%H%M%S)"
git reset --hard <pre-implementation-commit>
git reset --hard <base-branch-merge-base>
For selective task rollback:
git revert --no-commit <commit-hash-3>
git revert --no-commit <commit-hash-2>
git revert --no-commit <commit-hash-1>
git commit -m "revert: undo tasks 2-3 from <task-name>
Reverted commits: ghi9012, def5678
Reason: <user's reason or 'requested by user'>
Tasks reverted: Task 2 (API gateway routing), Task 3 (monitoring)
Tasks preserved: Task 1 (retry logic)"
For last commit:
git revert HEAD --no-edit
Step 5: Update session state
After rollback:
- Update the session file (
.workflow_artifacts/memory/sessions/<date>-<task-name>.md): mark rolled-back tasks as pending again
- Note the rollback in the session's decision log
- Update
.workflow_artifacts/memory/git-log.md with the revert commits
Step 6: Report
Tell the user:
- What was reverted
- Current state of the branch
- Which plan tasks are now pending again
- Whether any stashed changes need to be restored (
git stash pop)
- What to do next (re-implement, modify the plan, etc.)
Safety rules
- Never force-push. Use
git revert for shared branches, git reset only for local-only branches. Ask first.
- Stash before resetting. Always preserve uncommitted work.
- Check for dependents. If other branches or PRs depend on the commits being reverted, warn the user.
- Preserve the plan. Rollback reverts code, not planning artifacts. The plan, critic responses, and review docs stay intact.
- Log the rollback. Append to the session state and git-log so future sessions know what happened.
Edge cases
Rollback after partial merge/PR:
- If commits were already pushed, use
git revert (not reset)
- If a PR was merged, create a revert PR instead
Rollback with database migrations:
- Warn the user that code rollback doesn't undo database changes
- List any migration files that were part of the rolled-back commits
- Suggest running down-migrations if they exist
Rollback across repos:
- If implementation touched multiple repos, rollback each independently
- Present the full cross-repo impact before confirming