Skip to content
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.env
.claude/
117 changes: 117 additions & 0 deletions plugins/newtab/skills/trainhop/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
---
name: trainhop
description: Runs the New Tab train-hop release workflow for all automated steps: metabug, locales, metrics, Nimbus recipe, Confluence page, and QA ticket. Use when asked to start a train-hop. Do NOT use for individual steps like updating locales alone — those have their own skills.
argument-hint: "[bug-number] (optional)"
disable-model-invocation: true
---

# Train-hop: Full Workflow

## Before Starting

**Step 1 — Pick the phase.** Ask (numbered list):

> Which steps would you like to run?
>
> **1. Full workflow** — all steps, start to finish
> **2. Prep only** — steps 1–3 (metabug, locales, metrics), run before the XPI is cut
> **3. Post-XPI** — steps 4–5 (Nimbus recipe, Confluence page + QA ticket)
> **4. Custom** — specify which step numbers to include

**Step 2 — Resolve, ask, confirm.** Resolve what you can automatically, ask the user only for the rest, then present the plan and wait for a single confirmation.

*Resolve automatically:*
- **`TARGET_VERSION`** — read the Nightly major from `browser/config/version.txt`; the release target is `Nightly − 2` (e.g. `154` → `152`). This labels the meta bug, per-step bugs, QA ticket, and Confluence title — **not** the Nightly version. Use it everywhere; don't re-derive it per step.
- **Conductor** — defaults to the signed-in user (`${MCP}atlassianUserInfo`).
- **QA contact** — defaults to Valentin Bandac (`6310ac8255b0a9e29f1af16d`).
- **accountIds** — resolve names → accountIds with `${MCP}lookupJiraAccountId`.

*Ask the user (only what the chosen phase needs):*
- **Target version** — confirm the computed `Nightly − 2` default, or take an override.
- If step 4 is included — **Ship task URL** (from ShipIt).
- If step 5 is included:
- **Dates**: XPI cut, QA handoff, QA sign-off (= the Jira due date), and the staged **Release** dates — 50% and 100% (usually consecutive days).
- **Rel Man** — name only (check https://whattrainisitnow.com/release/?version=release if unknown).
- **Conductor & QA contact** — default to you (the signed-in user) and Valentin Bandac; confirm, or name someone else.

Then show the resolved target version and the exact steps to run, and wait for one confirmation before proceeding.

## Pre-conditions

Run these checks at the start — don't ask the user to verify them. If something is off, explain it in plain language and wait.

1. **In the Firefox checkout.** Confirm `browser/config/version.txt` exists in the working directory. If not, the workflow is running from the wrong place — ask the user to start it from their local Firefox source checkout (every step operates there).
2. **Clean tree on `main`.** Run `git status --porcelain` and `git rev-parse --abbrev-ref HEAD`. If there are uncommitted changes or the branch isn't `main`, stop and say so plainly (e.g. "there are unsaved changes in the Firefox repo" / "you're on branch X, not `main`") and ask how to proceed — do not run on a dirty tree. Once clean and on `main`, run `git pull` to get up to date.
3. **Bugzilla API key.** The key must be a non-empty `BUGZILLA_API_KEY` line in `~/.mozbuild/trainhop.env`. Check with:
```bash
grep -qE '^BUGZILLA_API_KEY=.+' ~/.mozbuild/trainhop.env && echo ok
```
If that prints `ok`, continue. If not (file missing or value empty), the key isn't set up: point the user to the setup doc — https://mozilla-hub.atlassian.net/wiki/x/QoA4qg — and **stop** until they've created the file in their own terminal, then re-check. **Never ask for the key in the chat, prompt for it, or write it yourself** — the key must never enter the Claude session. You only verify it's present.
4. **Atlassian MCP** (only needed for step 5). The Atlassian MCP plugin must be installed and authenticated: `/plugin install atlassian@claude-plugins-official` then `/reload-plugins` (OAuth on first use, no token). See `references/credentials.md` for setup and the `${MCP}` tool-prefix note.

## Steps

Stop and report to the user if any step fails. Do not proceed past a failed step without explicit instruction.

### 1. Create meta bug

Follow `references/create-metabug.md`. Note the returned bug number — pass it to all subsequent steps.

### 2. Update locales

Follow `references/update-locales.md`, passing the meta bug number from step 1.

### 3. Update metrics

Follow `references/update-metrics.md`, passing the meta bug number from step 1.

### 4. Generate the Nimbus recipe

```bash
./mach newtab trainhop-recipe <ship-task-url>
```

Use the Ship task URL collected in the Inputs section. Display the full output.

### 5. Create Confluence page and file QA ticket

This is a three-substep flow. Do them in order:

#### 5a. Create the Confluence page (with `QA-TBD` placeholder)

Follow `references/create-confluence-page.md`, passing:
- The meta bug number from step 1 (full URL form)
- A placeholder for the QA ticket (`QA-TBD`) — the page will be updated in step 5c
- The dates (XPI Cut, QA Handoff, QA Sign-off, Release 50%/100%) as `YYYY-MM-DD` for HTML `<time>` nodes
- Rel Man, QA, and Conductor as HTML `<span data-type="mention">` nodes (name + accountId)

Note the returned **page ID** and **page URL**.

#### 5b. File the QA ticket

Follow `references/file-qa-ticket.md`, passing:
- The meta bug URL from step 1
- The Confluence page URL from step 5a
- The QA sign-off date (becomes the Jira ticket's due date)
- The QA contact's accountId (for `assignee_account_id`)
- The conductor's name (for `customfield_10138` Feature Owner)

Note the returned **QA ticket key** (e.g. `QA-XXXX`).

#### 5c. Update the Confluence page with the real QA ticket reference

Use `${MCP}updateConfluencePage` to replace the `QA-TBD` placeholder with a real link to the ticket key from 5b. Pass `versionMessage: "Fill in QA ticket reference (QA-XXXX)"`.

## Troubleshooting

**A reference file is missing**
The skill may need to be reinstalled: `claude plugin install newtab`.

**Step fails partway through**
Report which step failed and its error output. Do not continue without explicit user instruction.

**MCP call denied by the auto-mode classifier**
Re-prompt the user — these calls (Jira ticket creation, Confluence page creation/update) are intentional and on the user's behalf.

**Atlassian MCP unavailable**
Create the Confluence page and QA ticket manually in the UI by cloning a recent one — see the "If the Atlassian MCP is unavailable" notes in `references/create-confluence-page.md` and `references/file-qa-ticket.md`.
133 changes: 133 additions & 0 deletions plugins/newtab/skills/trainhop/references/create-confluence-page.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# Train-hop: Create Confluence Page

Creates the `HNT TARGET_VERSION Train Hop` tracking page in the FPS Confluence space.

This step is run **before** the QA ticket is filed (because the QA ticket needs the page URL). Once the QA ticket exists, this page is updated to swap the `QA-TBD` placeholder for the real ticket link.

## Constants

- **Cloud ID**: `mozilla-hub.atlassian.net` (the tools also accept the site URL directly; the UUID form is `d8febd08-c6e9-4c03-9c13-db37c2369ce5`)
- **FPS space ID**: `12484637`
- **Parent folder ID** ("HNT Train Hop Releases"): `1872035972`

> **Tool prefix:** tool names below are written `${MCP}<tool>`, where `${MCP}` is your Atlassian MCP prefix (`mcp__plugin_atlassian_atlassian__` for the official plugin). Resolve it once — see `references/credentials.md` → "Tool prefix (`${MCP}`)" — and substitute it into every `${MCP}...` name.

## Steps

### 1. Create the page

Use `${MCP}createConfluencePage` with **`contentFormat: "html"`**. This MCP documents and round-trips the HTML+ format most reliably; native dates (`<time>`), `@mention`s (`<span data-type="mention">`), inline cards, and code macros all render from HTML. (ADF is also accepted via `contentFormat: "adf"` but is less well documented here.)

**Required parameters**:

- `cloudId`: `mozilla-hub.atlassian.net`
- `spaceId`: `12484637`
- `parentId`: `1872035972`
- `title`: `HNT TARGET_VERSION Train Hop`
- `contentFormat`: `html`
- `body`: the HTML below, with placeholders substituted

### 2. Body template (HTML)

Substitute the placeholders (see below) before sending.

```html
<h3>Summary:</h3>
<ul>
<li>Meta Bug: <a href="METABUG_URL" data-card-appearance="inline">METABUG_URL</a></li>
<li>QA Bug: QA-TBD (will update once filed)</li>
<li>Timeline:
<ul>
<li>XPI Cut: <time datetime="XPI_CUT_DATE">XPI_CUT_DATE</time></li>
<li>QA Handoff: <time datetime="QA_HANDOFF_DATE">QA_HANDOFF_DATE</time></li>
<li>QA Sign-off: <time datetime="QA_SIGNOFF_DATE">QA_SIGNOFF_DATE</time></li>
<li>Release:
<ul>
<li>50%: <time datetime="RELEASE_50_DATE">RELEASE_50_DATE</time></li>
<li>100%: <time datetime="RELEASE_100_DATE">RELEASE_100_DATE</time></li>
</ul>
</li>
</ul>
</li>
<li>Rel Man contact: <span data-type="mention" data-user-id="RELMAN_ACCOUNT_ID">@RELMAN_NAME</span></li>
<li>QA contact: <span data-type="mention" data-user-id="QA_ACCOUNT_ID">@QA_NAME</span></li>
<li>Conductor: <span data-type="mention" data-user-id="CONDUCTOR_ACCOUNT_ID">@CONDUCTOR_NAME</span></li>
</ul>
<p>Release info:</p>
<p>Train hop Recipe:</p>
<pre><code class="language-json">{
"addon_version": "",
"xpi_download_path": ""
}</code></pre>
<h4>Helpful Links</h4>
<ul>
<li>See documentation: <a href="https://mozilla-hub.atlassian.net/wiki/spaces/FPS/pages/1785135275" data-card-appearance="inline">documentation</a></li>
<li>See check-list: <a href="https://mozilla-hub.atlassian.net/wiki/x/kABgag" data-card-appearance="inline">check-list</a></li>
<li><a href="https://sql.telemetry.mozilla.org/dashboard/new-tab-train-hop-add-on-uptake-dashboard">Real-time Train Hop Adoption Graph</a></li>
</ul>
<hr/>
<h3>Features being tested:</h3>
<p>These are the experiments/tickets that are planned as part of this release. They can be required or optional.</p>
<table><tbody>
<tr><th><strong>Jira Ticket / Summary</strong></th><th><strong>Product Owner</strong></th><th><strong>Priority</strong></th></tr>
<tr><td></td><td></td><td></td></tr>
</tbody></table>
<h3>QA Verified Bugs/Patches</h3>
<table><tbody>
<tr><th><strong>Bug</strong></th><th><strong>Summary</strong></th><th><strong>Landing Status</strong></th><th><strong>QA Status</strong></th></tr>
<tr><td></td><td></td><td></td><td></td></tr>
</tbody></table>
<h3>Shims added for <code>@backward-compat</code></h3>
<table><tbody>
<tr><th><strong>Bug Link</strong></th><th><strong>Title</strong></th><th><strong>Notes</strong></th></tr>
<tr><td></td><td></td><td></td></tr>
</tbody></table>
```

### 3. Placeholder substitutions

- `TARGET_VERSION` — Firefox release-target version (e.g. `152.0.5`)
- `METABUG_URL` — `https://bugzilla.mozilla.org/show_bug.cgi?id=BUG_ID`
- `*_DATE` — `YYYY-MM-DD`. Release is a **staged rollout**: `RELEASE_50_DATE` (50%) and `RELEASE_100_DATE` (100%, usually the next day). `<time datetime="YYYY-MM-DD">` renders as a native Confluence date.
- `RELMAN_ACCOUNT_ID` / `QA_ACCOUNT_ID` / `CONDUCTOR_ACCOUNT_ID` — Atlassian accountIds; resolve with `${MCP}lookupJiraAccountId`. Get the conductor's own id via `${MCP}atlassianUserInfo`.
- `*_NAME` — display name shown after `@`.

The **Train hop Recipe** JSON (`addon_version`, `xpi_download_path`) is left empty — the conductor fills it in manually after the recipe is generated.

Common accountIds (verify before use):
- Valentin Bandac (QA): `6310ac8255b0a9e29f1af16d`

### 4. Note the page ID and URL, and move it to the top

The response contains `id` and `_links.webui`. Pass them to the next step (file QA ticket).

### 5. Update the page after the QA ticket is filed

Once the QA ticket key is known, use `${MCP}updateConfluencePage` (pass the full HTML body again with the placeholder swapped). Replace the `QA Bug: QA-TBD ...` list item with:

```html
<li>QA Bug: <a href="https://mozilla-hub.atlassian.net/browse/QA-XXXX">QA-XXXX: Testing New Tab train-hop for Firefox TARGET_VERSION Release</a></li>
```

Pass `versionMessage: "Fill in QA ticket reference (QA-XXXX)"` so the page history is self-documenting.

## Expected Result

The page is accessible under "HNT Train Hop Releases" in the FPS space with the summary, timeline (interactive date macros, staged 50%/100% release), contact `@mention`s, and the empty Release info / Train hop Recipe block. The "Features being tested" / "QA Verified Bugs/Patches" / "Shims" tables are empty and filled in by the conductor.

## Troubleshooting

**MCP tools not found under the `${MCP}` prefix**
The plugin may register a different prefix. List available `mcp__*atlassian*` tools and use the matching names.

**MCP returns 404 on `parentId`**
The "HNT Train Hop Releases" folder may have moved. Search with `${MCP}searchConfluenceUsingCql` for `space = FPS AND title ~ "Train Hop"` and find the folder (type `folder`) — update the constant at the top of this file.

**HTML rejected with a validation error**
The MCP validates ADF nesting: list items can hold paragraphs, nested lists, and code blocks, but not headings/tables/panels; panels can't contain tables/expands. Read the returned error and adjust.

**Mention shows as `@unknown`**
The accountId is wrong. Resolve via `${MCP}lookupJiraAccountId` and try again.

**If the Atlassian MCP is unavailable**
Create the page manually in the Confluence UI: open the most recent `HNT … Train Hop` page, use **••• → Make a copy**, and update the title, dates, links, and @mentions.
27 changes: 27 additions & 0 deletions plugins/newtab/skills/trainhop/references/create-metabug.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Train-hop: Create Meta Bug

Files the Bugzilla meta bug that tracks the full train-hop release. All other bugs filed during the train-hop process block this meta bug.

## Steps

### 1. File the meta bug

Use the `TARGET_VERSION` (the release-target version) determined by the orchestrator — see SKILL.md → "Determine the train-hop target version". If running this step standalone, default to `(Nightly major from browser/config/version.txt) − 2` and confirm with the user. Then run:

```bash
python3 <skill-scripts-dir>/file_bug.py \
--summary "[meta] Firefox TARGET_VERSION train-hop metabug" \
--type task \
--keywords meta
```

The script prompts for your Bugzilla API key and prints the new bug ID. Note it — all subsequent bugs in this workflow will block it.

## Expected Result

The meta bug is visible at `https://bugzilla.mozilla.org/show_bug.cgi?id=BUG_ID` with summary `[meta] Firefox TARGET_VERSION train-hop metabug` and the `meta` keyword.

## Troubleshooting

**API returns an error**
The script prints the full message. Common cause: the `meta` keyword requires special Bugzilla permissions — ask a peer with those permissions to file it manually.
74 changes: 74 additions & 0 deletions plugins/newtab/skills/trainhop/references/credentials.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Credential Setup

Before running a train-hop you only need to set up **one** thing: a **Bugzilla API key**. Jira and Confluence sign you in automatically through the Atlassian plugin — no key to manage (see [Atlassian (Jira & Confluence)](#atlassian-jira--confluence) below).

## Set up your Bugzilla API key (one time)

**Full walkthrough:** https://mozilla-hub.atlassian.net/wiki/x/QoA4qg

In short: generate a key at https://bugzilla.mozilla.org (your name → **Preferences** → **API Keys**), then save it as a single non-empty line in `~/.mozbuild/trainhop.env`:

```
BUGZILLA_API_KEY=your_key_here
```

Do this in your own terminal or editor — **never paste the key into the Claude session** (chat or the `!` prefix), because anything typed there is saved in the conversation log. The train-hop only reads the file; it never asks for the key in-session. If the key is missing when a step runs, the skill points you to the doc above and stops.

### Notes (advanced)

- `~/.mozbuild` is Firefox's per-user directory — it already exists once you have a working local Firefox, keeps the key out of every repo, and survives plugin updates. `scripts/trainhop.env.example` is a template.
- An exported `BUGZILLA_API_KEY` overrides the file (handy for a one-off): `BUGZILLA_API_KEY=... python3 file_bug.py`.

## Atlassian (Jira & Confluence)

Jira and Confluence are handled by the **Atlassian MCP plugin** — you sign in once via OAuth and there's no API key to manage. It is published in the **official Anthropic marketplace** (`claude-plugins-official`), which is registered automatically when Claude Code starts. See the [Discover plugins → External integrations](https://code.claude.com/docs/en/discover-plugins#external-integrations) docs for the full list.

### Install

From inside Claude Code, run:

```
/plugin install atlassian@claude-plugins-official
```

Then reload so the new MCP server is picked up:

```
/reload-plugins
```

If Claude Code reports the plugin is not found, refresh the marketplace first and retry:

```
/plugin marketplace update claude-plugins-official
/plugin install atlassian@claude-plugins-official
```

### Authenticate

On first use, the plugin walks you through an OAuth flow against your Mozilla Atlassian workspace (`mozilla-hub.atlassian.net`). Approve the requested scopes (read/write for Jira and Confluence). No tokens are stored locally — Claude Code manages the OAuth session.

### Tool prefix (`${MCP}`)

Throughout this skill, **`${MCP}`** stands for the Atlassian MCP tool prefix. Resolve it once and substitute it into every `${MCP}<tool>` name (e.g. `${MCP}createJiraIssue`):

- **Official plugin** (the recommended install above): `mcp__plugin_atlassian_atlassian__`
- **Directly-configured MCP server** named `atlassian` (via `claude mcp add` / settings.json): `mcp__atlassian__`
- **Other / unsure**: list the available `mcp__*atlassian*` tools and use whatever prefix appears.

The tool *base names* — `createConfluencePage`, `createJiraIssue`, `updateConfluencePage`, `lookupJiraAccountId`, `atlassianUserInfo`, `getJiraIssue`, `searchConfluenceUsingCql` — are stable; only the prefix varies. A newly-added MCP server may only appear after `/reload-plugins` or a CLI restart.

### Verify

After install + reload, the `${MCP}*` tools should be available. A quick smoke test from Claude:

> List the spaces I can access in Confluence (use `${MCP}getConfluenceSpaces` with `cloudId: mozilla-hub.atlassian.net`).

You should see "Firefox Product Space" (key `FPS`, id `12484637`) in the response.

### Troubleshooting

- **`${MCP}*` tools missing after install**: run `/reload-plugins`. If still missing, check `/plugin` → Errors tab.
- **OAuth fails**: confirm you have permission on `mozilla-hub.atlassian.net`. The marketplace plugin's docs (linked from `/plugin` → Discover → atlassian) cover re-auth.
- **MCP call blocked by the auto-mode classifier**: this is a Claude Code permission prompt, not an MCP failure. Approve the call to proceed.
- **MCP entirely unavailable**: create the Jira ticket and Confluence page manually in the UI (clone a recent one) — see the "If the Atlassian MCP is unavailable" notes in `references/file-qa-ticket.md` and `references/create-confluence-page.md`.
Loading