main

Generate Quarterly Report

Step 1 — Determine Period

Parse the user’s request to extract the quarter and year. Convert to date range:

Input Start End
Q1 2026 2026-01-01 2026-03-31
Q2 2026 2026-04-01 2026-06-30
Q3 2026 2026-07-01 2026-09-30
Q4 2026 2026-10-01 2026-12-31
“last quarter” Calculate from current date

Step 2 — Collect Data (Parallel)

Run all data collection concurrently.

2a. daily-plan review (GitHub + Jira + Org + AI)

daily-plan review "<START>..<END>" --json --no-discussions --no-comments

This returns JSON with:

  • org_done — completed org-mode TODO items

  • ai_sessions — AI coding sessions

  • jira_completed — Jira issues resolved via resolutiondate JQL (keys: key, summary, status, priority, url)

  • github_merged — PRs authored and merged (keys: repo, number, title, url)

  • github_reviewed — PRs reviewed, excluding own PRs (keys: repo, number, title, author, url)

  • github_issues_created — Issues filed (keys: repo, number, title, url)

  • github_security_advisories — Security advisories active in the period (created, published, closed, or updated in window) across the configured VMT repos (keys: ghsa_id, cve_id, summary, severity, state, repo, url, credits, role). The role field is one of:

    • author — the user opened the advisory
    • credited — the user is in the credits list
    • triaged — neither, but the repo is a VMT repo, so the advisory was reviewed as part of the security queue

    daily-plan paginates (--paginate --slurp, per_page=100) so high-volume repos like tektoncd/pipeline are not truncated. The actual review activity (comments, state transitions) lives in private advisory forks and is not exposed by the REST API — triaged is a proxy based on VMT repo membership. The ground-truth signal for review/triage workload is the tekton-vmt mailing list (see 2g).

daily-plan automatically splits queries by month and uses --limit 1000 to avoid GitHub Search API truncation. Results are scoped to the configured owners (tektoncd, openshift-pipelines).

daily-plan’s GitHub queries are scoped to the configured owners (tektoncd, openshift-pipelines). PRs in other orgs (NixOS/nixpkgs, containers/, chmouel/, github/advisory-database, etc.) are not included — add those orgs to daily-plan’s config if they should be tracked. Security advisories are filtered to only those where the user is in credits or is author — team-based collaborating_users access is excluded.

2b. GitLab (gitlab.cee.redhat.com)

The internal Red Hat GitLab instance requires REST API with a personal access token. The mcp__plugin_gitlab_gitlab__* MCP plugin targets gitlab.com only — do NOT use it for internal GitLab.

Load token:

export GITLAB_TOKEN=$(passage show gitlab/cee-redhat/token 2>/dev/null || echo "")

If empty, ask the user to provide it or skip GitLab. Generate tokens at: https://gitlab.cee.redhat.com/-/user_settings/personal_access_tokens (scope: read_api).

Resolve user ID:

curl -s "https://gitlab.cee.redhat.com/api/v4/users?username=vdemeest" \
  -H "PRIVATE-TOKEN: $GITLAB_TOKEN" | python3 -c "import json,sys; u=json.load(sys.stdin)[0]; print(u['id'], u['name'])"

MRs authored:

curl -s "https://gitlab.cee.redhat.com/api/v4/merge_requests?author_id=<ID>&state=merged&scope=all&order_by=updated_at&sort=desc&per_page=100&created_after=<START>&created_before=<END>" \
  -H "PRIVATE-TOKEN: $GITLAB_TOKEN"

MRs reviewed:

curl -s "https://gitlab.cee.redhat.com/api/v4/merge_requests?reviewer_id=<ID>&state=merged&scope=all&order_by=updated_at&sort=desc&per_page=100&created_after=<START>&created_before=<END>" \
  -H "PRIVATE-TOKEN: $GITLAB_TOKEN"

Note: author_username filter does NOT work on the global MR endpoint — always use author_id (numeric) + scope=all.

Extract from each MR: iid, title, references.full, merged_at, web_url. Group by namespace/project.

2c. Google Calendar — Meetings

Use GoogleWorkspace skill scripts to query calendar events for the quarter:

cd ~/.config/claude/skills/GoogleWorkspace/scripts && node workspace.js call calendar events.list '{
  "calendarId": "primary",
  "timeMin": "<START>T00:00:00Z",
  "timeMax": "<END>T23:59:59Z",
  "singleEvents": true,
  "orderBy": "startTime",
  "maxResults": 2500,
  "fields": "items(summary,start,end,status,attendees)"
}'

Filter to work meetings (exclude personal blocks like “Focus time”, “Home”, etc.). Count by meeting name to identify recurring series. Only count meetings with 2+ attendees.

2d. Gmail / mu — Meeting Notes

Use mu find (Maildir) to search for Gemini meeting notes in Q1:

mu find 'from:gemini-notes@google.com AND date:<START_YYYYMMDD>..<END_YYYYMMDD>' --fields 's' 2>/dev/null

Alternatively use GoogleWorkspace scripts:

cd ~/.config/claude/skills/GoogleWorkspace/scripts && node workspace.js gmail-search \
  "from:gemini-notes@google.com newer_than:120d"

If MCP tools are available instead (e.g. in Claude Desktop):

mcp__google-workspace__search_emails: from:gemini-notes@google.com to:vdemeest@redhat.com after:<START> before:<END>

2e. Google Drive — Documents

cd ~/.config/claude/skills/GoogleWorkspace/scripts && node workspace.js drive-search \
  "modifiedTime > '<START>T00:00:00' and modifiedTime < '<END>T23:59:59' and trashed=false"

2f. Slack

Use the Slack skill:

~/.config/claude/skills/Slack/tools/SlackRead.sh search "from:vdemeest <relevant_terms>" --after <START> --before <END>

Or via MCP if available:

mcp__slack__search_messages: from:vdemeest after:<START> before:<END>

2g. tekton-vmt — Security Review & Triage (ground truth)

The security advisory review/triage workload is not fully visible via the GitHub API (review activity lives in private advisory forks). The reliable signal is the tekton-vmt mailing list traffic. Count distinct security threads in the period:

# distinct threads (strip Re:/Fwd: and dedup)
mu find 'tekton-vmt AND date:<START_YYYYMMDD>..<END_YYYYMMDD>' --fields 's' 2>/dev/null \
  | sed 's/^Re: //; s/^Fwd: //' | sort -u | wc -l

# full list with subjects for the report narrative
mu find 'tekton-vmt AND date:<START_YYYYMMDD>..<END_YYYYMMDD>' --fields 's' 2>/dev/null \
  | sed 's/^Re: //; s/^Fwd: //' | sort | uniq -c | sort -rn

Combine this with github_security_advisories:

  • Credited/Authored advisories (role in author/credited) — formal recognition, list individually with CVE links
  • Triaged advisories (role = triaged) + tekton-vmt thread count — the review queue volume; summarize as a count with notable examples

This ensures the security section reflects the full VMT workload, not just credited CVEs.

Step 3 — Analyze & Categorize

Before generating HTML, analyze the raw data:

  1. Count totals for the stats strip (PRs merged, reviewed, GitLab MRs, issues filed, repos contributed to)
  2. Group GitHub PRs by repository, ordered by PR count descending
  3. Group GitHub issues by theme (security, infrastructure, features, governance, code modernization)
  4. Group GitLab MRs by release track (OSP 1.15, 1.20, 1.21, 1.22, cross-release)
  5. Identify Jira epics — filter jira_completed for Epics and Features only (exclude Stories, Tasks, Sub-tasks, Bugs for the main epic section; keep notable bugs separate)
  6. Identify key initiatives — look for patterns: security campaigns, governance work, community leadership, tooling, release management
  7. Extract meeting patterns from Gmail/Gemini notes — recurring meetings, frequency

Step 4 — Generate HTML

Use the template from reference/Template.html in this skill directory. The template is a complete, self-contained HTML file with all CSS inline.

Read the template:

read reference/Template.html

Fill in all data sections. Key rules:

Linking — every item must be clickable

Item type URL format
Jira keys <a href="https://issues.redhat.com/browse/<KEY>" target="_blank" class="key-badge">KEY</a>
GitHub PRs/issues Use the url field from gh search JSON output: <a href="<url>" target="_blank" class="pr-link">title</a>
GitLab MRs Use web_url from API: <a href="<web_url>" target="_blank" class="pr-link">!iid title</a>

Security Advisories table

Render advisories as a single table with two group-header rows splitting by role:

  • ⭐ Credited / Authored (N) — advisories where role is author or credited
  • 🛡 Triaged — VMT review queue (N) — advisories where role is triaged

Columns: Advisory | Summary | Repo | Severity | Outcome

  • Advisory cell: <a class="pr-link" href="<url>">ghsa_id</a> plus <span class="cve-badge">CVE</span> when cve_id is set
  • Severity badge: <span class="tag {red|amber|blue}"> — red for critical/high, amber for medium, blue for low
  • Outcome badge (from the state field): <span class="tag {green|red|amber|blue}">{label}</span>
    • closed → green “Closed” · published → red “Published” · draft → amber “Draft” · triage → blue “In triage” · withdrawn → purple “Withdrawn”

The section summary must state the credited-vs-triaged split, the tekton-vmt thread count (from 2g), and the triaged outcome breakdown (e.g. “of N triaged, X closed, Y published, Z draft/triage”).

A working reference generator that produces this table (and the full report) lives at reference/generate.py in this skill — adapt its adv_rows() / section assembly rather than rewriting from scratch.

Section summaries

Every data section must open with a <div class="section-summary"> containing 2–3 sentences synthesizing the section’s impact. Cover: total volume, standout items, and business/technical significance. Interpret — don’t just repeat the table.

Executive summary

3–5 sentences in third person, past tense, executive-readable prose. Cover:

  • Major outcomes (epics closed, features shipped, security work)
  • Breadth of contribution (repos, systems, teams)
  • Standout contributions (CVEs, governance, proposals → merged)
  • Quantified signal (“50+ PRs merged across 15 repos”)

Sections to include (omit any with zero results)

  1. Executive Summary (always)
  2. Jira — Epics & Features Closed
  3. GitHub — PRs Authored & Merged (grouped by repo, with group headers like “⚡ Tekton Core”, “🏛 Community”)
  4. GitHub — PRs Reviewed (grouped by repo)
  5. GitHub — Issues Filed (grouped by theme)
  6. GitHub — Security Advisories — split into Credited/Authored (list with CVE links) and Triaged (VMT review queue) (count + notable examples), supplemented by the tekton-vmt thread count
  7. GitLab — MRs Reviewed (grouped by release track)
  8. Key Initiatives & Strategic Contributions (achievement grid)
  9. Meeting & Cross-Team Engagement (Google Calendar)
  10. Documents & Presentations (Google Drive)
  11. Communication Highlights (mu/Gmail — notable outbound threads)
  12. Areas of Focus (tag cloud)

Stats strip values

Count from the collected data:

  • Epics Closed (Jira epics/features with Done status)
  • GitHub PRs Merged (authored)
  • GitHub PRs Reviewed
  • Issues Filed
  • Security Advisories (credited/authored)
  • Jira Issues Closed
  • GitLab MRs Reviewed (if available)

Step 5 — Write Output

# Write the HTML report
write ~/desktop/downloads/q<N>-<YEAR>-achievements-vincent-demeester.html

Confirm to the user: file path, total counts, any data sources that were unavailable.

Common Mistakes

Mistake Fix
Including personal repos (vdemeester/*) Exclude — only include org repos (tektoncd, openshift-pipelines, etc.)
Plain text Jira keys, PR titles, MR titles Always hyperlink with <a> tags
Including Jira Stories/Tasks in the epics section Filter for issuetype in (Epic, Feature) only
Using MCP gitlab plugin for internal GitLab MCP targets gitlab.com — use REST API for gitlab.cee.redhat.com
author_username on global MR endpoint Use author_id (numeric) + scope=all
Missing section summaries Every card must have a section-summary div
Sections with zero results Omit entirely — no “No items found” text
Forgetting to load GITLAB_TOKEN Load from passage or ask user before GitLab queries

Graceful Degradation

If a data source is unavailable:

  1. GitLab token missing → Skip GitLab section, note in footer
  2. Google Workspace not configured → Skip Gmail/Drive sections
  3. Slack not configured → Skip Slack section
  4. daily-plan not found → Fall back to direct gh + Jira CLI queries
  5. Always note unavailable sources in the footer “Data sources” line