standup command
Daily repo hygiene — review the day's repo state, then perform the cleanups under per-action confirmation. Fast-forward only, never destructive without a yes.
/ca:standup$ca-standup/ca-standupAvailability is read from each shipped host catalog. Preview-only commands are marked on the page.
What it does
Section titled “What it does”The daily hygiene checklist, made routine and gated: fetch and offer a fast-forward pull, list
locally-merged branches for individual confirm-and-delete, list stale worktrees for either
confirm-and-delete as one named group or individually, surface stashes, uncommitted changes, and
un-pushed commits with a suggested next step, run a read-only advisory board-drift sweep comparing
recent merge history against open-tasks.md, and offer to archive long-done tasks one at a time.
Nothing destructive happens without an explicit yes, and the task board is never auto-flipped — a
drifted task is resolved only through /ca:task done <id>.
A locally-merged branch that was squash-merged (its upstream already pruned to : gone]) will
typically refuse a plain git branch -d. When that happens, standup checks the merged PR record
for that branch; if it proves containment (the PR’s headRefOid matching the branch’s local tip),
-D is offered instead, with the proof and the branch name both stated in the confirmation.
Without that proof, a refusal is reported and left alone.
Stale worktrees are presented together with the evidence for each, then offered as one explicitly enumerated group — naming every member — so a multi-lane day doesn’t turn into four or five identical yes prompts. Declining the group falls back to confirming each worktree on its own; either way, the main worktree is never touched and nothing is removed without a yes.
The archival sweep exists because long-done tasks accumulate until the in-flight count stops
meaning anything. Each aged item is proposed separately and moved by its own helper call: the
record is appended to done-tasks.md before it is removed from open-tasks.md, so an
interrupted sweep leaves a duplicate the next run absorbs rather than a lost record. That ordering
is why the confirmations aren’t batched — one yes, one move. An item marked [x] with no
(done YYYY-MM-DD) stamp can’t be aged and is never proposed; it can only be archived deliberately
on request. Declining costs nothing: an unarchived task stays exactly where it is.
/ca:standupTakes no arguments.
Example
Section titled “Example”> /ca:standup
Fetched. Branch is 2 commits behind origin/main, tree clean — fast-forward pull? (y/n)> yPulled 2 commits.
Locally-merged branches: fix/typo-in-readme — delete? (y/n)> yDeleted fix/typo-in-readme.
Board-drift sweep: 1 DRIFTED task (poc.docs.0001 merged but not marked [x] in open-tasks.md).Resolve with /ca:task done poc.docs.0001. The sweep never auto-flips tasks.
Archival sweep: 3 done tasks older than the cutoff. poc.api.0004 — "rate-limit the webhook" (done 2026-05-02) — archive? (y/n)> yArchived poc.api.0004 to done-tasks.md. poc.api.0007 — "retry on 429" (done 2026-05-11) — archive? (y/n)> nLeft poc.api.0007 on the board.When to reach for it
Section titled “When to reach for it”The daily cleanup pass with your yes on each destructive step. For a read-only snapshot without
acting, use /ca:status; to actually flip the drifted task, use /ca:task.
| Gate | When | Effect |
|---|---|---|
| per-action confirmation | pruning a merged branch or removing a stale worktree | branch deletions are confirmed one at a time; stale worktrees may be confirmed as one explicitly enumerated group naming every member, or individually if you’d rather keep some — either way declining leaves items in place, and nothing is ever an implied yes |
| fast-forward-only pull | the working tree is clean and the branch is behind upstream | a —ff-only pull is offered; a dirty tree withholds the pull and reports the dirty state instead, and a diverged branch is refused rather than merged |
| one confirmation per archived task | sweeping long-done tasks off the board | each aged item is proposed on its own and archived only on its own yes — never a batched “archive all 12?”, and an undated item is never proposed at all because it cannot be aged |
Related
Section titled “Related”Source
Section titled “Source”Source — plugins/ca/commands/standup.md (v2.18.2)
---description: Daily repo hygiene — review the day's repo state, then perform the cleanups under per-action confirmation. Fast-forward only, never destructive without a yes.argument-hint: (none)---
# /ca:standup — daily hygiene
The best-practice checklist you run when you sit down to code, made routine andgated. The SessionStart briefing *reports* hygiene state read-only; this command iswhere the *actions* happen — each one confirmed individually, none taken unbidden.Arbiter gathers and proposes; you decide every mutation.
## Flow
The orchestrator reads the current repo state (reusing the briefing's read-onlycomputation — branch, ahead/behind, dirty tree, stashes, prune-candidate branches,stale worktrees) and presents it, then offers each applicable action in turn. Skipan action that has no candidates; never bundle confirmations across differentactions or items — branch deletions always stay per-item, and the sole exceptionis step 3's explicitly enumerated worktree group, which is still one confirmationthat names every member, never an implied yes.
1. **Fetch + fast-forward pull** — kick `git fetch`, then offer a **`--ff-only`** pull of the current branch. Eligibility is the briefing summary's `ff_pull_eligible` flag (SH-6: clean working tree AND behind upstream) — the same pure helper the SessionStart briefing computes, not a condition re-derived here. On a dirty tree the pull is withheld and the dirty state is reported instead. A diverged branch (would need a merge) is refused with a diverged-branch message — never a merge commit.2. **Prune merged local branches** — list local branches already merged on remote (the `: gone]` upstream set), excluding the current branch and the default (`main`). Delete a listed branch only after an explicit per-branch confirmation; declining leaves it in place.
A `: gone]` branch that was squash-merged will typically **refuse** plain `git branch -d` — its upstream is already pruned, so `-d` has nothing to test reachability against. Before reporting that as a stop, check for a merged PR record: `gh pr list --head <branch> --state merged --json headRefOid`. When a `MERGED` PR's `headRefOid` equals that branch's local tip, containment is proven and `git branch -D` is permitted — with the proof stated and the branch named in the confirmation — mirroring the `post-merge-cleanup` Phase 5 contract. Without that proof, a refusal stays a report-and-stop; never guess.3. **Remove stale worktrees** — list stale/merged worktrees (branch gone-or-merged, or path missing on disk), never the main worktree. Present the full stale list together with each item's evidence, then offer removal as an explicitly **enumerated group** — one confirmation that names every member — as well as per-item confirmation for anyone who wants to keep some. Declining the group falls back to per-item confirmation, and declining any item leaves it intact.4. **Surface stashes / dirty / un-pushed** — list stashes, uncommitted changes, and un-pushed commits, each with a suggested next step (`/ca:commit`, `git push`, `git stash show`). Report-and-route only: never discard a stash, reset, or push.5. **Advisory board-drift sweep** — run `git log` over the recent merge window (since the last `ca`-scoped tag, or a rolling 30-day window when no tag exists) and pipe that text to `"$PY" "${CLAUDE_PLUGIN_ROOT}/hooks/boardsync.py" reconcile`. Resolve `$PY` once by presence — `PY=python3; { command -v python3 >/dev/null 2>&1 && python3 --version >/dev/null 2>&1; } || PY=python` — never `python3 X || python X`, which reruns X on any nonzero exit (#577); this resolution covers step 6's helper call too. Display the advisory drift report as-is: DRIFTED tasks (work merged but board state not `[x]`) and informational UNKNOWN ids (in the log but absent from the board). This step is read-only and best-effort — the dotted-id grep can miss a task never named in a commit. The board is never mutated here; `open-tasks.md` is never written. Any drifted task must be resolved explicitly through `/ca:task done <id>` — the only blessed board writer. State this clearly to the user; do not auto-flip.
6. **Archival sweep — proposed per item, never batched** (B-24). Long-done tasks accumulate on the board; they are already excluded from the in-flight count. List dated done items strictly more than 14 calendar days old (`ARCHIVE_CUTOFF_DAYS`), then ask about **each one separately** and archive only the ones the user says yes to: `"$PY" "${CLAUDE_PLUGIN_ROOT}/hooks/taskwrite.py" archive <id>`.
One confirmation per item, one helper call per item — the two map 1:1 on purpose. A batched "archive all 12?" turns twelve decisions into one, and the helper's own per-item ordering (append to `done-tasks.md` first, then remove from `open-tasks.md`) is what makes an interrupted sweep recoverable; a batch loop that answered once would throw that away.
An item marked `[x]` with **no `(done YYYY-MM-DD)` stamp** cannot be aged, so it is never in the proposed set. Offer it only if the user asks, and only with `--allow-undated` — both `/ca:task done` and the board classifier require the stamp, so an unstamped entry is legacy or override-era and its real age is unknown.
Declining is always available and costs nothing: an unarchived task stays exactly where it is. Never archive without a yes.
Present a one-line summary of what was done and what was declined.
## When NOT to use
- A read-only state snapshot without acting → `/ca:status`.- Install health (interpreter, payload, hooks) → `/ca:doctor`.- Transcript hygiene to extend the session → `/ca:prune`.- Committing staged work → `/ca:commit`.
## Hard gate
- MUST pull with `--ff-only` and ONLY on a clean working tree — never a merge commit, never on a dirty tree, never a rebase.- MUST exclude the current branch and the default branch from branch pruning, and the main worktree from worktree cleanup.- MUST confirm branch deletions individually — no batched or implied yes. Worktree removals MAY be confirmed as one explicitly enumerated group (naming every member) or individually; either way there is no implied yes, and declining the group falls back to per-item confirmation rather than removing anything.- MUST treat stash / dirty / un-pushed state as report-and-route only — never discard, reset, force, or push on the user's behalf.- MUST NOT write to or force-push the default branch.- MUST NOT auto-flip any board entry during the drift sweep — the sweep is advisory and read-only; resolving a drifted task is the user's decision and routes exclusively through `/ca:task done <id>`.