Repository navigation
PM-46025: Platform RAG standup format + weekly-recap skill - #306
addisonbeck wants to merge 8 commits into
Conversation
Platform's standup format is RAG-first: a status color and one-sentence
bottom line, then Yesterday/Today/Blockers (Last week/This week/Blockers
for weekly recaps). Restructure the daily-recap template to match.
- Add `data-rag` (green/amber/red) driving a CSS status dot and
left-border color, not emoji, so rendering doesn't depend on emoji
support
- Add a one-sentence BLUF line
- Replace the single shipped list with three sections: past / next /
blockers, using placeholder labels and a `{{STANDUP_PERIOD}}` header
so a weekly recap can reuse the same template
- Rename `{{STANDUP_SHIPPED}}` to `{{STANDUP_PAST}}`
- Update render-guide.md to document the new placeholders
The `<script>` block and its CSP hash are intentionally unchanged here.
The Copy output still produces the old shape and will move to the RAG
format in a follow-up commit; until then Copy omits the Blockers
section.
Refs: PM-46025
Copy built its text from `li.textContent`, which dropped every link and left out the Blockers section added in 09f49fd. Platform's standup is posted to Slack, so the copied text has to match Platform's RAG template. - Copy now produces: a Slack RAG shortcode (`:large_green_circle:` / `:large_orange_circle:` / `:red_circle:` from `data-rag`), then the BLUF line, a blank line, and one section per standup column. Each section has a backtick label taken from the column's rendered `<h3>`, so weekly recaps get "Last week"/"This week" with no code change. - Bullets keep their links as markdown: `<a>` becomes `[text](href)`, with parentheses in the href percent-encoded, and `<code>` becomes backticks. An empty section prints `- None`. - The CSP `sha256` hash was recomputed for the new script. - A test now checks that the CSP hash matches the `<script>` block, so a stale hash fails the test suite instead of silently disabling filters and Copy. - Verified: `daily-recap.test.sh` passes (254 passed, 0 failed). Headless Chrome shows the script runs under the new hash, is blocked when the hash is altered, and Copy produces the expected text byte for byte. Refs: PM-46025
…up rules 09f49fd and 204333d switched the standup card to the Platform RAG format and made Copy emit linked text, but nothing told the skill how to fill the new placeholders. Document that here: - The RAG color is derived from in-window evidence only (ADR-097): returned issues, Blocked transitions, PRs with changes requested, and open or deferred streams. Any blocker means at least amber; mixed signals also mean amber. - The BLUF is one HTML-escaped sentence. - What belongs in the past, next, and blockers sections. - Bullets are outcome-first, with each Jira key or PR linked on the noun it refers to. - Stream cards and the timeline keep the `#1234` + title link pattern, unchanged from the standup card's rules. Refs: PM-46025
daily-recap already handled weeks through a per-day loop, and
Platform's standup format (approved for both daily and weekly) needs
"Last week"/"This week" labels. Rather than a standalone weekly skill
with copied scripts, weekly-recap is a thin SKILL.md that defines only
what differs for a week and reuses daily-recap's scripts, template,
and references through `${CLAUDE_PLUGIN_ROOT}`. Moving the weekly
trigger phrases to weekly-recap means only one skill owns weekly
requests.
- New `weekly-recap` skill: default window is the most recently
completed Mon-Fri week; "this week" is Monday through today; day
boundaries use daily-recap's cutoff hour. Per-day runs of
gather-gh-events, find-sessions, and gather-atlassian; one ranged
gather-gh-search call. Labels Last week/This week,
`{{STANDUP_PERIOD}}` `Week · {range}`, page file
`recap-week-YYYY-MM-DD.html`, title text "Weekly Recap", Today
banner skipped, a day divider per day in the timeline.
- daily-recap: drop "this week" from the description; weeks now point
to weekly-recap, other multi-day ranges keep the per-day loop.
- render-guide: document the weekly `{{STANDUP_PERIOD}}` form.
- No scripts or template copied; `skills/weekly-recap/` holds only
SKILL.md.
Refs: PM-46025
PM-46025: bump bitwarden-retrospective-tools from 1.1.0 to 1.2.0 in plugin.json, marketplace.json, and the README catalog table. This is the first of four child TODOs splitting out a previously oversized "bump to 1.2.0 with CHANGELOG and README" task. CHANGELOG entries, plugin description text, and README skill-list/cspell fixes are deliberately deferred to three sibling TODOs and are not part of this commit.
Document the weekly-recap skill, the Platform RAG standup card format, and the standup bullet rules as a new 1.2.0 release section in CHANGELOG.md, completing the Keep-a-Changelog record for changes already landed on this branch. Refs: PM-46025
plugin.json's description still described a daily-only recap from before the weekly-recap skill existed. The plugin README's skill table never got a weekly-recap row when that skill was added (0a03d78). The daily-recap README also lacked a note about the standup card's Copy button now preserving markdown links and code instead of stripping them (204333d). Closes out doc-sync gaps flagged by two prior sessions in this hub chain. This is a pre-decomposed child of the larger 1.2.0 bump TODO; sibling commits (version bump, CHANGELOG) already landed on this branch. Refs: PM-46025
… drift The plugin's cspell dictionary was missing "BLUF", introduced earlier in this chain (204333d/09f49fd's RAG standup wording) and flagged by `pnpm run lint:spelling`. The retrospective-tools README's skill table had also drifted out of Prettier's table-column formatting after the weekly-recap row was added (0a03d78), tripping `lint:prettier`. This is the fourth and final child of the 4-way split of the 1.2.0 bump/CHANGELOG/doc-sync/lint TODO chain on this branch. With this commit, `pnpm run lint` exits clean, unblocking the PR-open TODO. Refs: PM-46025
Claude Code validationResult: Pass Validated the Not covered: The template's CSP Findings
Dropped after documentation check: Three findings — raised independently by the plugin validator and both skill reviewers, one of them at CRITICAL — claimed that |
| } else if (child.tagName === "A") { | ||
| const href = child | ||
| .getAttribute("href") | ||
| .replace(/\(/g, "%28") | ||
| .replace(/\)/g, "%29"); | ||
| out += `[${collapse(child.textContent)}](${href})`; |
There was a problem hiding this comment.
getAttribute("href") is dereferenced without a null guard, so one <a> with no href throws and silently kills the whole Copy button.
Details and fix
child.getAttribute("href") returns null for an anchor that carries no href attribute, and .replace() on null throws a TypeError. The throw happens inside the click listener before flashCopyState runs, so the button keeps reading "Copy", nothing reaches the clipboard, and the user gets no error — the exact silent-failure mode this PR set out to fix in the old li.textContent path.
The render guide does tell the generating model to render a Jira key with a null url as plain text (references/render-guide.md:104), but that is prose guidance to an LLM, not a guarantee about the DOM. One malformed anchor takes out Copy for the entire page.
Falling through to the existing else branch degrades correctly: the anchor's text still appears in the output, just unlinked.
| } else if (child.tagName === "A") { | |
| const href = child | |
| .getAttribute("href") | |
| .replace(/\(/g, "%28") | |
| .replace(/\)/g, "%29"); | |
| out += `[${collapse(child.textContent)}](${href})`; | |
| } else if (child.tagName === "A" && child.hasAttribute("href")) { | |
| const href = child | |
| .getAttribute("href") | |
| .replace(/\(/g, "%28") | |
| .replace(/\)/g, "%29"); | |
| out += `[${collapse(child.textContent)}](${href})`; |
Because this edits the inline <script> block, the CSP sha256- hash in the <meta> tag at line 14 has to be regenerated alongside it — the new test case [16] added in scripts/daily-recap.test.sh will fail otherwise.
validate-ai
| "source": "./plugins/bitwarden-retrospective-tools", | ||
| "version": "1.1.0", | ||
| "version": "1.2.0", | ||
| "description": "Tools for looking back on your own engineering work: a daily work recap built from your Claude Code session logs, your GitHub activity, and your Jira and Confluence activity." |
There was a problem hiding this comment.
🎨 SUGGESTED: The marketplace description still advertises a daily-only plugin after plugin.json's was rewritten to cover weekly recaps and the RAG format.
Details and fix
This PR changed plugins/bitwarden-retrospective-tools/.claude-plugin/plugin.json:4 to "a daily or weekly work recap, in Platform RAG standup format, built from…" and shipped the weekly-recap skill, but left this entry at the 1.1.0 text. The marketplace entry is what users read when browsing the catalog, so it now undersells what 1.2.0 contains.
For accuracy: byte-identical descriptions are the majority convention here but not a hard rule — 17 of 23 entries match their manifest exactly and 5 others already diverge for unrelated reasons. The point is the staleness relative to this changeset, not the mismatch per se.
| "description": "Tools for looking back on your own engineering work: a daily work recap built from your Claude Code session logs, your GitHub activity, and your Jira and Confluence activity." | |
| "description": "Tools for looking back on your own engineering work: a daily or weekly work recap, in Platform RAG standup format, built from your Claude Code session logs, your GitHub activity, and your Jira and Confluence activity." |
validate-ai
| context: fork | ||
| agent: general-purpose | ||
| description: Generates an interactive HTML recap of the user's own engineering work over the time window given as arguments, ready for standup, 1:1 prep, or sharing with colleagues. Use when the user asks what they worked on, shipped, merged, pushed, reviewed, or moved tickets and docs on over a time window ("yesterday", "this morning", "last night", "since standup", "this week", a specific date), or asks for a "recap", "daily summary", "standup notes", "morning recap", or "1:1 prep", including phrasings like "throw together what I did yesterday" or "something I can show my manager". Not for looking up a single specific PR, commit, ticket, or document, for someone else's activity, for meeting recaps, for repository-level overviews, or for critiquing how a single Claude Code session went. | ||
| description: Generates an interactive HTML recap of the user's own engineering work over the time window given as arguments, ready for standup, 1:1 prep, or sharing with colleagues. Use when the user asks what they worked on, shipped, merged, pushed, reviewed, or moved tickets and docs on over a time window ("yesterday", "this morning", "last night", "since standup", a specific date), or asks for a "recap", "daily summary", "standup notes", "morning recap", or "1:1 prep", including phrasings like "throw together what I did yesterday" or "something I can show my manager". Not for looking up a single specific PR, commit, ticket, or document, for someone else's activity, for meeting recaps, for repository-level overviews, or for critiquing how a single Claude Code session went. |
There was a problem hiding this comment.
🎨 SUGGESTED: Dropping "this week" left no multi-day trigger in either skill's description, while the body still documents a multi-day procedure.
Details and fix
Every time cue remaining in this description is single-day: "yesterday", "this morning", "last night", "since standup", a specific date. The new weekly-recap description claims only Monday-to-Friday weeks and explicitly excludes anything shorter.
But line 28 of this file still carries the Another multi-day range: branch with full per-day collection instructions. So "recap the last three days", "what did I ship since Monday", or "this sprint" now match neither description, and the procedure at line 28 has nothing routing to it.
Add one non-week range cue to the trigger list, leaving full weeks to weekly-recap:
..."since standup", a specific date, or a date range such as "the last three days"), or asks for a...
Reference: https://code.claude.com/docs/en/skills
validate-ai
| | Skill | What It Does | | ||
| | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| | `daily-recap` | Builds an interactive HTML recap of one workday from your Claude Code sessions and GitHub events, with a standup card, themed work streams, and a filterable GitHub timeline. See [its README](skills/daily-recap/README.md). | | ||
| | `weekly-recap` | A thin skill that reuses daily-recap's scripts, template, and references to build the same recap over a Monday-to-Friday workweek instead of a single day. | |
There was a problem hiding this comment.
🎨 SUGGESTED: The new weekly-recap row has no "See its README" link, unlike every other skill row in this plugin.
Details and fix
daily-recap on line 15 links to skills/daily-recap/README.md, which documents its environment variables (DAILY_RECAP_CUTOFF_HOUR, DAILY_RECAP_OUTPUT_DIR, the Atlassian tokens), where output lands, and its prerequisites. weekly-recap ships with no such file, so a user who wants to know which of those settings apply to a weekly run, or where recap-week-*.html is written, has to read the agent-facing SKILL.md.
The skill is deliberately thin, so a short README that states what it inherits from daily-recap and what differs would be enough — no need to duplicate the daily-recap content.
Add plugins/bitwarden-retrospective-tools/skills/weekly-recap/README.md and link it from this row:
| `weekly-recap` | A thin skill that reuses daily-recap's scripts, template, and references to build the same recap over a Monday-to-Friday workweek instead of a single day. See [its README](skills/weekly-recap/README.md). |
validate-ai
Summary
Restructures the
bitwarden-retrospective-toolsstandup card (and its Copybutton) into Platform's RAG (red/amber/green) format, documents the RAG
heuristic and bullet-linking rules, and adds a thin
weekly-recapskill thatreuses
daily-recap's scripts and template instead of duplicating them.Bumps the plugin from 1.1.0 to 1.2.0 with a matching CHANGELOG entry.
Jira: https://bitwarden.atlassian.net/browse/PM-46025 (PM-46025)
Why
PR #295 (merged) added Jira/Confluence/GitHub-search collection to
daily-recapat 1.1.0. The originally planned follow-up — a standaloneweekly-standupskill with its own RAG thresholds — was superseded afterPatrick approved adopting Platform's RAG standup format and the AI team
flagged that a weekly skill should reuse
daily-recap's machinery ratherthan duplicate it. This PR carries both changes together: the RAG
format/linking rework and the weekly-recap reuse.
What changed
data-ragstatus dot (green/amber/red, driven by CSS, not emoji), aone-sentence BLUF line, and three labeled sections — Yesterday/Today/
Blockers for daily recaps, Last week/This week/Blockers for weekly
recaps — behind a shared
{{STANDUP_PERIOD}}header.{{STANDUP_SHIPPED}}is renamed to{{STANDUP_PAST}}.reads
li.textContent(which silently dropped every link and theBlockers section). It now emits a Slack RAG shortcode
(
:large_green_circle:/:large_orange_circle:/:red_circle:)derived from
data-rag, the BLUF line, and one backtick-labeled sectionper column, with bullets converted to markdown links
(
<a>→[text](href), href parens percent-encoded) and<code>→backticks. Empty sections print
- None. The CSP script hash wasrecomputed in the same commit, and a new test asserts the hash matches
the
<script>block so a stale hash fails CI instead of silentlydisabling Copy.
derived from in-window evidence only (returned issues, Blocked
transitions, PRs with changes requested, open/deferred streams); any
blocker means at least amber, and mixed signals mean amber over green.
Bullets are outcome-first, with the Jira key or PR linked on the noun it
refers to. Stream cards and the timeline keep their existing
#1234+title link pattern, unchanged.
weekly-recapskill: a 48-lineSKILL.mdthat defines onlywhat differs for a week (default window: most recently completed
Mon–Fri; "this week" = Monday through today) and reuses
daily-recap's scripts, template, and reference docs via${CLAUDE_PLUGIN_ROOT}— no files are copied.daily-recapdrops its"this week" trigger phrase so only
weekly-recapowns weekly requests.plugin.json,marketplace.json,and the plugin README catalog table, with a CHANGELOG entry covering
the weekly-recap skill, the RAG standup card, RAG Copy with links, and
the bullet-rule docs. Plugin docs (description, README skill table,
daily-recap README) were synced to match, and a cspell/Prettier drift
introduced along the way was fixed.
pnpm run lintis green on thisbranch.
Design trade-off: RAG from in-window evidence only
The RAG color has no current-state snapshot — it is derived entirely from
activity that happened inside the recap window (see ADR-097, which
resolves ADR-096's earlier deferred "weekly-standup current-state
snapshot" idea as not built). This is a known, accepted trade-off: work
that is stuck but has no in-window activity (no returned issue, no status
transition, no PR update) can be missed by the RAG signal. A prior
current-state-snapshot attempt was removed after repeated bugs rooted in a
time-semantics mismatch (ADR-096), so this PR does not reintroduce one.
Before / after: Copy output
Before (
li.textContent-based): links and the Blockers section weresilently dropped, e.g.:
After (Platform RAG shape — verified byte-for-byte via headless
Chrome, 457-byte output): a Slack RAG shortcode, a BLUF line, and
backtick-labeled sections with inline markdown links on the noun, matching
the format used previously by the
bitwarden-standupplugin'ssynthesize-standup-reportskill:(Illustrative — reflects the verified output shape, not a literal
captured transcript.) Whether markdown links pasted into Slack actually
render as clickable links is still an open question, deferred to
acceptance verification for this change.
Testing
daily-recap.test.sh: 254/254 passed, including a new test assertingthe CSP script hash matches the
<script>block.blocked when the hash is altered, and Copy produces the expected RAG
text byte-for-byte.
pnpm run lint(prettier + spelling) is green on this branch.daily-recapandweekly-recapskills,and whether Slack renders the pasted markdown links, is deferred to the
follow-up "Acceptance verification" task — not covered by this PR.
Related