|
| 1 | +--- |
| 2 | +description: Friday release ritual — create git tag, GitHub release (auto-generated changelog), and a customer-facing email draft (Cal.com style) |
| 3 | +allowed-tools: Bash, Write, Read, Skill |
| 4 | +--- |
| 5 | + |
| 6 | +You are running the Friday release ritual for TryPost. Three artifacts are produced: |
| 7 | + |
| 8 | +1. A git tag (semver) |
| 9 | +2. A GitHub release with the **auto-generated** changelog (PR list + authors via GitHub's native generator — flat, technical, for developers) |
| 10 | +3. A **customer-facing email draft** in Cal.com style (themed prose, end-user voice, no commit/PR references) |
| 11 | + |
| 12 | +Plus local mirrors in `releases/<version>/`. |
| 13 | + |
| 14 | +**Always confirm with the user before any push/tag/release.** |
| 15 | + |
| 16 | +## Context (auto-loaded) |
| 17 | + |
| 18 | +- Current branch: !`git branch --show-current` |
| 19 | +- Working tree: !`git status --porcelain` |
| 20 | +- Latest tag: !`git describe --tags --abbrev=0 2>/dev/null || echo "(none)"` |
| 21 | +- Repo (owner/name): !`gh repo view --json nameWithOwner -q .nameWithOwner 2>/dev/null || echo "(no gh)"` |
| 22 | +- Local vs origin/main: !`git fetch --quiet origin main 2>/dev/null; git rev-list --left-right --count HEAD...origin/main 2>/dev/null || echo "0 0"` |
| 23 | +- Commits since latest tag (or all if no tag): !`LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null); if [ -z "$LAST_TAG" ]; then git log --pretty=format:"%H%x09%s" --reverse; else git log "$LAST_TAG"..HEAD --pretty=format:"%H%x09%s" --reverse; fi` |
| 24 | + |
| 25 | +## Workflow |
| 26 | + |
| 27 | +### Step 1 — Pre-flight checks |
| 28 | + |
| 29 | +Stop and tell the user if any of these fail: |
| 30 | + |
| 31 | +- Current branch must be `main`. Else: ask user to `git checkout main`. |
| 32 | +- Working tree must be clean. Else: ask user to commit/stash. |
| 33 | +- Local in sync with `origin/main` (rev-list count `0 0`). Else: ask user to pull/push. |
| 34 | +- Commits-since-tag list must be non-empty. Else: "Nada novo desde a última tag." |
| 35 | + |
| 36 | +### Step 2 — Determine next version |
| 37 | + |
| 38 | +TryPost uses **sequential numbering with rollover at 9** — not standard semver. Do not parse conventional commits to choose the bump. Every release is the next sequential number, whatever the commits look like. |
| 39 | + |
| 40 | +1. If no previous tag exists → next version = **`v1.0.0`** (first release ever). |
| 41 | +2. Otherwise, parse the latest tag as `vMAJOR.MINOR.PATCH` and increment by these rules: |
| 42 | + - `patch += 1` |
| 43 | + - If `patch` reaches `10`: set `patch = 0`, `minor += 1` |
| 44 | + - If `minor` reaches `10`: set `minor = 0`, `major += 1` |
| 45 | +3. Re-prefix with `v`. |
| 46 | + |
| 47 | +Examples: |
| 48 | + |
| 49 | +| From | To | |
| 50 | +|---|---| |
| 51 | +| (no tag) | v1.0.0 | |
| 52 | +| v1.0.0 | v1.0.1 | |
| 53 | +| v1.0.8 | v1.0.9 | |
| 54 | +| v1.0.9 | v1.1.0 | |
| 55 | +| v1.5.7 | v1.5.8 | |
| 56 | +| v1.9.8 | v1.9.9 | |
| 57 | +| v1.9.9 | v2.0.0 | |
| 58 | + |
| 59 | +There is no manual override — the next version is whatever the rule above produces. If a release needs a different version for some special reason, the user must create the tag manually outside this command. |
| 60 | + |
| 61 | +### Step 3 — Preview the changelog (GitHub native format) |
| 62 | + |
| 63 | +Use GitHub's release-notes generator API to produce the changelog **without creating anything yet**: |
| 64 | + |
| 65 | +```bash |
| 66 | +gh api -X POST "repos/{OWNER}/{REPO}/releases/generate-notes" \ |
| 67 | + -f tag_name="<new_version>" \ |
| 68 | + -f target_commitish="main" \ |
| 69 | + -f previous_tag_name="<latest_tag>" \ |
| 70 | + --jq '.body' |
| 71 | +``` |
| 72 | + |
| 73 | +For the first release ever (no previous tag), omit the `previous_tag_name` flag — GitHub falls back to the initial commit. |
| 74 | + |
| 75 | +The body already contains: |
| 76 | +- `<subject> by @<author> in #<PR>` lines |
| 77 | +- "New Contributors" section when applicable |
| 78 | +- `Full Changelog: ...` compare link |
| 79 | + |
| 80 | +**Do not modify it.** The GitHub-native format is the goal. |
| 81 | + |
| 82 | +### Step 4 — Draft the customer email (Cal.com style) |
| 83 | + |
| 84 | +This email is for **end users of TryPost** — non-developers, paying customers, trial users. It must **NOT** reference: commits, PRs, authors, SHAs, conventional commit scopes, version control concepts, internal class names, file paths. |
| 85 | + |
| 86 | +Read the commits only as **internal source material**. Translate to user-facing language. |
| 87 | + |
| 88 | +#### Structure |
| 89 | + |
| 90 | +```markdown |
| 91 | +--- |
| 92 | +subject: "Changelog: TryPost <version> — <theme 1>, <theme 2>, <theme 3>..." |
| 93 | +--- |
| 94 | + |
| 95 | +# Changelog: TryPost <version> — <theme 1>, <theme 2>, <theme 3>... |
| 96 | + |
| 97 | +By Paulo Castellano • Release <version> |
| 98 | + |
| 99 | +Hello! Welcome to this week's update. Here's what's new in TryPost. |
| 100 | + |
| 101 | +## <Theme 1> |
| 102 | + |
| 103 | +<2-4 sentences of concrete narrative — what changed, why a user should care, what they'll notice. No marketing puffery.> |
| 104 | + |
| 105 | +## <Theme 2> |
| 106 | + |
| 107 | +<same> |
| 108 | + |
| 109 | +## <Theme 3 — only if there are genuinely 3 themes worth of work> |
| 110 | + |
| 111 | +<same> |
| 112 | + |
| 113 | +## New features |
| 114 | + |
| 115 | +- <user-facing one-liner — what they can now do> |
| 116 | +- <...> |
| 117 | + |
| 118 | +## Fixes |
| 119 | + |
| 120 | +- <user-facing one-liner — what no longer breaks> |
| 121 | +- <...> |
| 122 | + |
| 123 | +Cheers, |
| 124 | +Paulo Castellano from TryPost.it |
| 125 | +``` |
| 126 | + |
| 127 | +#### Theme grouping (AI clusters by user impact) |
| 128 | + |
| 129 | +Read all commits since the last tag and cluster into **2-3 user-facing themes**. Use whatever frame makes the changes feel coherent to a customer, not to a developer. |
| 130 | + |
| 131 | +**Good themes** (end-user framing): |
| 132 | +- "Trial protection" — bundles billing/Stripe Radar work |
| 133 | +- "Reliable Facebook posting" — bundles Facebook fixes |
| 134 | +- "Faster scheduling" — bundles queue/post improvements |
| 135 | +- "Better post editor" — bundles UI changes to the post composer |
| 136 | + |
| 137 | +**Bad themes** (internal framing — never use these): |
| 138 | +- "Refactoring" |
| 139 | +- "Dependency updates" |
| 140 | +- "Feature commits" / "Fix commits" |
| 141 | +- "Backend improvements" |
| 142 | + |
| 143 | +If there are fewer than 3 themeable groups, use 2 or just 1. Don't pad. Internal-only changes (chore, CI, refactor, deps) usually shouldn't appear at all — fold the user-visible ones into "Fixes" with a user-voice rewrite, drop the rest. |
| 144 | + |
| 145 | +#### Bullet rules for "New features" / "Fixes" |
| 146 | + |
| 147 | +Rewrite each item in **user voice**, not commit voice: |
| 148 | + |
| 149 | +- ❌ "fix(facebook): send Graph API requests as form-urlencoded" |
| 150 | +- ✅ "Fixed an issue where multi-image Facebook posts could fail to publish" |
| 151 | + |
| 152 | +- ❌ "feat(billing): charge one-time trial setup fee at Stripe Checkout" |
| 153 | +- ✅ (Probably its own theme, not a bullet — billing is a big user-facing topic) |
| 154 | + |
| 155 | +- ❌ "chore(deps): bump axios to 1.13.5" |
| 156 | +- ✅ (Skip entirely — pure internal) |
| 157 | + |
| 158 | +If a commit has no user-visible effect, **omit it**. Don't pad the email. |
| 159 | + |
| 160 | +#### Subject line |
| 161 | + |
| 162 | +Pattern: `Changelog: TryPost <version> — <theme 1>, <theme 2>, <theme 3>...` |
| 163 | + |
| 164 | +Cap around 80 chars. If themes don't fit, shorten to the 2 most impactful + "and more...". |
| 165 | + |
| 166 | +### Step 5 — Humanize the email prose |
| 167 | + |
| 168 | +Run the email body through the `humanizer` skill before previewing: |
| 169 | + |
| 170 | +1. Invoke the `Skill` tool with `skill: humanizer` and pass the draft email body plus this context: *"This is a customer-facing changelog email for TryPost (social media scheduler SaaS). Tone: developer founder writing to early users on a Friday — warm, specific, no marketing puffery. Cal.com style. Keep the existing structure (subject frontmatter, section headers, bullets, signature). Do not strip section headers or the 'Cheers, Paulo Castellano from TryPost.it' signature."* |
| 171 | +2. Replace the draft email body with the humanized version. |
| 172 | + |
| 173 | +**Do NOT humanize:** |
| 174 | +- The changelog from Step 3 (flat commit list, no prose). |
| 175 | +- The subject line frontmatter. |
| 176 | +- The literal signature `Cheers,\nPaulo Castellano from TryPost.it` — keep it exact. |
| 177 | + |
| 178 | +The humanizer skill itself covers all patterns. Trust it. |
| 179 | + |
| 180 | +### Step 6 — Confirm with the user |
| 181 | + |
| 182 | +Show: |
| 183 | +1. **Proposed version** (e.g., `v1.0.9 → v1.1.0` — sequential rollover at 9). |
| 184 | +2. **Changelog preview** (Step 3 output). |
| 185 | +3. **Email preview**: subject line + full body (post-humanizer). |
| 186 | +4. **Files that will be created/pushed**: |
| 187 | + - Tag `<version>` (pushed to origin) |
| 188 | + - GitHub release `<version>` |
| 189 | + - `releases/<version>/changelog.md` |
| 190 | + - `releases/<version>/email.md` |
| 191 | + |
| 192 | +Then ask in Portuguese: **"Crio a tag, publico o release e salvo os arquivos?"** |
| 193 | + |
| 194 | +Do **not** proceed without explicit yes. |
| 195 | + |
| 196 | +### Step 7 — Execute |
| 197 | + |
| 198 | +After confirmation, in this exact order: |
| 199 | + |
| 200 | +1. Create local directory: `mkdir -p releases/<version>` |
| 201 | +2. Write `releases/<version>/changelog.md` with the Step 3 content (raw GitHub markdown). |
| 202 | +3. Write `releases/<version>/email.md` with frontmatter + humanized body. |
| 203 | +4. Create annotated tag: `git tag -a <version> -m "Release <version>"` |
| 204 | +5. Push tag: `git push origin <version>` |
| 205 | +6. Create the GitHub release using the changelog file as body: |
| 206 | + ```bash |
| 207 | + gh release create <version> --title "<version>" --notes-file releases/<version>/changelog.md |
| 208 | + ``` |
| 209 | +7. Report to the user: |
| 210 | + - GitHub release URL (from `gh` output) |
| 211 | + - Local paths: `releases/<version>/changelog.md`, `releases/<version>/email.md` |
| 212 | + - Reminder: *"Os arquivos em `releases/<version>/` não foram commitados. Commit depois se quiser preservar o histórico no repo."* |
| 213 | + |
| 214 | +### On failure |
| 215 | + |
| 216 | +- `git push origin <version>` fails: report the exact error, leave the local tag in place, do not retry destructively. |
| 217 | +- `gh release create` fails: the tag is already pushed; tell the user they can recreate manually with `gh release create <version> --title "<version>" --notes-file releases/<version>/changelog.md`. |
| 218 | +- `Skill` or `Write` failure during artifact prep: report and stop. Do not push the tag without the artifacts being prepared. |
0 commit comments