plugins/avanti/skills/promote/SKILL.md
Move an artifact forward through its lifecycle and record the transition in pulse
npx skillsauth add acostanzo/quickstop promoteInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
You are the /avanti:promote orchestrator. When the user runs /avanti:promote <artifact>, resolve the artifact's current state, propose the next legal transition, and on confirmation: move the file (for plans and tickets), update its frontmatter status: and updated:, and append a pulse entry noting the transition.
Read ${CLAUDE_PLUGIN_ROOT}/references/sdlc-conventions.md for the full lifecycle model and the legal-transition rules.
$ARGUMENTS is either a path or a shortcut. Shortcuts:
plan:<slug> → project/plans/*/<slug>.mdticket:<id-slug> → project/tickets/*/<id-slug>.mdadr:<NNN-slug> → project/adrs/<NNN-slug>.mdIf a path is given directly (starts with project/ or is absolute), use it as-is. Otherwise parse the shortcut.
Check for --supersedes <adr-id> in $ARGUMENTS. Store the target ADR id (zero-padded, e.g. 007) as SUPERSEDES_ID if present, else null.
Run git rev-parse --show-toplevel 2>/dev/null. Abort on failure. Store as REPO_ROOT.
If input was a shortcut, glob under REPO_ROOT:
plan:<slug> → match project/plans/*/<slug>.md (zero, one, or many — see below)ticket:<id-slug> → match project/tickets/*/<id-slug>.mdadr:<NNN-slug> → match project/adrs/<NNN-slug>.mdResolution rules:
Read ARTIFACT_PATH. Extract status: from the frontmatter.
For plans and tickets, the folder is authoritative:
project/plans/draft/*.md → draftproject/plans/active/*.md → activeproject/plans/done/*.md → doneproject/tickets/open/*.md → open or in-progress (use frontmatter status: to distinguish, since both live in open/)project/tickets/closed/*.md → closedFor ADRs, the folder is flat — read status: from frontmatter directly.
Store the resolved current state as CURRENT_STATE. If status: and folder disagree (a convention violation), tell the user explicitly and ask whether to proceed based on folder (authoritative) or frontmatter — this is rare and worth a pause.
| Type | Current | Next | Notes |
|---|---|---|---|
| plan | draft | active | move draft/ → active/ |
| plan | active | done | move active/ → done/; guard: all tickets in plan's tickets: array must be in closed state |
| plan | done | — | terminal; error |
| ticket | open | in-progress | frontmatter only; folder stays in open/ |
| ticket | in-progress | closed | move open/ → closed/ |
| ticket | open | closed | direct close, move open/ → closed/ (ask which if ambiguous) |
| ticket | closed | — | terminal; error |
| adr | proposed | accepted | frontmatter only |
| adr | accepted | superseded | frontmatter only; requires --supersedes <id> |
| adr | superseded | — | terminal; error |
If TYPE is plan and NEXT_STATE is done, scan every ticket ID in the plan's tickets: frontmatter array and check that each corresponding file lives in project/tickets/closed/. If any ticket is not closed, abort with a clear message:
Cannot promote plan "${<plan-slug>}" to done: N tickets still open.
- t3-foo (open)
- t5-bar (in-progress)
Close each open ticket first with /avanti:promote ticket:<id-slug>.
This mirrors the convention stated in references/sdlc-conventions.md: "A plan only leaves active when every ticket it owns is closed."
If the current state has exactly one next legal state, use it. If multiple (ticket open), use AskUserQuestion to pick:
If no legal next state exists (terminal) → abort with:
${TYPE} at ${ARTIFACT_PATH} is already in terminal state ${CURRENT_STATE}.
There is no legal forward transition. To retire or replace, author a new
artifact (plan: draft a successor; ADR: propose a new ADR that supersedes this one).
Store the target state as NEXT_STATE.
If TYPE is adr and NEXT_STATE is superseded:
--supersedes <new-adr-id> pointing to the ADR that replaces this one."project/adrs/${SUPERSEDES_ID}-*.md glob. If no match, abort with "No ADR found with id ${SUPERSEDES_ID}."Store the superseding ADR path as SUPERSEDER_PATH.
Tell the user the proposed transition and ask for confirmation via AskUserQuestion:
${TYPE}: ${ARTIFACT_PATH}
${CURRENT_STATE} → ${NEXT_STATE}
For ticket open → closed direct, note that in-progress is being skipped. For ADR supersession, also name the superseding ADR. If the user declines, abort without changes.
Run date +%Y-%m-%d via Bash. Store as TODAY.
If NEXT_STATE is active, target folder is project/plans/active/.
If NEXT_STATE is done, target folder is project/plans/done/.
mkdir -p <target-folder> via Bash. Plan-state subdirectories (active/, done/) are avanti's destination — create them on demand if missing rather than failing the move.status: ${CURRENT_STATE} → status: ${NEXT_STATE}, bump updated: ${TODAY}.mv ARTIFACT_PATH <target-folder>/<basename> to move the file.Store the new path as NEW_PATH.
If NEXT_STATE is in-progress:
status: open → status: in-progress, bump updated: ${TODAY}.project/tickets/open/. NEW_PATH = ARTIFACT_PATH.If NEXT_STATE is closed:
project/tickets/closed/ exists: mkdir -p via Bash if missing.status: ${CURRENT_STATE} → status: closed, bump updated: ${TODAY}.mv ARTIFACT_PATH project/tickets/closed/<basename>.Store the new path as NEW_PATH.
NEXT_STATE is either accepted or superseded; the folder is flat, so only frontmatter changes.
If NEXT_STATE is accepted:
status: proposed → status: accepted, bump updated: ${TODAY}.If NEXT_STATE is superseded:
status: accepted → status: superseded, bump updated: ${TODAY}, and set superseded_by: ${SUPERSEDES_ID}.supersedes: field records this ADR's id. If the field is absent, add it immediately after superseded_by:.NEW_PATH = ARTIFACT_PATH for ADRs.
Invoke /avanti:pulse with a terse transition message so the journal records the lifecycle event. Message template:
Promoted ${TYPE} ${<basename-without-ext>}: ${CURRENT_STATE} → ${NEXT_STATE}.
For ADR supersession, append " (superseded by ${SUPERSEDES_ID})."
/avanti:pulse is model-invocable, so dispatching it from this skill is the supported path. If the Skill call is unavailable in the current harness, fall back to appending the entry directly to today's pulse file per the convention in ${CLAUDE_PLUGIN_ROOT}/references/sdlc-conventions.md#pulse-structure.
Tell the user:
${TYPE} promoted: ${CURRENT_STATE} → ${NEXT_STATE}
path: ${NEW_PATH}
updated: ${TODAY}
pulse: logged to project/pulse/${TODAY}.md
For ADR supersession, also report the cross-linked superseder path.
/avanti:status to list known artifacts.--supersedes: abort with usage.--supersedes target not found: abort, name the missing id.documentation
Surface (and optionally fix) doc-tree drift — duplicates, dead links, stale docs, template non-compliance, missing `## Related` blocks. Read-only by default; `--apply` does mechanical fixes; `--apply-semantic` emits diffs for human review.
documentation
Full-text search over the repo's `docs/` tree (FTS5-backed). Returns ranked hits with file paths, tags, and matching snippets.
testing
Retrieval-augmented Q&A over the repo's `docs/` tree. Returns a one-paragraph synthesis plus citations (doc path + heading anchor) and per-citation corroboration verdicts. Field shape and ordering are locked at M3; M5 populates the verdicts.
documentation
Scaffold a new doc under `docs/` from a Diátaxis template, or update an existing one and bump its `updated:` date. Suggests `## Related` candidates and refreshes the FTS5 index on write.