Skip to content

feat(bitwarden-testing-tools): add context building and service scoping to the web test pipeline - #210

Open
kdenney wants to merge 73 commits into
mainfrom
add/testing-tools-context-and-scoping
Open

kdenney wants to merge 73 commits into
mainfrom
add/testing-tools-context-and-scoping

Conversation

@kdenney

@kdenney kdenney commented Aug 21, 2026 •

Copy link
Copy Markdown
Contributor

🎟️ Tracking

No ticket. Part of the stacked series building the bitwarden-testing-tools Playwright web test pipeline. This PR is the context-building and service-scoping layer, following #209 (merged).

📔 Objective

Adds the planning layer of the web test pipeline: the components that turn a feature reference into the grounded context and service set the later stages consume.

  • Two skills: scoping-playwright-application-context builds a state-centric Application Context (states, flows, and any feature flags the run depends on) from the changed code, drawing on per-domain catalogs of known flows, and mapping-services-under-test resolves which local services a change needs.
  • Three planning-phase agents, each independently invocable: a context gatherer that acquires the feature source from a Jira ticket, plan file, or description, plus thin wrappers for the two skills. They pass fenced markdown artifacts to each other, and the caller supplies the changed files as a diff artifact (scripts/repo-diff.sh lists them).
  • A PreToolUse hook confines each agent to its own skill, and a shared untrusted-source policy tells them to treat anything they read as data, never instructions.
  • references/playwright-tool-policy.md, the shared tool boundary for the pipeline, with Mailcatcher and Stripe handled by skills in their own plugins, declared as dependencies.
  • Advice-only behavior evals and a trigger suite for both skills (neither yet run), plus the 1.3.0 version bump, changelog, and README and cspell updates.

@kdenney
kdenney force-pushed the add/testing-tools-context-and-scoping branch from 2465025 to ce465ed Compare August 22, 2026 03:28
@github-actions

github-actions Bot commented Aug 22, 2026 •

Copy link
Copy Markdown

Claude Code validation

Result: Pass

Re-ran against the pull request's original merge base 0e684b9 (head now 1c308c2) after the author pushed 1c308c2 to address the previous round's four findings. All four are fixed, and the fixes are substantive rather than cosmetic — the agents manifest key, the seventh self-review check, the dev master password literal, and the two missing trigger suites all check out on disk. No new findings were hunted against the fix commit, per the re-run rule; one newly surfaced SUGGESTED nit on original (non-fix) changeset content is listed below, and two candidate findings were dropped against the official documentation. No critical findings, no security weakening, and the 1.2.0 → 1.3.0 bump remains consistent across all four required files with a matching [1.3.0] changelog entry that now also documents this round's changes.

Not covered: The hook unit suite could not be executed — every python3 invocation was denied by the sandbox, so hooks/restrict_planning_agents.py and its 218-line test file were reviewed statically only (the five lines 1c308c2 touched are docstring prose; no code changed, the first/last-segment matcher still agrees with every assertion in the test file, and main() still fails closed on RecursionError and on any decide() exception for restricted agents). validate-plugin-structure.sh, validate-marketplace.sh, and validate-version-bump.sh are not run here — the workflow runs them as dedicated steps before this review; see their own check statuses.

Findings
  • 🎨 SUGGESTED: Finding 1: mapping-services-under-test/SKILL.md is the only skill in the plugin whose body opens with prose instead of an H1 title (plugins/bitwarden-testing-tools/skills/mapping-services-under-test/SKILL.md:8)

    Details and fix

    All four sibling skills in this plugin open with an H1 at line 8 — scoping-playwright-application-context/SKILL.md:8 (# Scoping the Playwright Application Context), recommending-test-layers/SKILL.md:8, assessing-test-coverage/SKILL.md:8, and writing-manual-test-cases/SKILL.md:8. This file goes straight from the frontmatter fence to the body paragraph.

    Nothing breaks: no H1 is required, the skill loads and triggers normally, and the frontmatter name is what Claude Code uses. It is purely a side-by-side readability inconsistency.

    Raised in the interest of full disclosure rather than as a request: this surfaced only in this round, on content the changeset introduced in its original state (the fix commit 1c308c2 did not touch this file), after three previous rounds passed over it. It carries no severity beyond cosmetic, so closing the pull request without it is entirely reasonable.

    Fix — insert a title and a blank line before the current line 8:

    # Mapping Services Under Test

Dropped after documentation check: two candidate findings the checks raised were cleared against the official documentation and are absent above.

  • plugins/bitwarden-testing-tools/.claude-plugin/plugin.json:11 dependencies — raised as IMPORTANT on the grounds that the field is not in the manifest schema and that the README therefore oversells it. The plugin manifest reference documents it in the Fields table — "| dependencies | Array of strings or objects | Plugins that must be enabled for this one to work |" — and gives it a section: "Plugins that must be enabled for this one to work. Each entry is \"name\", \"name@marketplace\", or { \"name\": \"...\", \"marketplace\": \"...\", \"version\": \"...\" }. Bare names resolve against this plugin's own marketplace." Both named plugins are entries in this same marketplace, so the bare names resolve, and the defaultEnabled section adds "A plugin that an enabled plugin depends on starts enabled regardless", which backs the README's wording. The check was reading a bundled offline copy of the schema, not the live reference.
  • plugins/bitwarden-testing-tools/agents/playwright-application-context-scoper/AGENT.md:27 color: purple — the sub-agents reference: "| color | No | Display color for the subagent in the task list and transcript. Accepts red, blue, green, yellow, purple, orange, pink, or cyan |". purple is accepted. The warning originates in the plugin-dev validator's own narrower six-colour list, which is out of step with the documentation; the file is correct as written. (Both of these were also dropped on the same grounds last round.)
Previous findings — re-check
# Previous finding Status
1 ⚠️ IMPORTANT — no agents manifest key, so each agent loaded under a doubled scoped name (plugin.json:11) Fixed
2 🎨 SUGGESTED — terminal self-review had no check that catalog-only fields were dropped (scoping.../SKILL.md:157) Fixed
3 🎨 SUGGESTED — dev master password literal inline in flow Steps and parameter lines (known-flows/auth.md:50) Fixed
4 🎨 SUGGESTED — no trigger-eval.json and no documented rationale for its absence (mapping.../evals/README.md:16) Fixed

Finding 1. plugin.json:12-16 now declares all three agents; every path resolves on disk and the manifest is valid JSON. The documentation settles the consequence in the fix's favour: "Replaces the default: commands, agents, outputStyles, workflows, experimental.themes" — so the agents/ directory scan no longer runs and the doubled alias is not registered at all. Each file's frontmatter name matches its directory, giving exactly bitwarden-testing-tools:<agent>, the bare form README.md and the agents' cross-references already used. The three paths also sit inside the default agents/ folder, which is the documented way to avoid the Default agents/ folder is ignored because the manifest sets "agents" warning. One subagent reported the opposite — that agents supplements the scan — and the live reference contradicts it; the clean name is the only one produced.

Finding 2. scoping-playwright-application-context/SKILL.md:158 now reads "7. Catalog-only fields dropped. No emitted state or flow carries a **Select only when:** or **Sources:** line." Both field names are covered, and the check is correctly scoped to the plural **Sources:** flow bullet without colliding with the singular Source: citation line inside verification points, which must survive. It matches the rule at SKILL.md:18 and the two verbatim gradings in evals/behavior-eval.json.

Finding 3. The literal now appears exactly four times, each in a trailing **Note:** and nowhere else: known-flows/auth.md:59 and known-flows/billing.md:307,343,470. Those are precisely the four flows that declare a password parameter, so each states the value once and a copied billing signup flow still carries it without dragging in the auth flow. The former inline occurrences at auth.md:36,50 and billing.md:289,332,459 now read <password>.

Finding 4. Both new skills now ship a trigger-eval.json with 20 cases split 10 should-trigger / 10 should-not-trigger, matching the two pre-existing suites and the procedure at evals/README.md:108-123. The near-misses are genuine adversarial pressure rather than straw men and the discrimination is reciprocal — each suite uses the other skill's own trigger phrasings as its near-misses. Both evals/README.md files now describe what ships accurately, including declaring the baseline absent with its reason, consistent with the plugin-level evals/README.md and the changelog.

No inline comment was posted this round. Finding 1's thread is resolved and its problem no longer appears in the diff, so the repeat-a-persisting-finding exception does not apply; Finding 1 above is SUGGESTED, which stays in the summary by contract.

Checks run
Check Status
Plugin structure Not run here — dedicated workflow step before this review
Marketplace Not run here — dedicated workflow step before this review
Version bump Not run here — dedicated workflow step before this review. Confirmed incidentally: 1.3.0 in .claude-plugin/marketplace.json:111, plugins/bitwarden-testing-tools/.claude-plugin/plugin.json:3, root README.md:25, and all three AGENT.md version: fields, with a Keep-a-Changelog ## [1.3.0] - 2026-10-07 entry that 1c308c2 extended to cover the agents key and the new trigger suites
Plugin validation (AI) Passed — manifest and semver, directory structure, component frontmatter, hook JSON schema and event names, ${CLAUDE_PLUGIN_ROOT} usage, script exec bits, no MCP server declared, no hardcoded credentials. Two raised findings dropped against the documentation (above)
Skill review (AI) Passed with one SUGGESTED — both changed SKILL.md files reviewed independently; every referenced path resolves on disk in both, including the cross-plugin skills named in the catalogs
Configuration & security Passed — three agent definitions, hooks/hooks.json, the hook handler and its tests, scripts/repo-diff.sh, both plugin-level reference policies, and the skill reference and eval files. No settings.local.json in the changeset, no secrets, no new path by which contributor-controlled input reaches a shell, tool grants narrow and justified

Security posture. Unchanged by 1c308c2, which touched only a manifest key, a docstring, prose, and eval fixtures. The three agents hold read-only grants with no Bash, Write, or Edit — scoper Read, Skill, Grep, Glob; mapper Read, Skill; gatherer Read, Skill plus eight read-only Atlassian MCP tools with no create or update variants. The Skill escape hatch is closed by the PreToolUse hook, whose shell-form command correctly double-quotes ${CLAUDE_PLUGIN_ROOT}, takes its input on stdin with no interpolation, and never shells out; its [ -f … ] || exit 1 guard is deliberate, since without it a missing script would make python3 exit 2 and read as a block on every Skill call session-wide. scripts/repo-diff.sh sets -euo pipefail, checks argc, allowlists the repo by basename, quotes $1 at every use so a leading - cannot become an option, and runs git -c core.fsmonitor=false --name-only over a commit range, so no working-tree diff driver or textconv can execute config-driven commands. Moving the password literal into per-flow Notes is a tightening, not a weakening.

No file in the changeset attempts to direct this review. The CWE-1427 language throughout the skills and agents is defensive guidance aimed at their own runtime inputs, and the injection string at skills/scoping-playwright-application-context/evals/behavior-eval.json:69 is a deliberate negative-test fixture whose graded expectation is refusal.

Three further candidates were considered and dropped. .cspell.json:201 (titlecase appended out of alphabetical order) is not in the authoritative changed-file list for this review and is not Claude material under the scope patterns. The hook test file's MAPPER_DIR_FORM constant now pins a doubled agent name the manifest no longer produces, but the matcher remains deliberately tolerant of both forms, so the assertion still passes and the case is merely defensive — a consequence of the fix commit, not a defect in it. The operator-exception carve-out at scoping-playwright-application-context/SKILL.md:12 was present in the previously reviewed state, opens no shell path and widens no tool grant, and treating one's own task prompt as the trusted channel is ordinary agent design.

@kdenney
kdenney force-pushed the add/testing-tools-context-and-scoping branch from ce465ed to 67aaf57 Compare August 26, 2026 20:13
@kdenney
kdenney force-pushed the add/testing-tools-context-and-scoping branch from 67aaf57 to a39e947 Compare August 26, 2026 20:59
@kdenney
kdenney force-pushed the add/testing-tools-context-and-scoping branch from a39e947 to 0d74197 Compare August 27, 2026 21:25
@kdenney
kdenney force-pushed the add/testing-tools-context-and-scoping branch from 0d74197 to d03ec89 Compare August 27, 2026 22:36
@kdenney
kdenney force-pushed the add/testing-tools-context-and-scoping branch 6 times, most recently from f407a58 to 22c883d Compare September 1, 2026 23:25
@kdenney
kdenney force-pushed the add/testing-tools-context-and-scoping branch from 4477912 to 35ec1d1 Compare September 4, 2026 20:43
@kdenney
kdenney force-pushed the add/testing-tools-context-and-scoping branch from 35ec1d1 to db4d8d0 Compare September 18, 2026 20:50
@kdenney kdenney changed the title add/testing tools context and scoping feat(bitwarden-testing-tools): add context building and service scoping to the web test pipeline Sep 18, 2026
@kdenney
kdenney force-pushed the add/testing-tools-context-and-scoping branch from a272f0d to ea49f85 Compare September 23, 2026 17:00
@kdenney
kdenney force-pushed the add/testing-tools-context-and-scoping branch from ea49f85 to c358f18 Compare September 23, 2026 18:51
@kdenney
kdenney force-pushed the add/testing-tools-context-and-scoping branch 3 times, most recently from 4aca5bb to 8dc5007 Compare October 7, 2026 22:34
kdenney added 24 commits October 9, 2026 11:01
…ing trials to a paid-org signup

The PM-44448 run failed because the catalog's trial-email flow stated the
wrong enum values and the wrong wizard order. A normal paid-org signup
already starts a trial, so it becomes the default producer of a trialing
org; marketing and new sales-assisted flows are gated behind Select only
when conditions, and every sourced fact cites a literal the scoper checks.
…s in the scoper

Catalog entries were copied verbatim and treated as already validated,
so wrong enum values and step order reached the PM-44448 test plan. The
scoper now checks each cited literal as plain text before copying,
corrects drift or stops, and uses a Select only when flow only when the
context or the operator's extra instructions meet its condition.
Cases 7 and 8 relied on the paid-org state's two producers, which the
catalog change removed; they now test the Select only when filter and the
changed-UI tie-break. Four new cases cover the default paid-org trial,
marketing flows chosen by acceptance criteria about the trial pages, a
sales-assisted trial chosen by extra instructions, and a no-card trial
resolved by the shortest-chain fallback.
… and citation checks

A gated single-producer state could be copied with no producer, the
Grep escape list and 2,000-line Read limit could report false drift,
and a missing file or a fact that survived a refactor had no rule.
Weak citations now encode their facts, the existing-user mail retry is
capped, and eval case 12 states the real reason the marketing
without-payment flow is chosen.
… read

The using-stripe-cli wrapper now sends a POST to create_preview, which
changes no Stripe state. The policy defined a Stripe write by HTTP method,
which would have forbidden it, so define a write by whether it changes
state and name the preview as a read.
… Context

A test run paused to ask a human whether a flag was on, because the
scoper could only carry a flag requirement as prose in Notes, which the
writer turned into a [HUMAN] step. Give flags a structured section, and
reserve [HUMAN] for actions no granted tool can perform, so Stripe and
Mailcatcher reads stop pausing runs.
…rtifact

The environment check that verifies flags reads the composed test plan,
which holds the services artifact but not the Application Context. Have
the mapper copy the flag section through, and require the Api whenever a
flag is listed, since flag state is read from the running Api.
…ass for flag keys

Flag keys are not only declared in FeatureFlagKeys in Constants.cs; libraries
such as Invoicing declare their own [FlagKeyCollection] classes, which the
server registers the same way.
… service mapper

The Inputs bullet treated a bare path as a web vault route while step 7
said one matched no host-keyed route clause, so step 6's primary-URL
choice depended on which reading the model took. Step 3 now expands a
bare path to https://localhost:8080<path> before matching.
…plugins

reading-mailcatcher-api and using-stripe-cli now live in
bitwarden-mailcatcher-tools and bitwarden-stripe-tools. plugin.json
declares both as dependencies so they install with this plugin.

The tool policy names each category's owner by its plugin-qualified name
and drops the canonical script paths for both: a path into another
plugin does not resolve from this one, and invoking the owning skill is
what applies its script grant. The scoper's known-flows read email
through the skill rather than a script path, and the reach-via
conventions and scoper eval name the qualified Stripe skill.
…old no Bash

The scoper, mapper, and context gatherer list no Bash in tools:, so their
allowlists already keep it out and the hook's Bash branch never fires. Narrow
the matcher to Skill, which is the check that still matters: a forked skill
runs its Bash as a different agent type outside those allowlists. This also
stops the hook from starting a python3 process on every Bash call in any
session with the plugin enabled.
…rences

Harden repo-diff.sh against a planted repo's core.fsmonitor and say what
its basename check does and does not guard. Qualify the Mailcatcher skill
name in the known-flows catalogs and the agents' skills entries so they
match the plugin-qualified names the policy and hook require. Rebuild the
mapper's feature-flag passthrough from validated bullets instead of
copying them verbatim, and fix its argument-hint and bullet order. Record
that the hook reads tool_input.skill, and note the behavior-eval suites in
the evals README.

PR 210 now stands alone: the policy, hook docstring, changelog and hook
tests no longer name an orchestrator or later-PR agents.
…EADME skill name

The planning agents hold no Bash and the hook matches only Skill, so the
agent instructions no longer say it blocks Bash calls. The README row for
the Mailcatcher dependency now uses the plugin-qualified skill name.
… planning agents

The agents cannot call tools outside their allowlist, the Inputs section already says the caller supplies the diff artifact, and the hook explains its own block. The README Stripe row now uses the plugin-qualified skill name.
… context gatherer

The agent cannot call tools outside its allowlist, and the hook explains its own block.
…g citations

The scoper's magenta is not among the colors the sub-agents docs accept, so it
becomes purple, which no other agent in the plugin uses. The auth and admin
known-flows cited their verification points as file:line, which the scoping
skill never checks and which had already drifted in the source. They now use
the path and literal form billing.md uses, so a moved or renamed literal is
caught.
…catalogs and script

The PR validation report found places where the skills' stated contracts
disagreed with the catalogs they copy from and the script they call, so a
run that follows one rule produces output the other forbids.

- Scoper: States `Source:` takes the catalog citation form only; flows may
  carry an optional `**Note:**`; routes may keep an `:organizationId`
  segment; the route check ties a placeholder to its producing flow.
- Mapper: drop the nonexistent "render verification" reference; list the
  basename refusal among repo-diff.sh failures; replace the unverifiable
  working-directory check with the script's own failure path; validate
  feature-flag bullets before deciding to add Api.
- Hook: drop the unused env parameter and os import.
- auth.md: move the Note below the post-condition to match billing.md.
The skill was nearly 4,000 words, with the same rules restated in several
sections. Each rule now lives in one place, the cited-literal procedure
moves to its own reference, and the description drops its exclusion clause,
since the scoper agent invokes the skill by name.

- Prompt-injection notes record the concern and where it appeared, without
  carrying the embedded instruction into the artifact.
- Route placeholders are generalized from `:organizationId` to any `:<name>`
  segment for a value an earlier flow in the setup chain creates.
- Self-review failures emit the plain failure report, and the Notes section
  defers to the steps that send entries to it.
…ll already states

The citation form and the catalog-only field handling are defined in the
scoping skill. The section keeps only what the skill does not say: what
counts toward a Select only when condition, and why card iframe titles
carry no citation.
The plugin README documents both path variables.
…ontract

Review of the trimmed skill found the placeholder rule could fail copied
catalog states, whose organization ID comes from the state's own producer,
and left placeholders in flow-step URLs unruled.

- A `:<name>` value may come from the state's producer or its precondition
  chain; route-only states never carry one; self-review check 5 verifies the
  producer, the source of the value, and the parenthetical.
- Flow-step URLs may keep a `:<name>` segment, which is never a parameter.
- Notes and the failure report name what the reference file and the agent
  send them; the cited-literal check states its missing-file stop.
- The service mapper drops a Route's trailing parenthetical when it
  collects URLs.
…lure naming

Flows step 1 again names the inputs that decide a Select only when condition, since it is the only gate on flows copied for exercising changed UI. The failure report now names a flag key or a citation when that is what failed. Four sentences are tightened to keep the skill within its length target.
…gs use it

The rule said a state is reachable only through the playwright-cli skill, yet the catalogs mark rendered states yes when their producers also read Mailcatcher or fire an external trigger, and mark only Route: n/a states no. Reachability now turns on whether the state is a rendered page the executor can reach with steps it runs itself.
…h its input precedence

services.md claimed the procedure never resolves a repo path, and the eval purpose said the skill always runs its own diff. The skill uses caller-supplied changed files first and builds the path only in standalone runs, so the contradicting sentence is cut and clause (b) now describes the fallback.
@kdenney
kdenney force-pushed the add/testing-tools-context-and-scoping branch from 6b69e6b to 5cc1079 Compare October 9, 2026 17:57
Comment thread plugins/bitwarden-testing-tools/.claude-plugin/plugin.json
List the three agents under the manifest's agents key so each loads as
bitwarden-testing-tools:<agent> however the plugin is loaded, and drop the
hook docstring's note about the name forms seen in the wild.

Add a seventh self-review check that the catalog-only Select only when and
Sources fields were dropped, since the behavior suite grades that rule.

Keep the dev master password out of flow steps and parameter lines. Each
flow that takes <password> now states the value once, in its own Note, so a
copied billing signup flow still carries it without the auth flow.

Add trigger suites for the scoping and mapping skills, per the plugin's
documented procedure for a new skill. Each uses the other's phrasings as
near-misses. Neither has been run, so neither has a baseline yet.
@kdenney
kdenney marked this pull request as ready for review October 9, 2026 18:36
@kdenney
kdenney requested a review from a team as a code owner October 9, 2026 18:36
@github-actions

github-actions Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

🤖 Bitwarden Claude Code Review

Overall Assessment: APPROVE

This PR adds the planning layer of the bitwarden-testing-tools web test pipeline. It contains two skills (scoping-playwright-application-context, mapping-services-under-test), three planning agents, a PreToolUse hook that limits each agent to its own skill, the repo-diff.sh helper, shared policy references, known-flows catalogs, evals, and the 1.3.0 version bump. I checked the hook's fail-closed paths and its tests, the basename allowlist and core.fsmonitor hardening in repo-diff.sh, the agents' tool grants (each limited to Read, Skill, and read-only search or Atlassian lookups), the cross-references between the artifact contract, the skills, and the catalogs, and the version consistency across marketplace.json, plugin.json, README.md, and the AGENT.md files. The credential scan found nothing. The only password literal is the documented local dev fixture, and the agents manifest finding from the earlier thread is resolved.

Not covered: The skill review did not run. This review path cannot launch plugin-dev:skill-reviewer, so plugins/bitwarden-testing-tools/skills/scoping-playwright-application-context/SKILL.md and plugins/bitwarden-testing-tools/skills/mapping-services-under-test/SKILL.md were not checked for description quality, length, or progressive disclosure. performing-multi-agent-code-review covers those checks where plugin-dev is installed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant