Skip to content
Back to skills

Standup

ASecurity

Read-only release + backlog recap. Reports the last release, commits-since-tag classified by conventional-commit type with the semver bump they trigger and pushed-vs-origin state, the backlog bucketed (open/picked-up/dropped with epic parent→child nesting), the roadmap epics with their per-task tallies, and condensed open questions — then recommends the next pickup. One `cli.mjs recap` call returns all six recap keys. Invoke any time (on demand) to plan a release or choose the next thing to b...

  • 14 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added October 4, 2026
ai-agentsnodegit

Works with

  • terminal
  • cli

Security analysis

A100/100

Pro scans all 4 files and shows the line behind each finding

Scanned October 4, 2026

npx -y skills add friedbotstudio/baseline --skill standup --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Standup?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Standup
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/friedbotstudio-standup/badge)](https://www.skillsdirectory.com/skills/friedbotstudio-standup)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: standup
owner: baseline
description: Read-only release + backlog recap. Reports the last release, commits-since-tag classified by conventional-commit type with the semver bump they trigger and pushed-vs-origin state, the backlog bucketed (open/picked-up/dropped with epic parent→child nesting), the roadmap epics with their per-task tallies, and condensed open questions — then recommends the next pickup. One `cli.mjs recap` call returns all six recap keys. Invoke any time (on demand) to plan a release or choose the next thing to build; a compact form is also surfaced at session start. Not a workflow phase; never writes CHANGELOG; never starts or commits work.
disable-model-invocation: true
---

# standup — where are we, what's next

A read-only recap utility in the family of `audit-baseline` / `rca`: it reads state and reports, it never mutates. Run it whenever you sit down to plan a release or pick the next piece of work. It is **not** a workflow phase — it does not enter the Track Guard ordering and never blocks a commit.

## What it answers

1. **Shipped** — the last released version (from `CHANGELOG.md` / the latest tag).
2. **Staged but unreleased** — every commit since the last tag, classified by conventional-commit type, with the aggregate semver bump those commits will trigger (read from `.releaserc.json` at runtime, so it never drifts from the release config) and the pushed-vs-origin state.
3. **Backlog** — entries bucketed `open` / `picked-up` / `dropped`, with epic children nested under their parent.
4. **Open questions** — `pending-questions.md` condensed to id + question + blocker.
5. **Roadmap** — the execution plan's epics with their status and per-task tallies, the rows still open under each, plus the Progress bullets, read from `project.json → roadmap.path`.
6. **Recommended next pickup** — assembled in main context (see Article II below).

## How to run

One invocation returns the whole recap. Use the CLI front door:

```
node .claude/skills/standup/cli.mjs recap [--json] [--root <repo-root>] [--remote]
```

Without `--json` it prints the bounded rendering; with `--json` it prints the raw `StandupRecap`.

The bound is a **threshold, not a blanket collapse**. Detail renders while the pile is small and degrades to counts once printing it back would reproduce the cost the CLI exists to remove:

| Section | At or below the bound | Above it |
|---|---|---|
| Unreleased commits | one line per commit: type, bump, subject | counts-by-type plus the aggregate bump (`COMMIT_DETAIL_MAX` = 20) |
| Roadmap epics | each epic's OPEN rows listed beneath it — planned and in-progress, never done | per-task tallies only (`OPEN_TASK_DETAIL_MAX` = 20, measured across the whole plan) |

Collapsing at every size was the older behaviour, and it cost a second `--json` pass to answer "what is actually in this pile?" for a four-commit pile. Done rows are never listed at either size: they are the bulk of a finished epic and carry no pickup signal.

Alongside those, the pushed-vs-origin state and the declared completeness gate each render as their own line, so the two questions that decide whether to cut — *is this even pushed?* and *may a half-wired feature ship?* — are answered without opening git or `project.json`.

Every rendered detail line is whitespace-collapsed, clipped to 96 characters, and stripped of C0/C1 control characters. Roadmap titles, commit subjects and question bodies are all repository-controlled content on its way to a terminal, and a row title in this repo already runs past 1000 characters.

### `--remote` — prove the release picture against the remote

Every release figure in the recap is read from **local** refs: `git describe --tags` for the last tag, `rev-list @{upstream}...HEAD` for the pushed-vs-origin state. On a clone that has not fetched, both answer from a stale view. On 2026-08-13 that reported `v0.21.0` with 70 unreleased commits while `v0.22.0` was already tagged, published to npm, and released — the operator read a shipped release as an unshipped pile.

The default run therefore states its own limitation in the Release block (`Figures read local refs, not fetched`) and names the remedy. Pass `--remote` to check instead of caveat:

- `git ls-remote --tags origin` and `--heads origin <branch>` are read-only; nothing is fetched and no ref is mutated.
- A newer remote tag or a diverged branch head adds **`stale-remote-refs`** to `degraded[]` and fills `release.remote` with `{probed, stale, remoteTag, remoteHead, reason}`.
- A probe that cannot run — offline, no remote, auth, timeout — adds **`remote-probe-failed`** instead, keeps the local figures untouched, and still exits 0. "I could not check" never renders as "you are current", and never as "you are stale" either.
- `release.remote.headState` reports the branch-head comparison as one of `diverged` / `matched` / `unreachable` / `not-comparable`. The last two are distinct on purpose: `unreachable` means the branch tracks a remote and the probe failed, while `not-comparable` means there was no upstream to compare against (a detached HEAD, a branch never pushed, or a branch the remote does not carry). A checkout with nothing to compare is never reported as one whose refs match.
- Tag comparison is numeric on strict semver, so `v10.0.0` beats `v9.0.0`; any ref the remote advertises that does not parse is discarded before it can influence the answer.

`release.remote` is `null` on the default path, which is how a reader tells *not checked* from *checked and current*.

**`StandupRecap` carries six keys. All six come back in that one call:**

| Key | What it holds |
|---|---|
| `release` | `lastVersion`, `lastTag`, `commitsSinceTag[]` classified by conventional-commit type with the semver `bump` each triggers, and `upstream` — `{state, ahead, behind}`, where `state` is `ahead` / `behind` / `up-to-date` / `no-upstream` |
| `releaseModel` | the declared release POLICY from `project.json → release` — drives the regime-aware recommendation below |
| `backlog` | entries bucketed `open` / `picked-up` / `dropped`, epic children nested under their parent |
| `pendingQuestions` | `pending-questions.md` condensed to id + question + blocker |
| `roadmap` | the execution plan's `epics[]` (number, title, tag, status, per-task tallies, plus `openTasks[]` of `{id, status, title}` for the rows still open) and its Progress bullets |
| `degraded` | one marker per source that could not be read — `no-roadmap-plan`, `no-release-model`, and so on |

Do not re-derive any of these by hand afterwards. Re-reading the roadmap file or re-running `git log` after this call is the multi-pass behaviour the CLI replaced; every key above is already in the result.

Six is the whole list, and it stays six. New data nests inside an existing key rather than becoming a seventh — `openTasks` sits on the roadmap epic, `upstream` and `remote` sit on `release`. `tests/standup-cli-recap.test.mjs:71` asserts the count, so widening the top level breaks a contract other readers depend on.

It degrades gracefully — on a non-git tree, a repo with no tags, or missing memory files it names the missing precondition in `degraded[]`, still exits 0, and never throws. `gather.mjs` remains importable as the collector for in-process callers such as the session-start hook.

## Article II — where the judgment lives

Per CLAUDE.md Article II, decisions live in main context. The helper only **gathers** the mechanical recap; it does not pick what to build next. After reading the helper's JSON, the recommendation — the single suggested next pickup and its one-line rationale (smallest unblocker first) — is reasoned out **in main context**, not emitted by the helper. The session-start surface (the `memory_session_start` hook) shows only the compact mechanical recap plus a pointer back to `/standup`; the full judgment recommendation is on-demand here.

### Release-model-aware recommendation (AC-303)

When `gatherSync` surfaces a `releaseModel` (from `project.json → release`), the "can this unreleased pile ship?" judgment becomes **regime-aware** — the model tells main context which regime it is in, the way `git.workflow_model` overrides generic branching instincts:

- **`release_trigger: on-push` / `release_cycle: continuous` / `consumer_upgrade_cadence: frequent`** → recommend **pushing early and often**; an unpushed pile is the risk. Fix-forward is cheap.
- **`release_trigger: on-tag`|manual / `release_cycle: sprint-based`|manual / `consumer_upgrade_cadence: rare`** → a release is a CD pressing: recommend **gating the cut on a completeness/coherence audit**. When `release.completeness_gate.half_wired_blocks_release` is true, do **not** recommend cutting while any feature in the unreleased set is half-wired-on-the-disc (shipped-dark but opt-in-broken).

A `no-release-model` marker in `degraded` means no policy is declared — fall back to the pre-model behavior (report the semver bump from `.releaserc` and let the human decide). The judgment stays in main context (Article II); `collectReleaseModel` only surfaces the config.

## Constraints

- **Read-only.** Reads git, `.releaserc.json`, `CHANGELOG.md`, and the memory files. Writes nothing.
- **Never writes `CHANGELOG.md`** — semantic-release owns it in CI.
- **Never starts, stages, or commits work** — it recommends; you act.
- **Deterministic, offline core** — identical repo + memory state yields identical helper output (no clock read, and no network call, in the core path). `--remote` is an explicitly non-deterministic opt-in that sits **outside** this guarantee: its result depends on a remote that can change between two otherwise identical runs. The guarantee is narrowed in scope, not weakened — every caller that does not pass `--remote`, which is every in-process caller on disk including the `memory_session_start` hook, keeps exactly the property it had before.
- **Read-only over the network too** — the probe runs `git ls-remote` only. It never fetches, never writes a ref, and never changes what a later `git status` or `/commit` sees.

Files in this skill

  • SKILL.md9.9 KB
  • cli.mjs1.2 KB
  • gather.mjs19.3 KB
  • render.mjs8.2 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.