From 3ec159a9e7229ebd40764bf0c19c1ae3f697aa34 Mon Sep 17 00:00:00 2001 From: Ariane Coffin Date: Tue, 5 May 2026 17:04:59 -0700 Subject: [PATCH 1/5] Add skill to triage jira issues against code --- .claude-plugin/marketplace.json | 2 +- README.md | 2 +- .../.claude-plugin/plugin.json | 11 +- .../bitwarden-atlassian-tools/CHANGELOG.md | 6 + .../skills/triaging-jira-issues/SKILL.md | 111 ++++++++++++++++++ .../examples/triage_workflow.md | 55 +++++++++ 6 files changed, 183 insertions(+), 4 deletions(-) create mode 100644 plugins/bitwarden-atlassian-tools/skills/triaging-jira-issues/SKILL.md create mode 100644 plugins/bitwarden-atlassian-tools/skills/triaging-jira-issues/examples/triage_workflow.md diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index feda5ee0..3ec36859 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -48,7 +48,7 @@ { "name": "bitwarden-atlassian-tools", "source": "./plugins/bitwarden-atlassian-tools", - "version": "2.3.0", + "version": "2.4.0", "description": "Read-only Atlassian access via MCP server with deep Jira issue research skill, JQL search, Confluence pages, CQL search, and attachments" }, { diff --git a/README.md b/README.md index a53b17ee..45b7c480 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ A curated collection of plugins for AI-assisted development at Bitwarden. Enable | [bitwarden-ai-telemetry](plugins/bitwarden-ai-telemetry/) | 1.0.0 | Claude Code hooks emitting metadata-only AI-usage telemetry (identity, git-linkage, MCP) via OTLP | | [bitwarden-tech-lead](plugins/bitwarden-tech-lead/) | 2.3.2 | Tech lead for technical planning, architecture coherence, and surfacing patterns to Technical Strategy Ideas | | [bitwarden-shepherd](plugins/bitwarden-shepherd/) | 1.0.0 | Champion of a technical strategy — shepherds a TSI through evaluation into the funnel, then through to adoption | -| [bitwarden-atlassian-tools](plugins/bitwarden-atlassian-tools/) | 2.3.0 | Read-only Atlassian access via MCP server with deep Jira issue research skill | +| [bitwarden-atlassian-tools](plugins/bitwarden-atlassian-tools/) | 2.4.0 | Read-only Atlassian access via MCP server with deep Jira issue research skill | | [bitwarden-code-review](plugins/bitwarden-code-review/) | 1.13.1 | Autonomous code review agent following Bitwarden engineering standards with GitHub integration | | [bitwarden-delivery-tools](plugins/bitwarden-delivery-tools/) | 2.1.0 | Delivery lifecycle skills: initiative funnel navigation, work transitions, tech breakdowns and task decomposition, commits, PRs, preflight, labeling | | [bitwarden-designer](plugins/bitwarden-designer/) | 0.1.0 | Product designer persona: Code of Conduct and 30/60/90 critique, critique facilitation; dispatches into bitwarden-design-tools | diff --git a/plugins/bitwarden-atlassian-tools/.claude-plugin/plugin.json b/plugins/bitwarden-atlassian-tools/.claude-plugin/plugin.json index 81a26267..2539567d 100644 --- a/plugins/bitwarden-atlassian-tools/.claude-plugin/plugin.json +++ b/plugins/bitwarden-atlassian-tools/.claude-plugin/plugin.json @@ -1,9 +1,16 @@ { "name": "bitwarden-atlassian-tools", - "version": "2.3.0", + "version": "2.4.0", "description": "Read-only Atlassian access via MCP server with deep Jira issue research skill, JQL search, Confluence pages, CQL search, and attachments", "author": { "name": "Bitwarden" }, - "keywords": ["atlassian", "jira", "confluence", "mcp", "read-only", "cql"] + "keywords": [ + "atlassian", + "jira", + "confluence", + "mcp", + "read-only", + "cql" + ] } diff --git a/plugins/bitwarden-atlassian-tools/CHANGELOG.md b/plugins/bitwarden-atlassian-tools/CHANGELOG.md index 1bc181c0..c0e4522e 100644 --- a/plugins/bitwarden-atlassian-tools/CHANGELOG.md +++ b/plugins/bitwarden-atlassian-tools/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to the Bitwarden Atlassian Tools plugin will be documented i The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [2.4.0] - 2026-07-24 + +### Added + +- `triaging-jira-issues` skill for verifying whether a single Jira issue is still relevant against the current codebase — fetches the ticket, searches for the described code path across clients/server/sdk-internal, checks git history since the filed date, and returns a verdict (still relevant / partially addressed / no longer relevant / cannot determine) with file:line evidence + ## [2.3.0] - 2026-07-15 ### Added diff --git a/plugins/bitwarden-atlassian-tools/skills/triaging-jira-issues/SKILL.md b/plugins/bitwarden-atlassian-tools/skills/triaging-jira-issues/SKILL.md new file mode 100644 index 00000000..a3f19d35 --- /dev/null +++ b/plugins/bitwarden-atlassian-tools/skills/triaging-jira-issues/SKILL.md @@ -0,0 +1,111 @@ +--- +name: triaging-jira-issues +description: Use when the user provides a single Jira issue key and asks whether it is still relevant, still applicable, still pending, still a bug, has been fixed, or can be closed. Trigger phrases include "Is [TICKET] still relevant?", "Is this still an issue?", "Is PM-123 still pending?", "Has this been fixed?", "Can we close this?", "Is this ticket still valid?", "Is this still applicable?", "Does this bug still exist?". Fetches the ticket and verifies the described problem against the current codebase to return a verdict with evidence. For bulk triage of multiple tickets or a JQL filter, use the bulk-triaging-jira-issues skill instead. +--- + +# Triaging a Jira Issue for Relevance + +Determine whether a Jira issue still applies to the current codebase. Fetch the ticket, locate the specific code path it describes, compare current behavior against the ticket's description, and return a verdict with evidence. + +This skill is distinct from `researching-jira-issues`. That skill synthesizes context to *understand* a ticket. This skill verifies whether the described problem or task still exists in code — the answer should be a clear verdict, not a summary. + +## Workflow + +### Step 1: Fetch the Ticket and Its Context + +Use `get_issue` with `expand: ["renderedFields", "names"]`. Extract: + +- **The specific problem or task**: Read beyond the summary. The description, acceptance criteria, and replication steps are more precise. For bugs: what is the actual broken behavior and what is expected? For tasks: what specific code change is required? +- **Technical identifiers**: Method names, class names, file paths, API endpoint routes, UI strings that appear in source, config keys, feature flag names — anything named in the ticket that can be searched in code. Note these explicitly before moving on. +- **Filed date**: Used to scope `git log` searches. +- **Repo scope signal**: Determine whether this applies to `clients`, `server`, `sdk-internal`, or a combination. Use the team field, component labels, and description content (see Scope Notes below). + +Also note these **staleness signals** from the ticket fields before moving on: + +- **Age**: How many months since the ticket was filed? +- **Priority and assignee**: Is it Low/Lowest priority? Unassigned? +- **Parent epic**: Does the ticket have a parent epic? If so, fetch it (`get_issue`) and check whether all other child tickets are resolved. A lone surviving task in an otherwise-completed epic is a strong signal that the work may have been intentionally deferred or forgotten — not that it's still needed. + +After fetching the ticket, always do both of the following: + +**Fetch issue comments** (`get_issue_comments`): Comments often contain decisions that never made it back into the description — root cause findings, "we decided not to fix this", priority calls, or pointers to where the fix landed. Read them before building search targets. + +**Fetch linked issues** (`get_issue_remote_links` and the `issuelinks` field): Look specifically for blocking relationships — issues this ticket blocks or is blocked by. A ticket blocked by unresolved work may not be actionable yet; a blocker that has since been resolved may mean this ticket is now ready. Fetch (`get_issue`) any directly linked issues to check their current status and extract additional technical context. Do not traverse more than one level deep. + +### Step 2: Build Search Targets + +From the ticket, identify 2–5 specific identifiers to search for in code. Prioritize: + +- Method or function names mentioned in the ticket (e.g., `ValidateLegacyMigrationAsync`, `unlockViaBiometrics`, `validateCanManagePermission`) +- Class or component names (e.g., `BaseRequestValidator`, `LockComponent`, `CollectionDialog`) +- API route strings (e.g., `"trial/send-verification-email"`, `"verify-email-token"`) +- UI strings that appear in source or i18n JSON (e.g., `"managePermissionRequired"`) +- Config or feature flag keys (e.g., `DenyLegacyUserMinimumVersion`) + +If the ticket names no specific identifiers, derive them from the described behavior: what function would implement this, what component would render this UI, what endpoint would serve this request? + +### Step 3: Search the Code + +Run searches in the relevant repo(s): + +1. **Grep for each identifier** in the relevant source directories. Don't stop at confirming existence — read the surrounding code to understand current behavior. A symbol that still exists but now behaves differently may mean the bug is already fixed. + +2. **Read the actual implementation** at each match. The grep result shows where; the file content shows what it currently does. Confirm whether the behavior the ticket describes is still present, partially changed, or gone. + +3. **Check git history on affected files** since the ticket was filed: + ``` + git log --oneline --since="" -- + ``` + Look for commits that might have silently addressed the issue — refactors, renames, feature flag removals, component rewrites. If a commit looks relevant, read its diff on the affected lines. + +4. **Trace refactored paths**: If a named symbol no longer exists, find what replaced it. A deleted method does not mean the bug is fixed — the logic may have moved. Search for the behavior, not just the original name. + +### Step 4: Deliver Verdict + +Compare what the ticket describes against what the code does today. Reach a conclusion. + +**Verdict options**: + +- **Still relevant** — The described problem exists unchanged in the current code. Show the specific `file:line` that proves it. +- **Partially addressed** — Some part of the described problem was fixed, but a gap remains. State precisely what was fixed and what remains open, with evidence for each. +- **No longer relevant** — The problem no longer exists. Explain what changed and cite the current code or commit that proves it. Note whether the ticket is safe to close. +- **Technically relevant, but question whether still needed** — The gap exists in code, but staleness signals are strong enough that the work should be confirmed with the reporter or PM before picking it up. Use this when multiple signals combine: ticket is significantly old (> ~9 months), unassigned, low priority, and/or is the lone surviving task in an otherwise-completed epic. State the code evidence and the staleness signals separately so the reader can weigh both. +- **Cannot determine** — The ticket description is too vague to trace to specific code, and `git log` provides no signal. State what you searched and why it was inconclusive. Only use this after exhausting the search targets. + +**Format**: Lead with the verdict and its justification in plain prose. Cite `file:line` references as evidence. If still relevant, state what specifically remains to be done — do not just restate the ticket. If staleness signals are present even for a "Still relevant" verdict, note them at the end: ticket age, epic completion state, priority, and assignee. Keep it tight; a verdict paragraph with supporting evidence is sufficient. + +## Scope Notes + +Use these to determine which repo and directories to search: + +**Server repo** (`server/src/`): +- Team field (including but not limited to): Billing, Auth (server-side), Admin Console (server-side) +- Keywords in description: C#, .NET, API endpoint, stored procedure, Stripe, webhook, IdentityServer, database +- Key directories: `src/Identity/IdentityServer/` (auth/token flow), `src/Core/Billing/` and `src/Identity/Billing/` (billing), `src/Core/Services/Implementations/` (user/org services), `src/Api/` (REST controllers) + +**Clients repo** (`clients/`): +- Team field (including but not limited to): Key Management, Browser, Desktop, Admin Console (frontend), Vault +- Keywords in description: Angular, TypeScript, browser extension, desktop app, web vault, UI component, form +- Key directories: `apps/browser/src/` (extension), `apps/desktop/src/` (desktop), `apps/web/src/` (web vault), `libs/key-management-ui/src/lock/` (shared lock/unlock UI), `libs/key-management/src/` and `libs/auth/src/` (shared auth) + +**SDK repo** (`sdk-internal/crates/`): +- Team field (including but not limited to): SDK, Platform +- Keywords in description: Rust, crate, SDK, FFI, bitwarden-crypto, bitwarden-auth, bitwarden-core +- Key crates: `bitwarden-crypto/` (encryption), `bitwarden-auth/` (authentication), `bitwarden-core/` (core types), `bitwarden-ffi/` (cross-platform bindings) + +**Multiple repos**: Auth and key management tickets often span clients and server. Vault timeout, biometrics, and unlock flow bugs commonly touch both `clients` and `sdk-internal`. Start with the repo the team field suggests, then check the other if the first shows only half the picture. + +## What NOT to Do + +- Don't traverse linked issues more than one level — fetch directly linked issues (blocks, is blocked by, parent epic) but do not follow their links further +- Don't skip the parent epic check for task tickets — one extra `get_issue` call often changes the recommendation from "build this" to "confirm whether this is still wanted" +- Don't read Confluence pages unless the ticket has no description and a Confluence link is the only available context +- Don't return "cannot determine" without first checking both the named symbols AND `git log` on the relevant files +- Don't treat "symbol still exists" as "bug still present" — read the current behavior, not just the name +- Don't restate the ticket description as the verdict — the verdict must reflect what the code says today + +## Examples + +### examples/triage_workflow.md + +Three worked examples: a bug where the described code path was silently refactored away, a task whose implementation gap is confirmed present, and a spike made obsolete by later work. diff --git a/plugins/bitwarden-atlassian-tools/skills/triaging-jira-issues/examples/triage_workflow.md b/plugins/bitwarden-atlassian-tools/skills/triaging-jira-issues/examples/triage_workflow.md new file mode 100644 index 00000000..8919362d --- /dev/null +++ b/plugins/bitwarden-atlassian-tools/skills/triaging-jira-issues/examples/triage_workflow.md @@ -0,0 +1,55 @@ +# Example: Triage Workflow + +Three representative cases showing how the verdict changes depending on what the code investigation finds. + +--- + +## Case 1: Bug silently fixed by a refactor (No longer relevant) + +**User request**: "Is PM-XXXX still relevant?" + +**Ticket**: Bug: a cache extension method calls `cache.Get(key)` outside a try/catch, so a cache connection failure throws an unhandled exception instead of returning null. + +**Workflow**: + +1. Fetch PM-XXXX via `get_issue` → extract the described behavior and identifiers: `TryGetValue`, `DistributedCacheExtensions`, `cache.Get`. +2. Grep in `server/src/` → `DistributedCacheExtensions.cs` exists, `TryGetValue` present. +3. Read the file → the method exists but has **zero callers** in the codebase — the code path described by the ticket is dead. +4. `git log` on the file → confirms no callers were added recently; the extension was made obsolete by a service refactor. + +**Verdict**: No longer relevant. `TryGetValue` still has the exception-swallowing bug described in the ticket, but the method has no callers — the code path it describes is unreachable. Safe to close with a note that the method can be deleted. + +--- + +## Case 2: Task implementation gap confirmed (Still relevant) + +**User request**: "Is PM-YYYY still relevant?" + +**Ticket**: Task: when only one member/group has "Can Manage" permission on a collection, that row should be disabled (greyed out) with a tooltip, rather than allowing removal and showing a validation error on save. + +**Workflow**: + +1. Fetch PM-YYYY via `get_issue` → identifiers extracted: `validateCanManagePermission`, `managePermissionRequired`, `AccessItemView`, `readonly`. +2. Grep in `clients/apps/web/src/` → `validateCanManagePermission` found in `collection-dialog.component.ts:564` and wired into the form at lines 331–333. The i18n key `managePermissionRequired` resolves to "At least one member or group must have can manage permission." +3. Read `collection-dialog.component.html` → an error div shows the `managePermissionRequired` message when the validator fires — the error-on-submit path is what's there. +4. Check `AccessItemView` model → has a `readonly` flag that disables the permission editor and keeps the row selected, but no logic in the component or template computes "this is the last Manage-permission row → set `readonly: true`." +5. `git log --since` on both files → no recent commits touching the disabled-row behavior. + +**Verdict**: Still relevant. The current implementation is the error-on-submit model: `validateCanManagePermission` blocks save when no Manage permission exists, showing an error at `collection-dialog.component.html:91-96`. The required behavior — proactively disabling the last Manage row with a tooltip — has not been implemented. `AccessItemView.readonly` could support it, but the reactive logic that sets it has never been written. + +--- + +## Case 3: Spike obsoleted by later architectural work (No longer relevant) + +**User request**: "Is PM-ZZZZ still relevant?" + +**Ticket**: Spike from 2022: ~7,000 users have `Premium = false` but a future `PremiumExpirationDate`. The referenced code in `UserService.cs` sets `Premium = false` while preserving the expiration date. Investigate whether this is broken or intentional. + +**Workflow**: + +1. Fetch PM-ZZZZ via `get_issue` → identifiers extracted: `DisablePremiumAsync`, `PremiumExpirationDate`, `Premium = false`. +2. Grep in `server/src/` → `DisablePremiumAsync` still present at `UserService.cs:894`; sets `user.Premium = false` with `user.PremiumExpirationDate = expirationDate` — same as described. +3. Search how `Premium` is used for access control → `ValidateUserPremiumAsync` in `LicensingService.cs` returns `user.Premium` directly for cloud users. No check on `PremiumExpirationDate`. +4. Find a more recent `UserPremiumAccessView` SQL view → `PersonalPremium` mapped directly from `U.[Premium]`; expiration date not consulted. + +**Verdict**: No longer relevant as an open investigation. The inconsistency (`Premium = false` with a future `PremiumExpirationDate`) still exists in the data, but later billing system work answered the spike's question in code: `PremiumExpirationDate` is billing lifecycle metadata and plays no role in access gating on cloud — `user.Premium` is the sole gate. The data inconsistency has no user-facing impact. Safe to close with a note documenting this as intentional design. From 442a9695b46c34f2f07353f92314f8e886750429 Mon Sep 17 00:00:00 2001 From: ariane-bitwarden Date: Tue, 5 May 2026 17:31:24 -0700 Subject: [PATCH 2/5] Update plugins/bitwarden-atlassian-tools/skills/triaging-jira-issues/SKILL.md Removing reference to a bulk skill that does not exist yet. Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com> --- .../skills/triaging-jira-issues/SKILL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/bitwarden-atlassian-tools/skills/triaging-jira-issues/SKILL.md b/plugins/bitwarden-atlassian-tools/skills/triaging-jira-issues/SKILL.md index a3f19d35..8bc09c80 100644 --- a/plugins/bitwarden-atlassian-tools/skills/triaging-jira-issues/SKILL.md +++ b/plugins/bitwarden-atlassian-tools/skills/triaging-jira-issues/SKILL.md @@ -1,6 +1,6 @@ --- name: triaging-jira-issues -description: Use when the user provides a single Jira issue key and asks whether it is still relevant, still applicable, still pending, still a bug, has been fixed, or can be closed. Trigger phrases include "Is [TICKET] still relevant?", "Is this still an issue?", "Is PM-123 still pending?", "Has this been fixed?", "Can we close this?", "Is this ticket still valid?", "Is this still applicable?", "Does this bug still exist?". Fetches the ticket and verifies the described problem against the current codebase to return a verdict with evidence. For bulk triage of multiple tickets or a JQL filter, use the bulk-triaging-jira-issues skill instead. +description: Use when the user provides a single Jira issue key and asks whether it is still relevant, still applicable, still pending, still a bug, has been fixed, or can be closed. Trigger phrases include "Is [TICKET] still relevant?", "Is this still an issue?", "Is PM-123 still pending?", "Has this been fixed?", "Can we close this?", "Is this ticket still valid?", "Is this still applicable?", "Does this bug still exist?". Fetches the ticket and verifies the described problem against the current codebase to return a verdict with evidence. This skill triages a single ticket at a time; invoke it iteratively for multiple tickets. --- # Triaging a Jira Issue for Relevance From 57834481850c10fc7ee5867b236ba02ee8cbb83d Mon Sep 17 00:00:00 2001 From: Patrick Honkonen Date: Fri, 24 Jul 2026 16:03:56 -0400 Subject: [PATCH 3/5] Apply prettier formatting to triaging-jira-issues SKILL.md --- .../skills/triaging-jira-issues/SKILL.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/plugins/bitwarden-atlassian-tools/skills/triaging-jira-issues/SKILL.md b/plugins/bitwarden-atlassian-tools/skills/triaging-jira-issues/SKILL.md index 8bc09c80..39a1fa92 100644 --- a/plugins/bitwarden-atlassian-tools/skills/triaging-jira-issues/SKILL.md +++ b/plugins/bitwarden-atlassian-tools/skills/triaging-jira-issues/SKILL.md @@ -7,7 +7,7 @@ description: Use when the user provides a single Jira issue key and asks whether Determine whether a Jira issue still applies to the current codebase. Fetch the ticket, locate the specific code path it describes, compare current behavior against the ticket's description, and return a verdict with evidence. -This skill is distinct from `researching-jira-issues`. That skill synthesizes context to *understand* a ticket. This skill verifies whether the described problem or task still exists in code — the answer should be a clear verdict, not a summary. +This skill is distinct from `researching-jira-issues`. That skill synthesizes context to _understand_ a ticket. This skill verifies whether the described problem or task still exists in code — the answer should be a clear verdict, not a summary. ## Workflow @@ -53,9 +53,11 @@ Run searches in the relevant repo(s): 2. **Read the actual implementation** at each match. The grep result shows where; the file content shows what it currently does. Confirm whether the behavior the ticket describes is still present, partially changed, or gone. 3. **Check git history on affected files** since the ticket was filed: + ``` git log --oneline --since="" -- ``` + Look for commits that might have silently addressed the issue — refactors, renames, feature flag removals, component rewrites. If a commit looks relevant, read its diff on the affected lines. 4. **Trace refactored paths**: If a named symbol no longer exists, find what replaced it. A deleted method does not mean the bug is fixed — the logic may have moved. Search for the behavior, not just the original name. @@ -79,16 +81,19 @@ Compare what the ticket describes against what the code does today. Reach a conc Use these to determine which repo and directories to search: **Server repo** (`server/src/`): + - Team field (including but not limited to): Billing, Auth (server-side), Admin Console (server-side) - Keywords in description: C#, .NET, API endpoint, stored procedure, Stripe, webhook, IdentityServer, database - Key directories: `src/Identity/IdentityServer/` (auth/token flow), `src/Core/Billing/` and `src/Identity/Billing/` (billing), `src/Core/Services/Implementations/` (user/org services), `src/Api/` (REST controllers) **Clients repo** (`clients/`): + - Team field (including but not limited to): Key Management, Browser, Desktop, Admin Console (frontend), Vault - Keywords in description: Angular, TypeScript, browser extension, desktop app, web vault, UI component, form - Key directories: `apps/browser/src/` (extension), `apps/desktop/src/` (desktop), `apps/web/src/` (web vault), `libs/key-management-ui/src/lock/` (shared lock/unlock UI), `libs/key-management/src/` and `libs/auth/src/` (shared auth) **SDK repo** (`sdk-internal/crates/`): + - Team field (including but not limited to): SDK, Platform - Keywords in description: Rust, crate, SDK, FFI, bitwarden-crypto, bitwarden-auth, bitwarden-core - Key crates: `bitwarden-crypto/` (encryption), `bitwarden-auth/` (authentication), `bitwarden-core/` (core types), `bitwarden-ffi/` (cross-platform bindings) From 8d03837ceb5fa1d9c2ae6029d3968d3eb1a458bd Mon Sep 17 00:00:00 2001 From: Patrick Honkonen Date: Fri, 24 Jul 2026 17:32:42 -0400 Subject: [PATCH 4/5] Generalize repo scope and rename to assessing-jira-issue-relevance The skill was limited to clients, server, and sdk-internal via a hardcoded Scope Notes mapping, so tickets against any other Bitwarden repo were misrouted. It also assumed affected repos were cloned locally without checking; searching an absent repo returns no matches, which reads the same as the code having been deleted and could close a live ticket. Repo scope is now confirmed with the user before any searching, and the skill halts without a verdict when a repo in scope is unavailable. Renamed because the skill does not triage: it assesses one ticket at a time and neither ranks nor prioritizes across a set. Adds scoped allowed-tools so routine use stops prompting on every MCP fetch. No version bump: 2.4.0 is unpublished and exists only on this branch, so the changelog entry was folded in rather than superseded. --- .cspell.json | 1 + .../bitwarden-atlassian-tools/CHANGELOG.md | 5 +- .../SKILL.md | 81 ++++++++++--------- .../relevance_assessment_workflow.md} | 29 +++++-- 4 files changed, 74 insertions(+), 42 deletions(-) rename plugins/bitwarden-atlassian-tools/skills/{triaging-jira-issues => assessing-jira-issue-relevance}/SKILL.md (65%) rename plugins/bitwarden-atlassian-tools/skills/{triaging-jira-issues/examples/triage_workflow.md => assessing-jira-issue-relevance/examples/relevance_assessment_workflow.md} (65%) diff --git a/.cspell.json b/.cspell.json index cec7852e..38b7e09d 100644 --- a/.cspell.json +++ b/.cspell.json @@ -128,6 +128,7 @@ "tinyui", "touchpoint", "touchpoints", + "toplevel", "triaging", "unassigning", "unassigns", diff --git a/plugins/bitwarden-atlassian-tools/CHANGELOG.md b/plugins/bitwarden-atlassian-tools/CHANGELOG.md index c0e4522e..b20db999 100644 --- a/plugins/bitwarden-atlassian-tools/CHANGELOG.md +++ b/plugins/bitwarden-atlassian-tools/CHANGELOG.md @@ -9,7 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- `triaging-jira-issues` skill for verifying whether a single Jira issue is still relevant against the current codebase — fetches the ticket, searches for the described code path across clients/server/sdk-internal, checks git history since the filed date, and returns a verdict (still relevant / partially addressed / no longer relevant / cannot determine) with file:line evidence +- `assessing-jira-issue-relevance` skill for determining whether a single Jira issue still applies to the current codebase. Fetches the ticket along with its comments, linked issues, and parent epic; confirms which repositories are in scope before searching; greps for the code path the ticket describes; checks git history since the filed date; and returns a verdict (still relevant / partially addressed / no longer relevant / technically relevant but worth questioning / cannot determine) with `file:line` evidence. Intended for backlog cleanup — one ticket per invocation. + - Works against any Bitwarden repository. Where the ticket does not name one outright, the skill infers the likely repo from the available signals and presents that inference for confirmation before searching anything, then orients itself by reading the confirmed repo's own `CLAUDE.md`/`README`. + - Halts without a verdict if a repository in scope is not cloned locally and the user declines to clone it. Searching an absent repo returns no matches, which is indistinguishable from the code having been removed, and would otherwise produce a confident "no longer relevant" on a live ticket. + - Ships scoped `allowed-tools` covering the four read-only Atlassian MCP tools the workflow calls, so routine use does not prompt for permission on every fetch. ## [2.3.0] - 2026-07-15 diff --git a/plugins/bitwarden-atlassian-tools/skills/triaging-jira-issues/SKILL.md b/plugins/bitwarden-atlassian-tools/skills/assessing-jira-issue-relevance/SKILL.md similarity index 65% rename from plugins/bitwarden-atlassian-tools/skills/triaging-jira-issues/SKILL.md rename to plugins/bitwarden-atlassian-tools/skills/assessing-jira-issue-relevance/SKILL.md index 39a1fa92..36add8ca 100644 --- a/plugins/bitwarden-atlassian-tools/skills/triaging-jira-issues/SKILL.md +++ b/plugins/bitwarden-atlassian-tools/skills/assessing-jira-issue-relevance/SKILL.md @@ -1,14 +1,13 @@ --- -name: triaging-jira-issues -description: Use when the user provides a single Jira issue key and asks whether it is still relevant, still applicable, still pending, still a bug, has been fixed, or can be closed. Trigger phrases include "Is [TICKET] still relevant?", "Is this still an issue?", "Is PM-123 still pending?", "Has this been fixed?", "Can we close this?", "Is this ticket still valid?", "Is this still applicable?", "Does this bug still exist?". Fetches the ticket and verifies the described problem against the current codebase to return a verdict with evidence. This skill triages a single ticket at a time; invoke it iteratively for multiple tickets. +name: assessing-jira-issue-relevance +description: Use when the user provides a single Jira issue key and asks whether it is still relevant, still applicable, still pending, still a bug, has been fixed, or can be closed. Trigger phrases include "Is [TICKET] still relevant?", "Is this still an issue?", "Is PM-123 still pending?", "Has this been fixed?", "Can we close this?", "Is this ticket still valid?", "Is this still applicable?", "Does this bug still exist?". Fetches the ticket and verifies the described problem against the current codebase to return a verdict with evidence. This skill assesses a single ticket at a time; invoke it iteratively for multiple tickets. +allowed-tools: Read, Grep, Glob, AskUserQuestion, Bash(git log:*), Bash(git -C * log:*), Bash(git -C * rev-parse:*), Bash(git clone:*), mcp__plugin_bitwarden-atlassian-tools_bitwarden-atlassian__get_issue, mcp__plugin_bitwarden-atlassian-tools_bitwarden-atlassian__get_issue_comments, mcp__plugin_bitwarden-atlassian-tools_bitwarden-atlassian__get_issue_remote_links, mcp__plugin_bitwarden-atlassian-tools_bitwarden-atlassian__get_confluence_page --- -# Triaging a Jira Issue for Relevance +# Assessing a Jira Issue for Relevance Determine whether a Jira issue still applies to the current codebase. Fetch the ticket, locate the specific code path it describes, compare current behavior against the ticket's description, and return a verdict with evidence. -This skill is distinct from `researching-jira-issues`. That skill synthesizes context to _understand_ a ticket. This skill verifies whether the described problem or task still exists in code — the answer should be a clear verdict, not a summary. - ## Workflow ### Step 1: Fetch the Ticket and Its Context @@ -18,7 +17,9 @@ Use `get_issue` with `expand: ["renderedFields", "names"]`. Extract: - **The specific problem or task**: Read beyond the summary. The description, acceptance criteria, and replication steps are more precise. For bugs: what is the actual broken behavior and what is expected? For tasks: what specific code change is required? - **Technical identifiers**: Method names, class names, file paths, API endpoint routes, UI strings that appear in source, config keys, feature flag names — anything named in the ticket that can be searched in code. Note these explicitly before moving on. - **Filed date**: Used to scope `git log` searches. -- **Repo scope signal**: Determine whether this applies to `clients`, `server`, `sdk-internal`, or a combination. Use the team field, component labels, and description content (see Scope Notes below). +- **Repo scope evidence**: Gather what the ticket says about where the code lives, and note how strong that evidence is. Do not settle on a repo here — Step 2 decides. + - _Strong_: a literal repo name, a `github.com/bitwarden/` URL, or a linked PR/commit. + - _Weaker_: team field, component labels, language or platform keywords, file paths in the description. Also note these **staleness signals** from the ticket fields before moving on: @@ -32,7 +33,35 @@ After fetching the ticket, always do both of the following: **Fetch linked issues** (`get_issue_remote_links` and the `issuelinks` field): Look specifically for blocking relationships — issues this ticket blocks or is blocked by. A ticket blocked by unresolved work may not be actionable yet; a blocker that has since been resolved may mean this ticket is now ready. Fetch (`get_issue`) any directly linked issues to check their current status and extract additional technical context. Do not traverse more than one level deep. -### Step 2: Build Search Targets +### Step 2: Establish Repo Scope + +Settle which repositories are in scope and confirm they are readable **before** searching anything. Do not begin Step 3 until all three checks below pass. + +Bitwarden has many repositories — `clients`, `server`, `sdk-internal`, `sdk-sm`, `android`, `ios`, `mcp-server`, and others. Treat the candidate set as open; never assume a ticket must belong to one of the repos you have seen before. + +**1. Determine the repos.** + +If the ticket carries strong evidence (a literal repo name, a `github.com/bitwarden/` URL, or a linked PR/commit), use it and move on. + +Otherwise, infer the most likely repo(s) from the weaker signals and **present that inference for confirmation** with `AskUserQuestion`. State what you inferred and the signal it rests on, offer the plausible alternatives you considered, and allow multiple selections — tickets legitimately span repos. Never search a repo the user has not confirmed. + +**2. Resolve each repo to a path.** + +Use the current working directory if it is that repo; otherwise look for a sibling directory of the same name; otherwise ask for the path. Confirm each resolved path is a real checkout: + +``` +git -C rev-parse --show-toplevel +``` + +**3. Verify the repo is cloned, and stop if it is not.** + +If a repo in scope has no checkout on disk, ask whether to clone it. On approval, clone it and continue. + +**If the user declines to clone, stop immediately.** Return no verdict. State which repo was unavailable and that the assessment could not be completed. This halt applies even when other repos in scope are present — do not assess the available half and do not downgrade to a weaker verdict. Searching a repo that is not on disk returns no matches, which is indistinguishable from the code having been removed; proceeding would produce a confident "No longer relevant" on a ticket that is still live. + +This halt is distinct from the **Cannot determine** verdict in Step 5. "Cannot determine" means the ticket was too vague to trace. This means the evidence was never accessible. + +### Step 3: Build Search Targets From the ticket, identify 2–5 specific identifiers to search for in code. Prioritize: @@ -44,9 +73,13 @@ From the ticket, identify 2–5 specific identifiers to search for in code. Prio If the ticket names no specific identifiers, derive them from the described behavior: what function would implement this, what component would render this UI, what endpoint would serve this request? -### Step 3: Search the Code +### Step 4: Search the Code + +Run searches in the repo(s) confirmed in Step 2. + +First, **orient yourself in each repo**: read its `CLAUDE.md` and `README` to find the source roots, module layout, and test locations. Do this rather than relying on remembered directory names — layouts differ per repo and change over time. -Run searches in the relevant repo(s): +Then: 1. **Grep for each identifier** in the relevant source directories. Don't stop at confirming existence — read the surrounding code to understand current behavior. A symbol that still exists but now behaves differently may mean the bug is already fixed. @@ -62,7 +95,7 @@ Run searches in the relevant repo(s): 4. **Trace refactored paths**: If a named symbol no longer exists, find what replaced it. A deleted method does not mean the bug is fixed — the logic may have moved. Search for the behavior, not just the original name. -### Step 4: Deliver Verdict +### Step 5: Deliver Verdict Compare what the ticket describes against what the code does today. Reach a conclusion. @@ -76,30 +109,6 @@ Compare what the ticket describes against what the code does today. Reach a conc **Format**: Lead with the verdict and its justification in plain prose. Cite `file:line` references as evidence. If still relevant, state what specifically remains to be done — do not just restate the ticket. If staleness signals are present even for a "Still relevant" verdict, note them at the end: ticket age, epic completion state, priority, and assignee. Keep it tight; a verdict paragraph with supporting evidence is sufficient. -## Scope Notes - -Use these to determine which repo and directories to search: - -**Server repo** (`server/src/`): - -- Team field (including but not limited to): Billing, Auth (server-side), Admin Console (server-side) -- Keywords in description: C#, .NET, API endpoint, stored procedure, Stripe, webhook, IdentityServer, database -- Key directories: `src/Identity/IdentityServer/` (auth/token flow), `src/Core/Billing/` and `src/Identity/Billing/` (billing), `src/Core/Services/Implementations/` (user/org services), `src/Api/` (REST controllers) - -**Clients repo** (`clients/`): - -- Team field (including but not limited to): Key Management, Browser, Desktop, Admin Console (frontend), Vault -- Keywords in description: Angular, TypeScript, browser extension, desktop app, web vault, UI component, form -- Key directories: `apps/browser/src/` (extension), `apps/desktop/src/` (desktop), `apps/web/src/` (web vault), `libs/key-management-ui/src/lock/` (shared lock/unlock UI), `libs/key-management/src/` and `libs/auth/src/` (shared auth) - -**SDK repo** (`sdk-internal/crates/`): - -- Team field (including but not limited to): SDK, Platform -- Keywords in description: Rust, crate, SDK, FFI, bitwarden-crypto, bitwarden-auth, bitwarden-core -- Key crates: `bitwarden-crypto/` (encryption), `bitwarden-auth/` (authentication), `bitwarden-core/` (core types), `bitwarden-ffi/` (cross-platform bindings) - -**Multiple repos**: Auth and key management tickets often span clients and server. Vault timeout, biometrics, and unlock flow bugs commonly touch both `clients` and `sdk-internal`. Start with the repo the team field suggests, then check the other if the first shows only half the picture. - ## What NOT to Do - Don't traverse linked issues more than one level — fetch directly linked issues (blocks, is blocked by, parent epic) but do not follow their links further @@ -111,6 +120,6 @@ Use these to determine which repo and directories to search: ## Examples -### examples/triage_workflow.md +### examples/relevance_assessment_workflow.md -Three worked examples: a bug where the described code path was silently refactored away, a task whose implementation gap is confirmed present, and a spike made obsolete by later work. +Four worked examples: a bug where the described code path was silently refactored away, a task whose implementation gap is confirmed present, a spike made obsolete by later work, and a ticket whose repo is not cloned locally, where the skill halts without a verdict. diff --git a/plugins/bitwarden-atlassian-tools/skills/triaging-jira-issues/examples/triage_workflow.md b/plugins/bitwarden-atlassian-tools/skills/assessing-jira-issue-relevance/examples/relevance_assessment_workflow.md similarity index 65% rename from plugins/bitwarden-atlassian-tools/skills/triaging-jira-issues/examples/triage_workflow.md rename to plugins/bitwarden-atlassian-tools/skills/assessing-jira-issue-relevance/examples/relevance_assessment_workflow.md index 8919362d..814e3c6a 100644 --- a/plugins/bitwarden-atlassian-tools/skills/triaging-jira-issues/examples/triage_workflow.md +++ b/plugins/bitwarden-atlassian-tools/skills/assessing-jira-issue-relevance/examples/relevance_assessment_workflow.md @@ -1,6 +1,6 @@ -# Example: Triage Workflow +# Example: Relevance Assessment Workflow -Three representative cases showing how the verdict changes depending on what the code investigation finds. +Four representative cases: three showing how the verdict changes depending on what the code investigation finds, and one showing the skill halting before any investigation happens. --- @@ -12,7 +12,7 @@ Three representative cases showing how the verdict changes depending on what the **Workflow**: -1. Fetch PM-XXXX via `get_issue` → extract the described behavior and identifiers: `TryGetValue`, `DistributedCacheExtensions`, `cache.Get`. +1. Fetch PM-XXXX via `get_issue` → extract the described behavior and identifiers: `TryGetValue`, `DistributedCacheExtensions`, `cache.Get`. The ticket links a commit in `bitwarden/server`, so repo scope is explicit — no confirmation needed. `server` resolves to a sibling checkout. 2. Grep in `server/src/` → `DistributedCacheExtensions.cs` exists, `TryGetValue` present. 3. Read the file → the method exists but has **zero callers** in the codebase — the code path described by the ticket is dead. 4. `git log` on the file → confirms no callers were added recently; the extension was made obsolete by a service refactor. @@ -29,7 +29,7 @@ Three representative cases showing how the verdict changes depending on what the **Workflow**: -1. Fetch PM-YYYY via `get_issue` → identifiers extracted: `validateCanManagePermission`, `managePermissionRequired`, `AccessItemView`, `readonly`. +1. Fetch PM-YYYY via `get_issue` → identifiers extracted: `validateCanManagePermission`, `managePermissionRequired`, `AccessItemView`, `readonly`. No repo is named; the Angular component names and web-vault framing point to `clients`. Propose `clients` with that reasoning, user confirms. 2. Grep in `clients/apps/web/src/` → `validateCanManagePermission` found in `collection-dialog.component.ts:564` and wired into the form at lines 331–333. The i18n key `managePermissionRequired` resolves to "At least one member or group must have can manage permission." 3. Read `collection-dialog.component.html` → an error div shows the `managePermissionRequired` message when the validator fires — the error-on-submit path is what's there. 4. Check `AccessItemView` model → has a `readonly` flag that disables the permission editor and keeps the row selected, but no logic in the component or template computes "this is the last Manage-permission row → set `readonly: true`." @@ -47,9 +47,28 @@ Three representative cases showing how the verdict changes depending on what the **Workflow**: -1. Fetch PM-ZZZZ via `get_issue` → identifiers extracted: `DisablePremiumAsync`, `PremiumExpirationDate`, `Premium = false`. +1. Fetch PM-ZZZZ via `get_issue` → identifiers extracted: `DisablePremiumAsync`, `PremiumExpirationDate`, `Premium = false`. The ticket cites `UserService.cs` but names no repo — a file path alone is weak evidence, so propose `server` and confirm before searching. 2. Grep in `server/src/` → `DisablePremiumAsync` still present at `UserService.cs:894`; sets `user.Premium = false` with `user.PremiumExpirationDate = expirationDate` — same as described. 3. Search how `Premium` is used for access control → `ValidateUserPremiumAsync` in `LicensingService.cs` returns `user.Premium` directly for cloud users. No check on `PremiumExpirationDate`. 4. Find a more recent `UserPremiumAccessView` SQL view → `PersonalPremium` mapped directly from `U.[Premium]`; expiration date not consulted. **Verdict**: No longer relevant as an open investigation. The inconsistency (`Premium = false` with a future `PremiumExpirationDate`) still exists in the data, but later billing system work answered the spike's question in code: `PremiumExpirationDate` is billing lifecycle metadata and plays no role in access gating on cloud — `user.Premium` is the sole gate. The data inconsistency has no user-facing impact. Safe to close with a note documenting this as intentional design. + +--- + +## Case 4: Required repo not available (Halt, no verdict) + +**User request**: "Is PM-WWWW still relevant?" + +**Ticket**: Bug: on Android, the autofill accessibility service stops offering suggestions after the device is rotated while the inline suggestion sheet is open. + +**Workflow**: + +1. Fetch PM-WWWW via `get_issue` → identifiers extracted: `AutofillService`, inline suggestion, configuration change. No repo named; the platform framing points to `android`. Propose `android` with that reasoning, user confirms. +2. Resolve `android` to a path → not the current working directory, and no sibling directory of that name exists. `git -C ../android rev-parse --show-toplevel` fails. +3. Ask whether to clone `bitwarden/android`. User declines. +4. **Stop.** No searches are run. + +**Outcome**: No verdict. Report that the assessment could not be completed because `android` is not available locally and cloning was declined. + +Note what is deliberately _not_ done here: the skill does not fall back to grepping whatever repos happen to be on disk, and does not return "Cannot determine". Grepping an absent repo yields zero matches, which looks exactly like the code having been deleted — enough to produce a confident "No longer relevant" on a live bug. The same halt applies if the ticket had spanned `android` and `clients` and only `android` were missing: a partial search is not downgraded to a weaker verdict, it is refused. From 07c58f1e85839c6e08435a1fd36f43cdca7dad26 Mon Sep 17 00:00:00 2001 From: Patrick Honkonen Date: Fri, 24 Jul 2026 17:59:46 -0400 Subject: [PATCH 5/5] Fix prettier formatting in README table and plugin manifest --- README.md | 2 +- .../bitwarden-atlassian-tools/.claude-plugin/plugin.json | 9 +-------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 45b7c480..8debf15d 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ A curated collection of plugins for AI-assisted development at Bitwarden. Enable | [bitwarden-ai-telemetry](plugins/bitwarden-ai-telemetry/) | 1.0.0 | Claude Code hooks emitting metadata-only AI-usage telemetry (identity, git-linkage, MCP) via OTLP | | [bitwarden-tech-lead](plugins/bitwarden-tech-lead/) | 2.3.2 | Tech lead for technical planning, architecture coherence, and surfacing patterns to Technical Strategy Ideas | | [bitwarden-shepherd](plugins/bitwarden-shepherd/) | 1.0.0 | Champion of a technical strategy — shepherds a TSI through evaluation into the funnel, then through to adoption | -| [bitwarden-atlassian-tools](plugins/bitwarden-atlassian-tools/) | 2.4.0 | Read-only Atlassian access via MCP server with deep Jira issue research skill | +| [bitwarden-atlassian-tools](plugins/bitwarden-atlassian-tools/) | 2.4.0 | Read-only Atlassian access via MCP server with deep Jira issue research skill | | [bitwarden-code-review](plugins/bitwarden-code-review/) | 1.13.1 | Autonomous code review agent following Bitwarden engineering standards with GitHub integration | | [bitwarden-delivery-tools](plugins/bitwarden-delivery-tools/) | 2.1.0 | Delivery lifecycle skills: initiative funnel navigation, work transitions, tech breakdowns and task decomposition, commits, PRs, preflight, labeling | | [bitwarden-designer](plugins/bitwarden-designer/) | 0.1.0 | Product designer persona: Code of Conduct and 30/60/90 critique, critique facilitation; dispatches into bitwarden-design-tools | diff --git a/plugins/bitwarden-atlassian-tools/.claude-plugin/plugin.json b/plugins/bitwarden-atlassian-tools/.claude-plugin/plugin.json index 2539567d..379c417d 100644 --- a/plugins/bitwarden-atlassian-tools/.claude-plugin/plugin.json +++ b/plugins/bitwarden-atlassian-tools/.claude-plugin/plugin.json @@ -5,12 +5,5 @@ "author": { "name": "Bitwarden" }, - "keywords": [ - "atlassian", - "jira", - "confluence", - "mcp", - "read-only", - "cql" - ] + "keywords": ["atlassian", "jira", "confluence", "mcp", "read-only", "cql"] }