Test engineer plugin current coverage#152
Conversation
# Conflicts: # .cspell.json
Plugin Validation Summary —
|
| Check | Result |
|---|---|
plugin.json JSON validity & required fields (name, version, description) |
✅ valid |
Semantic version 1.0.0 |
✅ valid |
| Name kebab-case, matches directory | ✅ bitwarden-test-engineer |
Name/version consistency — plugin.json ↔ marketplace.json:97-100 ↔ CHANGELOG.md:7 |
✅ consistent |
Auto-discovery layout (skills/<name>/SKILL.md) |
✅ correct |
README.md present & comprehensive |
✅ |
CHANGELOG.md Keep-a-Changelog format |
✅ |
| No stray/unnecessary files | ✅ |
| Hardcoded credentials scan | ✅ clean |
No commands, agents, hooks, or self-declared MCP servers — matches the stated single-skill scope.
2. Skill Review (skill-reviewer) — PASS
Skill: assessing-test-coverage
- Frontmatter:
name+descriptionpresent and valid.allowed-toolsis tightly scoped (no blanketBash). - Description: strong — four concrete quoted trigger phrases, third-person, explicit scope boundary ("does NOT recommend new tests").
- Content: SKILL.md is lean (~770 words); detail correctly delegated to four
references/files (~4,100 words total). Exemplary progressive disclosure — each workflow step names the reference subsection it defers to. - Referenced files: all four
references/*.mdexist on disk; all inter-file links resolve.templates/breakdown.md,tasks.md, andcoverage.mdmentions correctly point to the externalbitwarden/tech-breakdownsrepo / runtime-generated output, not local files — not broken references.
Note — false positive corrected: the skill-reviewer flagged a stray
</output>tag on "line 47" of SKILL.md. Direct inspection (wc -l+tail) confirms the file is 46 lines and ends cleanly with the final Principles bullet. The</output>was the Read-tool output wrapper, not file content. No fix needed.
3. Security Validation (claude-config-validator) — PASS
| Check | Result |
|---|---|
| Committed secrets / API keys / tokens / passwords | ✅ none (only benign phrase "token spend" in README/finding-coverage) |
| Hardcoded credentials | ✅ none |
| Private keys / AWS keys / Bearer tokens | ✅ none |
| Dangerous command auto-approvals | ✅ none — allowed-tools Bash(...) patterns are all narrowly scoped (gh pr view:*, git -C * rev-parse:*, etc.) |
| Overly broad file access | ✅ none — least-privilege; no Bash(*) |
settings.local.json committed |
✅ n/a (no settings files in this plugin) |
Notable positive: the skill embeds explicit prompt-injection defense — SKILL.md:46 and references/input-sources.md:6-10 instruct treating all ingested Jira/Confluence/PR/CSV content as untrusted data and to flag instruction-like text as a CWE-1427 concern rather than acting on it.
Advisory (optional, no action required)
- Minor ·
SKILL.md:4—allowed-toolsreferences cross-plugin tools frombitwarden-atlassian-tools(aSkill(...)call and 5mcp__...tools). This is intentional and documented (README "Cross-Plugin Integration" notes graceful degradation when that plugin is absent). Flagged only to make the optional dependency explicit. - Optional · No
examples/directory. The inline JSON record infinding-coverage.md:110-140and the template incoverage-report-template.mdlargely cover this; a filled-incoverage.mdsample could be a future enhancement.
Recommendation: APPROVE. No blocking issues. Structure, manifest, skill quality, reference integrity, version consistency, and security all pass.
theMickster
left a comment
There was a problem hiding this comment.
A few things to change, all feel minor other that how we approach Tech Breakdowns. Great start @nthompson-bitwarden!
|
|
||
| | Plugin | How It's Used | | ||
| | --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| | `bitwarden-atlassian-tools` | Optional but recommended. Provides the `mcp__plugin_bitwarden-atlassian-tools_bitwarden-atlassian__*` server used to read Jira tickets and linked Confluence requirements. If absent, the plugin degrades gracefully — paste requirements or rely on the PR/CSV/description. | |
There was a problem hiding this comment.
❓ Do we have any reason to make our Atlassian tooling required? I would really love to see our Claude Code + testing efforts driven from directions in our Jira + Testmo tickets (if possible) 🤞🏼.
There was a problem hiding this comment.
Good question - I've kept the Atlassian tooling optional, but reframed it as the recommended driver in the README, and is already steered towards that direction in the reference files.
Ideally the skill is used early on in the SDLC - which would support making atlassian tools required - but it was purposefully designed to be used at any point in the process, even far after feature work is completed.
It could be also be used for assessing test coverage for Community PRs (which supports adding verbiage to resolve the other comment re: untrusted input boundaries).
That said, I fully agree with the direction. Perhaps the additive input sources are too broad/open-ended, which leads to variance in use, which might produce suboptimal determinism in its output.
For example, if QA has already written test cases for a feature - adding those test cases to the input along with the Jira Epic, will likely influence the output compared to if the skill was run before the test cases were written. 🤔
| ## Technical breakdown document | ||
|
|
||
| A Bitwarden **Tech Breakdown** — the Confluence artifact a team produces before implementation, | ||
| authored with the `bitwarden-delivery-tools:writing-tech-breakdowns` skill. It is the richest |
There was a problem hiding this comment.
❌ This skill has been removed so we need to refactor how we tackle referencing our tech breakdowns.
@trmartin4 do you think you could give us a hand here with some wording/phrasing? We have a challenge because we have the goal of tech breakdowns in a GitHub repo, but also still have them in Confluence that our new bitwarden-test-engineer plugin is going to need to work with.
I think we also want to be careful on how much we rely upon the tech breakdowns as a source of truth. While they are a source of truth for a period of time, once Jira tickets are crafted and code is created then we need to recognize and steer Claude more towards those and less at the tech breakdown. Correct? Or am I missing something?
There was a problem hiding this comment.
📓 I think the reference concern is moot in this PR. regardless of what the new name or entry point is for the process that generates a tech breakdown, it can be removed entirely since it's not a true invocation. It's not really necessary to elaborate on where the tech breakdown came from.
There was a problem hiding this comment.
Correct on both points.
We'd want to point this skill at the template in https://github.com/bitwarden/tech-breakdowns/tree/main/templates for contents, and the corresponding breakdown documents for each team, versus the Confluence space.
To the point about timing, I agree and I think it depends on when in the SDLC this skill is intended to be used. The intent is for completed breakdowns (i.e. the code has been implemented and is now the source of truth) will be moved to /complete subfolders for each team, so you could pretty confidently add open PRs and merged breakdowns not complete into your input sources, but guide Claude to preference the implementation over the breakdown if they conflict.
SaintPatrck
left a comment
There was a problem hiding this comment.
Great start! In addition to the inline comments, I think it would be beneficial for everyone if we go ahead and introduce trigger evals, at a minimum, so we can objectively measure effectiveness of these additions and future changes. #126 is an example of basic evals for a design based skill. The creating-pull-requests skill in delivery-tools plugin also has some basic evals you can use as a reference. Happy to sync up outside of this PR and talk through setting them up using /skill-creator if you'd like.
| "name": "bitwarden-test-engineer", | ||
| "source": "./plugins/bitwarden-test-engineer", | ||
| "version": "1.0.0", | ||
| "description": "Test engineering toolkit for Bitwarden. Inventories the test coverage a change already has — finding existing tests across the server, client, and mobile repos, bucketing each by layer (unit, integration, E2E), citing it as a stable GitHub permalink, and flagging untested behaviors as gaps in a markdown report. Designed to grow additional testing capabilities over time." |
There was a problem hiding this comment.
💭 Since this will eventually turn into a collection of SDET tools, I'm wondering if the description and plugin name should be more generic so we don't have to constantly update it when new tools or capabilities are added. bitwarden-sdet-tools, maybe?
There was a problem hiding this comment.
I like the idea of making the description/plugin name more generic. But I feel like SDET is too scoped. Software engineer should feel free to make use of the plugin as well as management, etc... 🤔
| | [bitwarden-product-analyst](plugins/bitwarden-product-analyst/) | 0.1.5 | Product analyst agent for creating comprehensive Bitwarden requirements documents from multiple sources | | ||
| | [bitwarden-security-engineer](plugins/bitwarden-security-engineer/) | 1.2.0 | Application security engineering: vulnerability triage, threat modeling, and secure code analysis | | ||
| | [bitwarden-software-engineer](plugins/bitwarden-software-engineer/) | 1.0.0 | Software engineer agent for a Bitwarden product team. Implements stories, tasks, and bugs with code quality, performance, security, and team comms in mind. | | ||
| | [bitwarden-test-engineer](plugins/bitwarden-test-engineer/) | 1.0.0 | Test engineering toolkit: role-specific testing agents spanning the test lifecycle, starting with risk-weighted test strategy and coverage planning. | |
There was a problem hiding this comment.
⛏️ This could also be more generic to avoid future maintenance.
| "name": "bitwarden-test-engineer", | ||
| "source": "./plugins/bitwarden-test-engineer", | ||
| "version": "1.0.0", | ||
| "description": "Test engineering toolkit for Bitwarden. Inventories the test coverage a change already has — finding existing tests across the server, client, and mobile repos, bucketing each by layer (unit, integration, E2E), citing it as a stable GitHub permalink, and flagging untested behaviors as gaps in a markdown report. Designed to grow additional testing capabilities over time." |
There was a problem hiding this comment.
⛏️ I don't think this trailer statement is necessary.
Designed to grow additional testing capabilities over time.
| "description": "Test engineering toolkit for Bitwarden. Inventories the test coverage a change already has — finding existing tests across the server, client, and mobile repos, bucketing each by layer (unit, integration, E2E), citing it as a stable GitHub permalink, and flagging untested behaviors as gaps in a markdown report. Designed to grow additional testing capabilities over time." | |
| "description": "Test engineering toolkit for Bitwarden. Inventories the test coverage a change already has — finding existing tests across the server, client, and mobile repos, bucketing each by layer (unit, integration, E2E), citing it as a stable GitHub permalink, and flagging untested behaviors as gaps in a markdown report." |
| { | ||
| "name": "bitwarden-test-engineer", | ||
| "version": "1.0.0", | ||
| "description": "Test engineering toolkit for Bitwarden. Inventories the test coverage a change already has — finding existing tests across the server, client, and mobile repos, bucketing each by layer (unit, integration, E2E), citing it as a stable GitHub permalink, and flagging untested behaviors as gaps in a markdown report. Designed to grow additional testing capabilities over time.", |
There was a problem hiding this comment.
⛏️ Same unnecessary trailer statement repeated here.
Designed to grow additional testing capabilities over time.
| "description": "Test engineering toolkit for Bitwarden. Inventories the test coverage a change already has — finding existing tests across the server, client, and mobile repos, bucketing each by layer (unit, integration, E2E), citing it as a stable GitHub permalink, and flagging untested behaviors as gaps in a markdown report. Designed to grow additional testing capabilities over time.", | |
| "description": "Test engineering toolkit for Bitwarden. Inventories the test coverage a change already has — finding existing tests across the server, client, and mobile repos, bucketing each by layer (unit, integration, E2E), citing it as a stable GitHub permalink, and flagging untested behaviors as gaps in a markdown report.", |
| @@ -0,0 +1,46 @@ | |||
| --- | |||
| name: assessing-test-coverage | |||
| description: Use when determining what test coverage ALREADY exists for a change — citing each covering test as a GitHub permalink bucketed by test layer, and flagging behaviors with no observed test as gaps. Triggers on "what's already tested", "does this PR have tests", "what coverage exists for", or "is this component covered". This is a backward-looking inventory of existing coverage — it does NOT recommend new tests or assign cheapest-sufficient layers. | |||
There was a problem hiding this comment.
🎨
"citing each covering test as a GitHub permalink bucketed by test layer, and flagging behaviors with no observed test as gaps"
This is describing output, not when the skill should be used. Unless it was added to cover missed triggers you encountered, consider stripping it.
Trigger evals would help determine if it is adding value or not. Consider having /skill-creator build trigger evals so we can measure effectiveness now and going forward.
| name: assessing-test-coverage | ||
| description: Use when determining what test coverage ALREADY exists for a change — citing each covering test as a GitHub permalink bucketed by test layer, and flagging behaviors with no observed test as gaps. Triggers on "what's already tested", "does this PR have tests", "what coverage exists for", or "is this component covered". This is a backward-looking inventory of existing coverage — it does NOT recommend new tests or assign cheapest-sufficient layers. | ||
| allowed-tools: "Read, Write, Grep, Glob, AskUserQuestion, Bash(gh pr view:*), Bash(gh pr diff:*), Bash(git rev-parse:*), Bash(git remote get-url:*), Bash(git -C * rev-parse:*), Bash(git -C * remote get-url:*), Skill(bitwarden-atlassian-tools:researching-jira-issues), 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, mcp__plugin_bitwarden-atlassian-tools_bitwarden-atlassian__search_issues, mcp__plugin_bitwarden-atlassian-tools_bitwarden-atlassian__search_confluence, mcp__plugin_bitwarden-atlassian-tools_bitwarden-atlassian__search_confluence_cql" | ||
| --- |
There was a problem hiding this comment.
🎨 Can use arguments and argument-hint to provide cues for users about what they can pass in when invoking the skill.
It also allows reference within the skill using $ARGUMENTS[i] or $argName.
See https://code.claude.com/docs/en/skills#frontmatter-reference for details on arguments and argument-hint usage.
|
|
||
| ## Output file | ||
|
|
||
| Write to `test-engineer-report-<slug>-<date>/coverage.md`: |
There was a problem hiding this comment.
💭 This can result in files saved to different locations, depending on where the Claude session is started and its CWD. What do you think about having a single, consistent output location that doesn't run the risk of being picked up by Git? $CLAUDE_PLUGIN_DATA points to the plugin installation directory (~/.claude/plugin-data/<plugin-name>/), so maybe something like ${CLAUDE_PLUGIN_DATA}/<slug>-<date>-coverage.md? Thoughts?
| Write to `test-engineer-report-<slug>-<date>/coverage.md`: | ||
|
|
||
| - `<slug>` — a kebab-case slug for the change (from the ticket key, PR number, or feature name). | ||
| - `<date>` — today's date as `YYYY-MM-DD`, **supplied by the caller** (skills can't read the clock). |
There was a problem hiding this comment.
❓ Where does the assertion that "skills can't read the clock" come from? I use date format in slugs for generated files and regularly see Claude add date and time stamps without issue. My gut says this is not accurate and should be removed, but worth double checking.
| are scope-only and any coverage is prospective. Surface this in the report's Evidence. | ||
| 5. **Preferred path.** The `researching-jira-issues` skill (preferred at the top of this file) does | ||
| this hierarchical discovery and depth-controlled traversal in one synthesized read — run it on the | ||
| epic key; the direct MCP calls above are the fallback. |
There was a problem hiding this comment.
🎨 This is re-iterating the steps defined in researching-jira-issues, and we already noted that the Skill and MCP server are bundled together. What do you think about rephrasing it to something along the lines of...
### Epic intake
When `issuetype` is `Epic` or `Feature`, the testable behaviors live on the children and their PRs, not on the epic body. Run `Skill(bitwarden-atlassian-tools:researching-jira-issues)` on the epic key; it does child discovery, depth-controlled fan-out, and PR-link traversal in one read. The direct MCP calls (`get_issue` per child, `get_issue_remote_links` for PRs) serve targeted lookups when you do not need the full synthesized read.
Carry three things forward that are specific to this analysis:
- **Source key per behavior.** A behavior from a child links to that child, not the epic.
- **PRs are the coverage backbone.** Each child's linked PRs carry the tests that shipped, permalink-ready via the head SHA. If `gh` cannot reach one, record it as evidence-not-inspected rather than dropping it.
- **Epic status bounds expectations.** `Done` children likely have tests-in-PR to audit; `To Do` children are scope-only. Note it in Evidence.There was a problem hiding this comment.
♻️ A local multi-agent code review raised the following finding.
16 of 21 added cspell words are dead entries — present in no shipped file
.cspell.json
Caught by: Code quality agent / Architecture agent
Surfaced by: both (opus + sonnet)
cmon-bro verdict: CONFIRMED — 5 words present in new files, 16 with 0 hits in new files and 0 on main
Details
This PR adds 21 words to the repo-wide cspell dictionary; only 5 (mockall, Robolectric, stylesheet, unfound, unlinkable) appear in any shipped file. The other 16 appear in no shipped file and — verified by whole-word grep across main — in no pre-existing repo file either:
actioned, automatable, Consolas, detekt, getline, inlines, ktlint, Menlo, nextest, SDET, Segoe, subdirs, tablist, tabpanel, tnum, XCUI.
These are residue from the larger PR #150 (Android/iOS/Rust tooling and UI/CSS terms from dropped content); the dictionary was not slimmed alongside the files. Repo CLAUDE.md guidance is to add domain terms only when they trip cspell on shipped content. Dead entries pollute the shared allowlist and silently accept future misspellings that collide with these tokens.
Suggested fix: drop the 16 unused words; keep only the 5 the shipped files use.
🎟️ Tracking
https://bitwarden.atlassian.net/browse/QA-1983
📔 Objective
Slimmed down version of #150 containing only files necessary to get a "current test coverage" report