| name | transition |
| description | Transition skill — sync status across wiki and backend |
| risk_class | hard |
| confirm_action | transition.apply |
Transition
Change a task's status in both the wiki and the external backend.
Arguments
$ARGUMENTS — Path to the task page and the target status (e.g., wiki/tasks/fix-login-bug.md in-progress)
Behavior
Step 1 — Read the task page
Read the task page. Extract current status and backend reference (gh_ref, jira_ref, or asana_ref).
Step 2 — Validate the target status
Target must be one of: backlog, to-do, in-progress, in-review, pending, blocked, done, deferred. If current → target is the same, tell the user and stop.
Step 3 — Resolve the backend mapping
Each backend maps these canonical statuses to its native states. Check @references/status-mapping.md (per-workspace) for the workspace's mapping table. If the file is missing, infer the mapping from the backend's own conventions (Jira workflow names, Asana custom field options, GitHub state + labels) — but flag uncertainty in the preview.
If the target status has no valid transition path from current in the backend (e.g., a Jira workflow that forbids pending → in-progress directly), present the available transitions and ask the user to pick one rather than forcing an invalid call.
Some statuses are wiki-only and have no backend equivalent (e.g., pending, deferred in many backends). For those, transition the wiki only and note in the preview that the backend is unchanged.
Step 4 — Decide the transition route, then act
Build the structured preview:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
TRANSITION PREVIEW
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Task: [[<task title>]]
Wiki: <current> → <target>
Backend: <name> (<ref>) — <native-from> → <native-to>
[or: wiki-only; backend unchanged]
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Branch A — Wiki-only (no backend touched). Soft write. Ask the
user once: "This transition won't touch any backend — proceed
wiki-only?" On yes, update the wiki status via
rubber-ducky frontmatter set, then append an audit-log entry so the
trail records the gate-bypass rationale:
rubber-ducky frontmatter set <task-file> status <target>
rubber-ducky frontmatter set <task-file> updated "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
rubber-ducky log append "[transition] wiki-only ($CURRENT → $TARGET) — no backend touched, user confirmed"
Branch B — Backend-touching. Hard write, fully gated by the CLI.
Request a confirm-token and invoke rubber-ducky transition, which
consumes the token, calls the backend, updates wiki status (and
closed if target is done), and appends the audit-log line:
TOKEN=$(rubber-ducky --no-json confirm request \
--action <backend>.transition \
--preview "$PREVIEW_TEXT")
rubber-ducky --json transition <task-file> \
--backend <backend> \
--to <target> \
--confirm-token "$TOKEN"
A missing / expired / action-mismatched token causes the verb to exit
non-zero before any external call, so an unconfirmed transition
cannot land in the backend.
Step 5 — Verify the outcome
The CLI's JSON output carries previousStatus / newStatus. Report
both to the user. If closed was stamped (target was done), surface
it.
Rules
- Backend transition fails → wiki status stays unchanged (the CLI verb refuses to write the wiki side when the backend call errors).
- Backend has no valid path from current to target → present alternatives in the preview, don't force an invalid call.
- Wiki-only transition → require explicit confirmation before applying, and log the bypass with
[transition] wiki-only ... so the trail records why no token was consumed.