Generate and commit user-facing release notes by querying the GitHub Project board, updating the OPS release notes file, and keeping the homepage roadmap board in sync.
64
80%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Low
Low-risk findings worth noting
Fix and improve this skill with Tessl
tessl review fix ./.claude/skills/update-release-notes/SKILL.mdGenerate user-facing release notes from the live GitHub Project board and commit them to the OPS data files via a PR.
Trigger phrases:
Apply this tone to ALL release notes output — both the review draft and the final data.js entries. Keep item titles in roadmapData.json short and user-facing in the same voice (a few words, not a sentence).
data.js content"You can now create grants in OPS! This includes adding grant-specific details such as the NOFO number, budget periods, ALN numbers and grant numbers."
"We fixed a bug related to the default sort order in the agreements list. It now sorts the agreement name alphabetically by default."
"Based on your feedback, the agreements list page now includes new columns such as Start, End, Total and FY25 Obligated..."
Default to the last 2 weeks from today. Allow overrides:
# Example: last 2 weeks — guarded by platform, since macOS's `date -v` and Linux's
# `date -d` aren't interchangeable (running the wrong one silently produces an empty
# START_DATE instead of erroring loudly).
if [[ "$(uname)" == "Darwin" ]]; then
START_DATE=$(date -v-14d +%Y-%m-%d)
else
START_DATE=$(date -d '14 days ago' +%Y-%m-%d)
fi
END_DATE=$(date +%Y-%m-%d)git remote -vConfirm you're in the OPRE-OPS repository before proceeding.
Fetch items from the ACF OPRE-OPS project:
gh project item-list 5 --owner HHS --format json -L 200gh project item-list defaults to 30 items, which can silently truncate a long-lived board. Pass a generous -L and compare the returned item count against the JSON's totalCount field; if they don't match, re-run with a higher -L rather than proceeding on an incomplete list.
Filter results into two groups:
"Done" on the roadmap board (Step 8)."Currently Developing" column on the roadmap board (Step 8) — read regardless of whether you're updating release notes this time.The date range from Step 1 is meant to narrow "Shipping now" to recent work, but gh project item-list's JSON doesn't reliably include the project item's own updatedAt — only the linked issue/PR (under content) has timestamps, and even that may be absent depending on the field's shape. Don't guess or silently skip the date filter. For each "Review / Demo" item, check content.updatedAt (or run gh issue view <number> / gh pr view <number> on its linked content if that's missing) to decide whether it falls in range. If an item's timestamp truly can't be determined, say so and ask the user whether to include it rather than assuming either way.
For each item, read its details (title, description, labels, linked PRs) to understand the change.
Only include items a user would notice. Ask: "Would a user see or experience this change?"
Include:
Skip:
Categorize each included item as one of:
"New Feature" — something that didn't exist before"Improvements" — an enhancement to something that already existed"Fixes" — a bug fix the user would have noticedGroup aggressively — if multiple items relate to the same feature area, combine them into one entry.
Using the Tone Guide above, write a draft of the release notes and present them to the user for review before making any file changes.
Format the draft as a simple list grouped by type so the user can review and request edits.
Ask the user for:
If not provided, look them up:
git tag --sort=-creatordate | head -1
git log --format="%ad %D" --date=short --tags --simplify-by-decoration | grep "tag:" | head -1Confirm with the user before proceeding.
Read the current file at: frontend/src/pages/home/release-notes/data.js
Add the new release note object at the TOP of the data array (after export const data = [). The first entry shows as the "current" release; everything below goes into a collapsible accordion as past releases.
Format:
{
releaseDate: "YYYY-MM-DD",
version: "X.Y.Z",
changes: [
{
id: "0001",
subject: "Feature name",
type: "New Feature", // or "Improvements" or "Fixes"
description:
"Plain language description of the change."
}
]
}Rules:
"0001" within each releasedescription: on one line, then the string indented on the nextRead frontend/src/pages/home/roadmapData.json — one flat array of {id, title, status} objects. status must be exactly one of these three plain strings (no JS enum to import — this is pure JSON):
"Done""Currently Developing""Not Started Yet"This file feeds two homepage surfaces at once: the "OPS Updates" summary card shown on every tab, and the full roadmap board on the "OPS at a Glance" tab. Editing it here keeps both in sync — this is exactly the kind of drift this file was introduced to prevent, so don't update data.js without also checking this file.
This step has two different kinds of decision in it — don't conflate them:
roadmapData.json is already tracking something as "Currently Developing" and it just shipped, it's done — regardless of whether data.js logged it as a "New Feature", "Improvements", or "Fixes" entry. A roadmap item that finishes via a late bugfix is exactly as done as one that finishes via a shiny new feature; leaving it stuck at "Currently Developing" because of how its last increment happened to be categorized recreates the exact drift this file exists to prevent."New Feature"-level work.What to do, using the "shipping now" and "in progress" items you gathered in Step 3:
data.js entry you added in Step 7 — regardless of its "New Feature" / "Improvements" / "Fixes" type — check whether it matches an existing "Currently Developing" item in roadmapData.json (by meaning, not just exact title string — e.g. "Add Procurement Tracker Award Step" shipping can complete an existing "Finish Procurement Tracker" entry). If it matches, move that existing item to "Done"."Done" for shipped work that wasn't already tracked and is big enough to be worth surfacing on the roadmap — use the same curation judgment as always; don't add one for every routine fix."Not Started Yet" should move to "Currently Developing" now that work has started on them — cross-check against the "In Progress" items from Step 3.id (continue the existing sequence; don't reuse an id still in use elsewhere in the file).Example of a single entry:
{ "id": 16, "title": "View Grant History", "status": "Done" }Keep the array's existing 4-space indentation and formatting exactly — this file is also checked by this repo's prettier and check json pre-commit hooks (see the note on hooks in Step 9), so malformed JSON or inconsistent spacing will fail CI even though this looks like a simple data edit.
Fetch first — a stale local main would cut the new branch from an outdated tree:
git fetch origin main
git checkout -b docs/updated-release-notes-sprint-{number} origin/mainBefore committing, run this repo's mandated frontend pre-commit checks proactively (per the root CLAUDE.md Pre-Commit Workflow) rather than relying only on the hooks to catch problems reactively. Run it as a subshell — the parentheses matter: Claude Code's shell keeps its working directory between separate commands, so a bare cd frontend && ... would leave later repo-root-relative paths (like git add frontend/...) resolving incorrectly.
(cd frontend && bun run format && bun run lint --fix)Then stage and commit, from the repo root:
git add frontend/src/pages/home/release-notes/data.js frontend/src/pages/home/roadmapData.json
git commit -m "docs: updated release notes sprint {number}"
git push origin docs/updated-release-notes-sprint-{number}Don't reach for --no-verify by default. roadmapData.json is plain JSON and gets checked by this repo's check json and prettier hooks specifically to catch exactly the kind of mistake a quick manual edit can introduce (malformed JSON, inconsistent indentation). If a hook fails:
prettier/check json failing almost always means the JSON needs a formatting fix — run (cd frontend && bun run format) and then git add the reformatted files again, from the repo root before re-committing. Re-running the commit without re-staging will just check the same stale, unformatted snapshot and fail again.--no-verify if an unrelated hook (e.g. a flaky secret scanner) is blocking an otherwise-correct commit, and say so when you do.Once the branch is pushed, don't call gh pr create yourself with just a title and short body — that skips the repo's PR template. This repo's create-pr skill populates the full .github/pull_request_template.md (What changed, Issue, How to test, A11y impact, Definition of Done, etc.) for exactly this, but it has disable-model-invocation: true, meaning you can't invoke it on your own — only the user can trigger it, by typing /create-pr. So instead, tell the user the branch is pushed and ask them to run /create-pr themselves to open it with the full template.
Let the user know they can preview locally:
docker compose up frontend --buildThen visit http://localhost:3000
Or wait for the staging deployment after merge.
frontend/src/pages/home/release-notes/data.jsfrontend/src/pages/home/roadmapData.json — plain JSON, shared by the homepage summary card and the "OPS at a Glance" tab. Update both files together so they can't drift apart again.gh CLI to be installed and authenticated with the read:project scope — a user can be fully authenticated and still have Step 3 fail, since default gh auth login scopes don't always include project access. If gh project item-list errors about missing scopes, run gh auth refresh -s read:project (an interactive browser step) rather than re-running gh auth login, which won't fix a scope gap on an already-authenticated account."0001" within each release object in data.js; roadmapData.json's ids are a single flat sequence across the whole file, not per-statusCloses #NNN or Fixes #NNN to PR descriptions — the team's process requires additional review steps before closing storiesHHS/OPRE-OPS
f2b8bfc
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.