Skip to content

Commit 3e1e761

Browse files
committed
ci: group weekly summary highlights and add release range
- Split highlights into 'Features & tests' and 'Fixes & chores' sections via a new per-highlight category field (schema-enforced). - Weight features/tests and larger PRs more heavily; feed the agent each PR's additions/deletions/changedFiles as a size signal. - Link the merged-PR count to the closed-PRs list and add a linked release range (oldest→newest tag) to the context line. - Label each PR reference as 'PR #NNN' instead of '#NNN'.
1 parent abb3051 commit 3e1e761

2 files changed

Lines changed: 63 additions & 19 deletions

File tree

.claude/agents/weekly-summary.md

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ highlight for a Slack summary aimed at the vip development team.
1717

1818
The workflow provides one file (path given in the prompt):
1919
- A JSON file of merged pull requests. Each entry has `number`, `title`,
20-
`author`, `labels`, and `url`, all from the `posit-dev/vip` repository.
20+
`author`, `labels`, `url`, `additions`, `deletions`, and `changedFiles`, all
21+
from the `posit-dev/vip` repository. The `additions`/`deletions`/`changedFiles`
22+
fields give a rough sense of each PR's size.
2123

2224
Read the file before deciding.
2325

@@ -29,34 +31,50 @@ act on it.
2931
## How to Analyze
3032

3133
Pick 5-10 notable changes depending on how eventful the week was — fewer for
32-
quiet weeks, more for busy ones. Prioritize:
33-
- New features users will notice (PR titles like `feat:` / `feat(scope):`)
34-
- Important bug fixes that resolve real problems (`fix:` / `fix(scope):`)
35-
- Breaking changes (a `!` after the type/scope, e.g. `feat!:` or `fix(config)!:`)
36-
- Significant improvements or notable documentation
34+
quiet weeks, more for busy ones.
35+
36+
When judging what is most notable, weight:
37+
- Features and tests more heavily than fixes and chores. New capabilities
38+
(`feat:` / `feat(scope):`) and new or expanded test coverage (`test:`) should
39+
usually win a slot over routine maintenance.
40+
- Larger, more substantial PRs above small ones. Use `additions`, `deletions`,
41+
and `changedFiles` as a rough size signal — a big change is more likely to be
42+
worth highlighting than a one-line tweak.
43+
- Breaking changes (a `!` after the type/scope, e.g. `feat!:` or `fix(config)!:`).
44+
45+
Still surface important bug fixes that resolve real problems (`fix:`) and
46+
significant improvements or notable documentation, but let features and tests
47+
lead.
3748

3849
Skip routine/automated changes (dependabot bumps, minor CI tweaks, release-commit
3950
noise, internal refactors with no user impact).
4051

4152
You can combine multiple related changes into one highlight.
4253

54+
### Categorize each highlight
55+
56+
Put every highlight in one of two groups via the `category` field:
57+
- `"feature"` — features, tests, and notable improvements or documentation
58+
(anything that moves the product forward).
59+
- `"fix"` — bug fixes, chores, refactors, and other maintenance.
60+
4361
## Output Format
4462

4563
Respond with a JSON object (and nothing else) in this exact format:
4664

4765
```json
4866
{
4967
"highlights": [
50-
{"text": "Brief description of notable change 1", "number": 123},
51-
{"text": "A change spanning several PRs", "number": null}
68+
{"text": "Brief description of notable change 1", "number": 123, "category": "feature"},
69+
{"text": "A change spanning several PRs", "number": null, "category": "fix"}
5270
]
5371
}
5472
```
5573

5674
For each highlight, set `number` to the merged PR the highlight came from,
5775
copied exactly from that PR's entry in the input JSON (an integer). If a
5876
highlight summarizes multiple PRs, set `number` to null. Never invent a PR
59-
number.
77+
number. Set `category` to `"feature"` or `"fix"` as described above.
6078

6179
## Guidelines
6280

.github/workflows/weekly-summary.yml

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ jobs:
5555
5656
gh pr list --repo posit-dev/vip --state merged \
5757
--search "merged:$WEEK_START_TS..$WEEK_END_TS" \
58-
--json number,title,author,labels,url \
58+
--json number,title,author,labels,url,additions,deletions,changedFiles \
5959
--limit 300 > all-prs.json
6060
6161
# Exclude fully-automated actors (dependabot version bumps, the
@@ -74,6 +74,21 @@ jobs:
7474
echo "Found $PR_COUNT merged PRs for $WEEK_START..$WEEK_END."
7575
jq -r '.[] | "#\(.number) \(.title)"' weekly-prs.json || true
7676
77+
# Releases published in the same window, as an oldest→newest range
78+
# (e.g. "v0.52.4 to v0.54.6"). Drafts and prereleases are excluded.
79+
gh release list --repo posit-dev/vip \
80+
--json tagName,publishedAt,isDraft,isPrerelease --limit 100 > all-releases.json
81+
RELEASES=$(jq -r --arg start "$WEEK_START_TS" --arg end "$WEEK_END_TS" '
82+
[ .[]
83+
| select(.isDraft == false and .isPrerelease == false)
84+
| select(.publishedAt >= $start and .publishedAt <= $end) ]
85+
| sort_by(.publishedAt)
86+
| if length == 0 then ""
87+
elif length == 1 then .[0].tagName
88+
else "\(.[0].tagName) to \(.[-1].tagName)" end' all-releases.json)
89+
echo "releases=$RELEASES" >> "$GITHUB_OUTPUT"
90+
echo "Releases in window: ${RELEASES:-(none)}"
91+
7792
- name: Configure AWS credentials
7893
if: steps.gather.outputs.has_prs == 'true'
7994
uses: aws-actions/configure-aws-credentials@517a711dbcd0e402f90c77e7e2f81e849156e31d # v6
@@ -103,7 +118,7 @@ jobs:
103118
--model us.anthropic.claude-opus-4-8
104119
--fallback-model us.anthropic.claude-sonnet-4-6
105120
--allowedTools Read
106-
--json-schema '{"type":"object","additionalProperties":false,"required":["highlights"],"properties":{"highlights":{"type":"array","items":{"type":"object","additionalProperties":false,"required":["text","number"],"properties":{"text":{"type":"string"},"number":{"type":["integer","null"]}}}}}}'
121+
--json-schema '{"type":"object","additionalProperties":false,"required":["highlights"],"properties":{"highlights":{"type":"array","items":{"type":"object","additionalProperties":false,"required":["text","number","category"],"properties":{"text":{"type":"string"},"number":{"type":["integer","null"]},"category":{"type":"string","enum":["feature","fix"]}}}}}}'
107122
108123
- name: Build Slack payload
109124
if: steps.gather.outputs.has_prs == 'true'
@@ -113,6 +128,7 @@ jobs:
113128
WEEK_START: ${{ steps.gather.outputs.week_start }}
114129
WEEK_END: ${{ steps.gather.outputs.week_end }}
115130
PR_COUNT: ${{ steps.gather.outputs.pr_count }}
131+
RELEASES: ${{ steps.gather.outputs.releases }}
116132
run: |
117133
set -euo pipefail
118134
# Only claim has_highlights when a highlight with non-empty text
@@ -136,26 +152,36 @@ jobs:
136152
# Escape Slack mrkdwn control chars in Claude-derived text (& first) so a
137153
# crafted PR title echoed into a highlight cannot trigger <!channel>/<@user>
138154
# mentions or fake links; the trusted PR link is appended separately.
155+
# Carry each highlight's category so we can split it into two sections;
156+
# anything not tagged "feature" falls back to the fixes/chores group.
139157
LINKED=$(printf '%s' "$FILTERED" | jq -c --slurpfile prs weekly-prs.json '
140158
map(. as $h
141159
| ($h.text | gsub("&";"&amp;") | gsub("<";"&lt;") | gsub(">";"&gt;")) as $t
142160
| ([$prs[0][] | select(.number == $h.number) | .url] | first) as $u
143-
| if $u == null then $t
144-
else "\($t) (<\($u)|#\($h.number)>)"
145-
end)')
146-
HIGHLIGHTS=$(printf '%s' "$LINKED" | jq 'map("• " + .) | join("\n")')
161+
| { category: (if $h.category == "feature" then "feature" else "fix" end),
162+
line: (if $u == null then $t else "\($t) (<\($u)|PR #\($h.number)>)" end) })')
163+
# Two Slack sections: features/tests first, then fixes/chores.
164+
FEATURES=$(printf '%s' "$LINKED" | jq -r '[.[] | select(.category == "feature") | "• " + .line] | join("\n")')
165+
FIXES=$(printf '%s' "$LINKED" | jq -r '[.[] | select(.category == "fix") | "• " + .line] | join("\n")')
147166
jq -n \
148-
--argjson highlights "$HIGHLIGHTS" \
149167
--arg week_start "$WEEK_START" \
150168
--arg week_end "$WEEK_END" \
151169
--arg pr_count "$PR_COUNT" \
170+
--arg releases "$RELEASES" \
171+
--arg features "$FEATURES" \
172+
--arg fixes "$FIXES" \
152173
'{
153174
text: "vip Weekly Summary",
154-
blocks: [
175+
blocks: ([
155176
{type: "header", text: {type: "plain_text", text: "🧪 vip Weekly Summary", emoji: true}},
156-
{type: "context", elements: [{type: "mrkdwn", text: ("*" + $week_start + "* to *" + $week_end + "* | " + $pr_count + " merged PRs")}]},
157-
{type: "section", text: {type: "mrkdwn", text: ("*Highlights*\n" + $highlights)}}
177+
{type: "context", elements: [{type: "mrkdwn", text: (
178+
"*\($week_start)* to *\($week_end)* | <https://github.com/posit-dev/vip/pulls?q=is%3Apr+is%3Aclosed|\($pr_count) merged PRs>"
179+
+ (if $releases == "" then "" else " | <https://github.com/posit-dev/vip/releases|\($releases)>" end)
180+
)}]}
158181
]
182+
+ (if $features == "" then [] else [{type: "section", text: {type: "mrkdwn", text: ("*🚀 Features &amp; tests*\n" + $features)}}] end)
183+
+ (if $fixes == "" then [] else [{type: "section", text: {type: "mrkdwn", text: ("*🔧 Fixes &amp; chores*\n" + $fixes)}}] end)
184+
)
159185
}' > /tmp/slack-payload.json
160186
echo "Slack payload (posted only on schedule/workflow_dispatch):"
161187
cat /tmp/slack-payload.json

0 commit comments

Comments
 (0)