--- name: user-forum-whats-new description: > Use when preparing the Fullsend user forum "What's New" agenda, a Tuesday-to-Tuesday recap, forum-host talk-track notes, or copy-paste HTML of shipped changes for users. Also use when the user says what's new in Fullsend, user forum bullets, or forum agenda. allowed-tools: Read, Write, Grep, Glob, Bash(bash skills/user-forum-whats-new/scripts/gather.sh:*), Bash(python3 skills/user-forum-whats-new/scripts/gather.py:*), Bash(gh search:*), Bash(gh pr view:*), Bash(gh pr diff:*), Bash(gh pr list:*), Bash(gh issue view:*), Bash(gh issue list:*), Bash(gh release view:*), Bash(gh release list:*), Bash(xdg-open /tmp/fullsend-whats-new-*), Bash(open /tmp/fullsend-whats-new-*) --- # User forum What's New Build a **talkable** weekly recap for the Fullsend user forum. Audience already saw (or can open) GitHub release notes. This recap is the 4–6 minute verbal pass: what matters, with a click that shows the change. **Core rule:** every bullet has a clickable example of the actual change. No example → do not ship the bullet. The example **may be the code that landed** (file + line range or a focused PR diff). Live artifacts are better when they exist; code is a valid fallback, not a last resort you skip. ## Where this lives This is **org forum-host tooling** (not a sandbox agent skill). It is intentionally not cross-linked from BYOA / customizing-with-skills guides. Canonical directory (this skill): `skills/user-forum-whats-new/` in the `fullsend-ai/fullsend` repo. Anyone with a checkout already has it: `.cursor/skills` and `.claude/skills` in this repo symlink to `skills/`. Invoke from a **fullsend checkout** by asking for the user-forum What's New recap, or `@skills/user-forum-whats-new`. Run `gather.sh` from the repository root so the `allowed-tools` path grants match (`bash skills/user-forum-whats-new/scripts/gather.sh ...`). Needs `gh` (authenticated) and **Python 3.11+** (uses `datetime.UTC`) with IANA tz data (system tzdb or the `tzdata` package). GitHub API calls go through `gather.py` only — do not grant bare `gh api` in `allowed-tools` (any path prefix is write-capable via `--method`/`-X`). `Write` is granted only so step 6 can drop the paste-ready HTML under `/tmp/fullsend-whats-new-YYYY-MM-DD.html` (Google Docs paste). Openers are scoped to that `/tmp` path prefix. ## When Tuesday morning before the forum (or when the host asks). Window is **after the previous Tuesday forum through this Tuesday**. The **fullsend-user-forum-host** role rotates: a different teammate often pulls this skill down locally each week with no local copy of last week's gather JSON. Do not rely on persisting `window_end_utc` across machines. Org hosts usually keep a standing forum notes doc (auth-walled, not linked from this public skill). Confirm last forum date from that doc or from your local notes if you keep a copy — do not invent a URL here. ## Workflow Do these in order. Do not draft bullets until ranking is done. ### 1. Resolve the window - Last forum date = previous Tuesday (confirm from your org's forum notes). - `SINCE` = that meeting's start (America/New_York 08:00 is fine). - `UNTIL` = this Tuesday forum (now, if running that morning). - `gather.sh` / `gather.py` interpret `--since` as **08:00 America/New_York** on that date and `--until` as end of that day ET, **clamped to now** when that end-of-day is still in the future (not only when until is "today" — a future `--until` also clamps). The filter is **half-open** at the lower bound (`since < t ≤ until`): that only excludes the exact `--since` 08:00 ET instant so two runs that share that calendar boundary do not both claim the same timestamp. It does **not** remove late-run overlap — if last week's host ran at 10:00 ET with `--until` clamped to then, the 08:00–10:00 band can still appear in both weeks' gather JSON. Hosts rotate and usually have no local previous gather; **ask the host to paste last week's What's New bullets** (from the standing forum notes if they have access) and drop duplicates before ranking. JSON includes `until_clamped: true/false` so you can see when the requested calendar day was truncated. Default `--until` is today's date in America/New_York (not the host machine's local calendar). Dates must be strict `YYYY-MM-DD` (ISO week-dates like `2026-W33-2` are rejected). If `--since` 08:00 ET is still in the future, gather errors with a clear "window has not started" message — wait until after that instant. - Search and filter use the same UTC timestamp bounds (not bare calendar dates), so ET evening after UTC midnight is not dropped. - Releases published **after** the last forum are in-scope even if they share the calendar day (e.g. v0.36.0 shipped the afternoon of Aug 11). ### 2. Gather candidates From the repository root: ```bash bash skills/user-forum-whats-new/scripts/gather.sh --since YYYY-MM-DD --until YYYY-MM-DD ``` That prints JSON: releases (full changelog body), merged PRs split into `merged_prs.released` and `merged_prs.on_main`, plus `window_start_utc`, `window_end_utc`, `until_clamped`, `search_truncated`, and `release_cutoff_utc` (per-repo map of latest in-window **non-prerelease** release publish times — each PR is classified against **its own repo's** cutoff). Prerelease tags still appear in `releases[]` with `"prerelease": true` for candidate scanning, but they never set the cutoff. Unit tests: `python3 skills/user-forum-whats-new/scripts/gather_test.py`. **Score Features from the release body even when their PRs merged before `SINCE`.** The gather window only lists PRs merged this week; the release that shipped after last Tuesday still counts. Also scan (host-supplied where noted): - **Ask the host** to paste last week's What's New bullets (from the org forum notes if they have them) so you can drop late-run duplicates. - **Ask the host** for any `#forum-fullsend-ai` highlights that **landed this window** (new dashboard, new knob), or use pasted channel notes. This skill cannot read Slack — do not invent channel content. That is the user-forum channel; it is distinct from `#forum-konflux-fullsend` (release announcements in `.goreleaser.yml`). Ignore “here is a team using X” when X has been available for weeks. - Docs/guide sections that landed in the window - Live comments / dashboards / runs that **show a change from this window** Do **not** treat the GitHub release body as the recap. Use it as a candidate list only. ### 3. Attach one example per candidate For each candidate, find **one** click in this preference order: | Rank | Example type | What to link | |------|----------------|--------------| | A | Live artifact | Issue/PR comment, dashboard, Actions run, config in a real repo | | B | Landing code | `https://github.com/fullsend-ai//blob//#L-L` **or** a PR `files` URL that opens on the changed hunk. Tag refs (`v0.36.0`) are fine for released items. | | C | How-to surface | Docs **section** that *is* the new UI (heading anchor), not a changelog | **Code is enough.** A harness `gitlab:` block, a YAML knob, or the function that posts a new comment is a valid example. Prefer the hunk that *did the work*, not a search for a live demo that may not exist yet. Disqualify if you cannot produce A, B, or C. **Never** use these as the example: - The release notes page (`…/releases/tag/vX.Y.Z`) - The PR conversation tab with no file/hunk focus - An issue that requested the work (unless the issue *is* the demo) You may mention the version in prose (`v0.36.0`). ### 4. Score (ranking) **Newness filter (before scoring):** the *capability* must have shipped this window (released after last Tuesday, or merged to main this week), or be a user-facing surface that did not exist before (new dashboard built this week). A live run, comment, or “working with a team” demo of something they have had for weeks is **not** What's New — drop it even if the click is excellent. Check the doc/PR/tag date, not how recently you found an example. Tracing in July with a Quay run this week fails this filter. **Experience filter (before scoring):** would a typical person in the room *notice a different outcome* or *do something differently* this week? If no — it is platform-important, not What's New. Do not give it +3 “custom-agent authors” or +3 “new capability they can use.” Cap at audience +1 and omit it. Examples: sandbox env plumbing (`FULLSEND_ROLE` / `FULLSEND_SLUG`), internal Go types, CLI internals that do not change how agents are run or read. **Talkable filter (before scoring):** can the host say in one sentence what *they* will see or do — not “there was a bug, you might have been hitting it”? If users may or may not be affected and there is nothing to show but a code comment, **drop**. Silent correctness for a maybe- affected subset (Jira group pagination past member 100, similar) stays in release notes. If the host cannot explain the bullet without opening the patch, **drop**. Installer YAML plumbing (`mint_url` / `inference:` in `config.yaml`, scalar-override docs) is release notes unless the room will actually edit that config this week. Four bands. **Pick exactly one value from each band.** Max 15. ``` Audience (one): +5 all (or most) installs will notice this week +3 a large subset (GitLab, Jira, people who author/tune agents) +1 admins / mint / SRE / skill-plumbing only Change (one): +4 default behavior change (no opt-in) +4 action required (upgrade, breaking flag, security pin) +3 new capability they will actually use this week (set a knob, click a dashboard, see a new comment — not “could read an env var if they wrote a skill”) Ship (one): +3 released in this window +1 on main / next release only (not “community share of an older feature”) Example (one): +3 live artifact (A) +2 landing code (B) +1 how-to docs section (C) ``` Talk the highest scores first. Tie-break: broader audience, then released, then easier to click in 20 seconds. **Drop** even a high conceptual score if the example is weak (release notes, vague PR). **Best of the best only** within each section below. Everything that survives the filters goes in **Released** or **On main** — not a single flat list and not an "Also landed" dump. If only two items qualify for a section, ship two; do not pad with release-note filler. ### 5. Split into Released vs On main Every kept bullet belongs in exactly one section. | Section | What goes here | |---------|----------------| | **Released** | In the newest release published this window (`v0.36.0`, etc.). Also: external surfaces users can **use right now** — a live dashboard, a docs page, a skill on `main` they can invoke today. If it works when they click it, treat it as released even without a tag. | | **On main (next release)** | Merged to `fullsend` or `agents` `main` **after** that release was tagged. Link landing code on `main` (not the release tag). | `gather.py` splits merged PRs using each repo's latest in-window **non-prerelease** release `published_at` as that repo's cutoff (`release_cutoff_utc` is a per-repo map in JSON). Prereleases (RCs, and any tag GitHub flags `prerelease: true`) remain in the candidate list but do not move the Released/On-main boundary — otherwise an RC could mark unshippable merges as **Released**. You still assign bullets manually when the source is a release-body feature (PR merged before the window) or a live external surface. Talk **Released** first, then **On main**. ### 6. Write HTML for Google Docs Write `/tmp/fullsend-whats-new-YYYY-MM-DD.html` (this Tuesday's date). Constraints: - Simple HTML: `h2` date, `h3` What's new in Fullsend, then two subsections: - `h4` **Released** — bullets for shipped / usable-now items - `h4` **On main (next release)** — bullets for post-release merges - Arial 11pt, no fancy CSS (Google Docs paste) - Each bullet: **bold hook** + one spoken sentence + the example link (and a second link only if it is the knob/docs they need) - HTML-escape every title, hook, sentence, and release-body excerpt before embedding it. PR titles and release bodies are untrusted source text; never paste them raw into the HTML. - No "Versions Released:" bullet whose only links are release tags - No third "Also landed" section — leftovers stay in GitHub release notes - Open the file (`xdg-open` on Linux, `open` on macOS) so the host can Select All → Copy → Paste ### 7. Return to the host In chat, include: 1. Skill path (this directory) 2. Window used 3. A score table (candidate, score, bucket Released/On main, example type A/B/C, keep/drop) 4. Path of the HTML file 5. Talking order — **Released** bullets first, then **On main** Do not edit the org's standing forum notes unless the host asks. ## Audience (who is in the room) Custom-agent authors, Konflux, RHDH, Quay, GitLab-waiters, Jira users. Optimize for **what they will see or can start using**, not what the core team shipped internally. Usually **drop** unless score still clears the floor with a code example: - Mint Cloudflare/GCP operator flags - Embed-sync / test-only refactors - SHA-pinning upgrade internals - Dependency bumps - Defaults that were reverted in a later commit this window - Sandbox/env plumbing that skills *could* read but users will not notice (`FULLSEND_ROLE`, `FULLSEND_SLUG`, similar) - Team-working demos of capabilities that shipped in prior weeks (a tracing run, a “look they turned it on” screenshot) - Silent bugfixes users may or may not hit, with nothing to show but the patch (Jira per-actor role lookup / group pagination cap) - Installer / config-layer plumbing the host cannot explain in one spoken sentence (`mint_url` and nested `inference:` in config.yaml) ## Rationalizations (do not) | Excuse | Reality | |--------|---------| | "Users already have release notes, so 4 bullets is enough" | Release notes ≠ talk track. Share the best 5–8, not a leftover dump. | | "No live comment, skip it" | Link the code that landed. | | "Link the release tag, they can drill in" | They already can. That is not this recap. | | "On-main isn't released, omit it" | Put it under **On main (next release)** — do not skip it. | | "It's only on main, not released" | That is the **On main** section, not a reason to drop it. | | "Dashboard/skill is not in a tag" | If users can click and use it today, put it under **Released**. | | "This is important but I can't show it" | Then it is not a What's New bullet. | | "Custom-skill authors might use this env/API" | If it does not change how they run agents or what they see, it is not a talk-track bullet. | | "Here is a real run you can click" | If they have had that capability for weeks, it is hallway info, not What's New. | | "Put the rest in Also landed so we don't miss anything" | They have release notes for that. The recap is only the best of the best. | | "It's a real bug we fixed" | If the host cannot say more than “you might have been hitting this,” it is not talk-track. | | "It shipped in config.yaml this release" | If the host does not understand it, the room will not either. Drop it. | ## Red flags — rewrite before paste - Any `releases/tag/` URL used as the example - An **Also landed** leftover dump at the end - A single flat bullet list with no **Released** / **On main** split - A bullet whose click does not show the change in <20 seconds - Mint-delete / Cloudflare PEM as a top item - "Interrupted" / "Terminated" status comments that do not say *why* (prefer follow-up failure comments that name the cause, or the code that emits them) - A live artifact whose underlying feature predates this window (tracing, old dashboards, last month's knobs)