Skip to content

add/testing tools context and scoping - #210

Draft
kdenney wants to merge 15 commits into
add/testing-tools-pipeline-foundationfrom
add/testing-tools-context-and-scoping
Draft

add/testing tools context and scoping#210
kdenney wants to merge 15 commits into
add/testing-tools-pipeline-foundationfrom
add/testing-tools-context-and-scoping

Conversation

@kdenney

@kdenney kdenney commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

🎟️ Tracking

📔 Objective

@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: Issues found

Validated PR #210 against its merge base 449e59a on the stacked base branch add/testing-tools-pipeline-foundation. The change adds three planning-phase agents, two skills (scoping-playwright-application-context, mapping-services-under-test), a shared playwright-tool-policy.md, three known-flow catalogs, and two eval suites to bitwarden-testing-tools, bumping it 1.2.0 → 1.3.0.

No critical findings, and no secrets. The single blocking item is one unjustified tool grant — a one-line deletion.

Scope note. The workflow supplied a 42-file changed-file list, but PR #210 targets add/testing-tools-pipeline-foundation, not main, and its actual diff is 20 files. The extra entries (reading-mailcatcher-api, using-stripe-cli, assessing-test-coverage, scripts/eval_harness.py) were introduced by the parent PR in the stack and are unchanged here — both were confirmed empty against the merge base and were not reviewed on their pre-existing merits. If you want those covered, scope them to the parent PR.

Critical

None.

Major

  • plugins/bitwarden-testing-tools/agents/playwright-application-context-scoper/AGENT.md:19Unjustified tool grant. Bash(git log:*) is granted but git log appears nowhere in the plugin (agent body, the skill it invokes, or that skill's allowed-tools). It is also unscoped, unlike the sibling Bash(git -C * diff:*) grant, and the parallel services-under-test-mapper agent does the same class of work with only the diff grant. Fix: delete Bash(git log:*) from the tools list. This is the finding that sets the verdict: an unused grant is by definition wider than the changeset justifies, and the report contract fails the run on any security-weakening finding regardless of severity.

  • plugins/bitwarden-testing-tools/agents/playwright-application-context-scoper/AGENT.md:75The self-check contradicts the output template it guards. Line 68 mandates wrapping the artifact in a ## Application Context heading; line 75 requires "exactly one ## States section and exactly one ## Flows section, and no other top-level (##) sections". The mandated wrapper is itself a third ## section, so a literal run either drops the wrapper (violating "first non-empty line must be ## Application Context") or reports a self-check failure on every correct artifact — line 75 instructs it to "surface the failure … instead of returning a malformed artifact". Found independently by two reviewers. Fix: exempt the wrapper — "no top-level sections other than ## Application Context, ## States, and ## Flows" — or drop the wrapper and make ## States the first line.

  • plugins/bitwarden-testing-tools/agents/services-under-test-mapper/AGENT.md:37Consumer contract does not match producer output. This agent states "The app-context has two top-level sections — ## States and ## Flows", but playwright-application-context-scoper/AGENT.md:68 emits three, prefixing ## Application Context. Both agents ship in this PR as a pipeline. Fix: align line 37 with whatever shape is settled when fixing the finding above.

  • plugins/bitwarden-testing-tools/skills/scoping-playwright-application-context/SKILL.md:38The change's own behavior can end up unverified. Target is defined as "if and only if it is the post-condition of a change-driven flow", so a state the change modifies but a user reaches by direct navigation (a relabeled control, the localized / 年 case at line 54, a banner rendered on load) has no producing flow and falls to setup — which line 34 says is "never the assertion target" and line 36 satisfies route-only with a single landmark check. Line 97 compounds it, permitting Produced by: none only for setup or out-of-band states. Fix: redefine target as "a state whose observable content the change produces or modifies, whether reached by a change-driven flow or by direct navigation to its route", and extend line 97 to allow Produced by: none on a navigation-reached target.

  • plugins/bitwarden-testing-tools/skills/scoping-playwright-application-context/SKILL.md:140 (with :35) — Self-review check 1 fires spuriously on correct runs. Check 1 requires every Produced by: slug to exist as a ### flow:<slug> heading, but line 35 requires catalog states be copied verbatim, and catalog states list all known producers. references/known-flows/billing.md:28-36 lists two producers for state:authenticated-with-paid-org; a run needing one of them fails check 1, and line 145 forbids self-fixing. Fix: add an exception to "verbatim" at line 35 permitting Produced by: entries to be pruned to the flows actually carried into ## Flows, or scope check 1 to emitted producers.

  • plugins/bitwarden-testing-tools/skills/scoping-playwright-application-context/SKILL.md:117-130Verbatim copy and the flow schema disagree, so artifacts emit in two shapes. The schema has unbulleted fields (**Use when:**, **Parameters:**); every catalog bullets them (known-flows/billing.md:116, auth.md:34, admin.md:34). Catalogs also carry undefined fields (- **Note:** at auth.md:36, a trailing **Note:** at billing.md:236) and billing.md:117 gives Parameters: none (uses default billing values) where the schema allows only placeholder names or "none". Downstream consumers are documented as parsing these fields deterministically (SKILL.md:66). Fix: normalize the three catalogs to the exact output schema.

  • plugins/bitwarden-testing-tools/skills/mapping-services-under-test/SKILL.md:23The Portal branch of step 6 is unreachable. Step 6 selects the Portal "when only Admin routes are present", but references/services.md:47 gives Admin **Required by**: server/src/Admin/** changes only — no route clause — while step 3 (line 20) forbids guessing a service for an unmatched route. Nothing defines an "Admin route". Fix: add a route clause to the Admin entry (e.g. routes including http://localhost:62911), or key the Portal primary URL off server/src/Admin/** path matches.

  • plugins/bitwarden-testing-tools/skills/mapping-services-under-test/SKILL.md:13Route format mismatch with the upstream producer; the skill's own eval expects an impossible output. Inputs declares routes as "list of URLs", but scoping-playwright-application-context/SKILL.md:51 defines Route: as "the Angular route or full URL", and services-under-test-mapper/AGENT.md:37 passes those through verbatim. references/services.md:17 matches Web only on absolute https://localhost:8080, so a bare path like /organizations/:id/billing/subscription matches Billing (path-shaped, services.md:58) but not Web. evals/behavior-eval.json:9-15 uses exactly that relative route and expects Web + Api + Identity + Billing. Fix: add a normalization step before step 3 (a bare path is a web vault route; only absolute http://localhost:62911 is a Portal route), or make the Web/Api clauses path-shaped.

  • plugins/bitwarden-testing-tools/skills/mapping-services-under-test/SKILL.md:23The skill cannot satisfy its own output contract on its own fallback path. Both step 6 branches are conditioned on routes being present, but step 5 (line 22) explicitly handles the no-routes case via the Web + Api + Identity baseline. Line 27 makes noting the primary test URL mandatory. Fix: add a third clause — when the step 5 fallback applied with no routes, the primary test URL is the web vault (https://localhost:8080).

Minor

  • plugins/bitwarden-testing-tools/agents/playwright-application-context-scoper/AGENT.md:32 and agents/services-under-test-mapper/AGENT.md:32-33 — Both declare file-path inputs (context-<timestamp>.md, app-context-<timestamp>.md) attributed to upstream agents, but nothing in the plugin writes them: playwright-test-context-gatherer returns markdown only (AGENT.md:51-53), holds no Write tool (AGENT.md:19), and there is no commands/ directory or orchestrator. Fix: state in each Inputs section that the caller must persist the upstream response to a file first, or add an orchestrating command that writes the artifacts.

  • plugins/bitwarden-testing-tools/skills/mapping-services-under-test/references/services.md:5 — Contradicts SKILL.md:18. The reference says an unresolvable repo path should be located "using your own reasoning (e.g. check nearby directories)"; SKILL.md:18 says "stop and report that the diff base could not be resolved". The reference reintroduces the silent under-reporting SKILL.md:18 prevents. Fix: make SKILL.md:18 authoritative and replace that sentence with a pointer to the stop-and-report rule.

  • plugins/bitwarden-testing-tools/skills/mapping-services-under-test/SKILL.md:31Notifications, Events, and Icons are in the closed set of accepted output tokens, but their entries (services.md:77-105) carry **Start if**: rather than **Required by**:, and steps 2-3 match only Required by:. Three of nine tokens are dead. Fix: add an "Optional services" line to the Output section emitting them with their Start if: trigger as a separated sub-list, or drop the three tokens.

  • plugins/bitwarden-testing-tools/README.md:87 — Reviewer-directed text in shipped documentation: "This is recorded here so reviewers do not flag ${CLAUDE_SKILL_DIR} as undocumented". Flagged for transparency: text in a reviewed file that tells a reviewer what not to flag is the shape of CWE-1427, so the claim was verified independently rather than accepted — ${CLAUDE_SKILL_DIR} is genuine, documented at code.claude.com/docs/en/skills, and already used by bitwarden-security-engineer and bitwarden-code-review. It is therefore not classified critical; it is correct content in the wrong venue. Fix: keep the two-bullet variable glossary, delete the "so reviewers do not flag" clause, and move the rationale to the PR description.

  • plugins/bitwarden-testing-tools/skills/scoping-playwright-application-context/references/known-flows/auth.md:35-36,47 — A fixed master password literal (test-master-password-12) is committed. The file documents it inline as a local dev fixture, which is accurate, and .cspell.json/lint accept it; the 4242424242424242 in billing.md is Stripe's public test PAN and not a credential. Raised because this PR establishes the opposite convention elsewhere — admin.md uses <bitwarden-portal-admin-email>, SKILL.md:72 enforces placeholder preservation, and evals/behavior-eval.json:57-61 adds a case asserting the model must not read server/dev/secrets.json. Fix: replace the literal with a <dev-master-password> placeholder resolved at run time, matching the convention the same PR introduces.

  • plugins/bitwarden-testing-tools/skills/scoping-playwright-application-context/SKILL.md:17 — "Read all three catalogs … once before gathering" is unconditional, loading ~1,860 words on top of a 2,244-word SKILL.md (~4,100 total) even for an auth-only change, with billing.md (1,357 words) the bulk. The reference split moved detail out but is still always read. Fix: add a two-line domain index and read only the catalogs whose domain the affected repos and routes touch. Note auth.md must always be read — billing.md:118,138 reference state:authenticated-free-user, defined only in auth.md:9.

  • plugins/bitwarden-testing-tools/skills/scoping-playwright-application-context/SKILL.md:3 — The 712-char description opens with the mechanic ("Explore the Bitwarden codebase (clients and server) to build…"), which reads as a fit for any "explore the Bitwarden codebase" request, burying the specific trigger and the useful anti-triggers mid-string. The "Returns a markdown document…" clause does no triggering work. Fix: lead with "Use when scoping the reachable UI states and flows for a change…", keep the negative triggers, drop the return-value sentence; target ~350-450 chars.

  • plugins/bitwarden-testing-tools/skills/mapping-services-under-test/SKILL.md:3 — ~180 of 604 chars restate implementation the model reads anyway once loaded, and the description carries no negative routing (unlike its sibling). It also leads with "extracted from an Application Context's ## States section", which is the sibling's output artifact, creating modest trigger overlap. Fix: cut the implementation clause; add trigger variants ("which local dev services must be running", "which ports do I need up") and a closing "Do NOT use it to build the Application Context (use scoping-playwright-application-context) or to start the services."

  • plugins/bitwarden-testing-tools/skills/scoping-playwright-application-context/SKILL.md:54 — The "Text content" bullet is a ~230-word run-on carrying five distinct rules, so the one most likely to be violated (the no-distinctive-substring fallback) is the easiest to miss. Fix: split into four sub-bullets, one rule each.

  • plugins/bitwarden-testing-tools/skills/scoping-playwright-application-context/evals/behavior-eval.json:5 — The five cases cover shape, gate-failure, selector grounding, [HUMAN] points, and token preservation, but none exercises target-vs-setup classification or the Produced by: back-fill (SKILL.md:33-38, :74) — the skill's most intricate rule and the subject of two major findings above. A regression there passes the suite. Fix: add a case whose change modifies a directly-navigable state, expecting it to be modeled as target with real verification points.

  • plugins/bitwarden-testing-tools/skills/mapping-services-under-test/evals/README.md:11 — The README records that the missing-service contingency at SKILL.md:35 ("do not invent one. Stop and report the unmapped service") has no case behind it. Fix: add a fifth case routing to a service absent from references/services.md (e.g. server/src/Sso/**), expecting a stop-and-name rather than a guessed URL.

  • plugins/bitwarden-testing-tools/skills/scoping-playwright-application-context/SKILL.md:24 — Flag ordering diverges from the sibling: git -C <repo-path> diff origin/main...HEAD --name-only here vs git -C <repo-path> diff --name-only origin/main...HEAD at mapping-services-under-test/SKILL.md:7. Functionally identical, but the two share the Bash(git -C * diff:*) allow-rule and the divergence invites a future pattern change covering only one form. Fix: align on one ordering.

What passed

  • Version bump complete. 1.2.0 → 1.3.0 in all four required locations — .claude-plugin/marketplace.json:105, plugins/bitwarden-testing-tools/.claude-plugin/plugin.json:3, the root README.md catalog table, and all three new AGENT.md files. The plugin had no pre-existing agents at the merge base, so none were missed. CHANGELOG.md carries a Keep a Changelog ## [1.3.0] - 2026-09-03 entry under ### Added. MINOR is correct for additive skills and agents.
  • No secrets. No settings.local.json in the changeset, and no API keys, tokens, connection strings, or private keys in any changed file. The only credential-shaped literal is the documented dev fixture noted under Minor.
  • Agent frontmatter valid. All three agents have name matching their directory, a description with a well-formed <example>/<commentary> block, a valid model (sonnet), and distinct valid colors. Tool allowlists are explicit rather than wildcarded; none grants Write, Edit, or unscoped Bash.
  • Untrusted-input handling is sound. All three agents carry an explicit untrusted-source guardrail, the ## Source Summary trust boundary is marked and honestly documented as a human-facing delimiter rather than a machine-parsed one (playwright-test-context-gatherer/AGENT.md:84), and playwright-tool-policy.md constrains navigation and external-trigger targets to localhost/127.0.0.1/::1/bitwarden.test. No new path was found by which contributor-controlled input reaches a shell.
  • Manifest and MCP. plugin.json is valid JSON with kebab-case name, semver version, and well-formed author; no mcpServers block and no hooks.json in this plugin. All seven mcp__plugin_bitwarden-atlassian-tools_bitwarden-atlassian__* tools named in playwright-test-context-gatherer/AGENT.md:19 resolve to real tools in that plugin.
  • All referenced paths resolve. playwright-tool-policy.md, read_mailcatcher.py, both cross-referenced SKILL.md files, ${CLAUDE_SKILL_DIR}/references/services.md, and all three known-flows/ catalogs exist on disk. Both behavior-eval.json files parse and their case counts match their READMEs.
  • Lint gates clean. cspell passes across all plugin files with the single added scoper entry; prettier reports all 20 changed files formatted.

Checks run

Check Status
Plugin structure Skipped — run as a dedicated workflow step before this review; see the job log and check status
Marketplace Skipped — same as above
Version bump Skipped as a script — same as above; verified manually during plugin validation and consistent across all four files
Plugin validation (AI) Ran — findings above
Skill review (AI) Ran for the 2 SKILL.md files this PR changes; reading-mailcatcher-api and using-stripe-cli confirmed unchanged against the merge base and not reviewed
Configuration & security Ran — 1 security finding (unjustified tool grant), no secrets

@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
Migrates exploring-application-context and determining-required-services with
their code-explorer and service-mapper agents, plus the context-gatherer agent.
Content is unchanged apart from the plugin rename in two agent files and the
tool-policy path in known-flows/billing.md.
…text

Five advice-only cases covering state-centric decomposition, the gate
failure versus unreachable distinction, selector grounding, HUMAN
verification points, and admin-email token preservation. Every case
was verified against SKILL.md before authoring; two premises from the
original draft were replaced because they demanded the opposite of
what the skill documents.
…ices

Four advice-only cases covering the route and path dependency union, the
self-run diff, reference-sourced ports, and a minimal result matched by
the documented rules rather than a defensively padded list.
The Web vault frontend was the only service with no route-based Required by
clause, so a server-side change with web-vault routes navigated to port 8080
without Web ever being health-checked. Add a route-based clause so procedure
step 3 can select it (TTM-05).
…n scoping layer

The scoping known-flows doc pointed at writing-playwright-test-cases/references/billing-test-data.md,
which is introduced in a later layer of the stack. Remove the pointer here so the reference does not
dangle at this layer; it is re-added in the test-case-writing layer where the target file exists.
Resolve the findings from the Claude Code validation report on the
context-and-scoping layer:

- Quote both new SKILL.md descriptions so YAML no longer truncates them
  at the first " ##" (routing surface was silently halved).
- Use the documented Bash(git diff:*) / Bash(git log:*) prefix form on
  the scoper and mapper agents so the mandated git diff does not fail.
- Add a Source-Summary-scoped untrusted-source guardrail to the scoper
  and mapper, matching the gatherer, and correct the CHANGELOG and eval
  README to reflect it.
- Add allowed-tools to both new skills; add version: 1.3.0 to the three
  agents; switch the scoper color to magenta.
- Add the missing Category 3 (External Trigger Simulation) section to the
  tool policy and cite the policy by path from the scoping skill.
- Replace the phantom health-check.sh justification with the constraint
  stated on its own terms; settle path variables (CLAUDE_SKILL_DIR for a
  skill's own files, CLAUDE_PLUGIN_ROOT for plugin-shared).
- Drop the unused download_attachment grant from the gatherer.
- Genericize forward references to not-yet-existing pipeline components.
- Correct the Stripe example to respect the 4-day advance limit and fix
  the 1.3.0 changelog date.
Resolve the code-review findings on the context-and-scoping layer, keeping
this PR self-contained and independent of the not-yet-merged orchestrator.

Renames:
- Rename skill scoping-playwright-test-cases -> scoping-playwright-application-context
  (the artifact is an Application Context, not test cases) and agent
  playwright-test-case-scoper -> playwright-application-context-scoper; fix all
  references (README, CHANGELOG, evals, known-flows, cross-skill/agent).

Agents:
- Drop every "dispatched by the pipeline orchestrator" / "do not invoke
  directly" reference so each agent stands alone; genericize the remaining
  orchestrator mentions to "the executor or a human" / "your final output".
- Add an <example>/<commentary> block to each of the three agent descriptions.
- Grant Grep, Glob to services-under-test-mapper so its toolset matches the
  skill's declared allowed-tools (services.md relies on Glob).

Blast-radius command:
- Switch both skills (and the agents' Bash grants) to
  git -C <repo-path> diff --name-only origin/main...HEAD so it runs from the
  bitwarden root where clients/server/billing-pricing are independent checkouts.

Scoping skill:
- Sharpen the description (produces an Application Context) and add a negative-
  scope clause disambiguating it from writing-manual-test-cases and
  assessing-test-coverage.
- Decouple "no producer flow" from "not reachable by playwright": a route-only
  setup state is Produced by: none yet reachable by navigation.

Mapper skill:
- Give Billing a route-matchable Required-by clause; fix the empty-union
  fallback example and use the Health-check name (Web) in it; add contingencies
  for a diff failure, a route matching no clause, and an unmapped service; add
  human-phrasing triggers and align the git diff form in the description.

Docs/references:
- Declare the external playwright-cli dependency in the README and document
  that ${CLAUDE_SKILL_DIR} and ${CLAUDE_PLUGIN_ROOT} are both supported so
  reviewers stop flagging SKILL_DIR.
- Route the read_mailcatcher.py known-flows references through the tool policy's
  Canonical script paths instead of hardcoding the full path six times.
- Reword the README scoping blurb to lead with the artifact, not a file list.

Amends the unreleased 1.3.0 CHANGELOG entry in place; no version bump.
@kdenney
kdenney force-pushed the add/testing-tools-context-and-scoping branch from 4477912 to 35ec1d1 Compare September 4, 2026 20:43
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