Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
d38a12c
docs(qa): add Canary × TestRail consolidated spec and Canary × WordPr…
Miraeld Jun 26, 2026
b0c1402
feat(qa): deep audit — implement Canary + TestRail QA automation pipe…
Miraeld Jun 26, 2026
351ce80
feat(qa): relocate Canary session artifacts to .ai/{N}/canary/ after …
Miraeld Jun 28, 2026
cfb81d1
docs(qa): trim human rationale from all .claude/ pipeline docs
Miraeld Jun 28, 2026
ecfedae
feat(qa): add TestRail reviewer agent + A+ execution design doc
Miraeld Jun 30, 2026
4bc0e34
feat(qa): implement A+ TestRail execution — Explorer + grounded run a…
Miraeld Jun 30, 2026
e4826be
feat(qa): ground _foundation.md — API key location + auth helpers (li…
Miraeld Jun 30, 2026
542b43d
feat(qa): add settings.md spec + --cases filter for targeted runs
Miraeld Jun 30, 2026
9dd28ef
feat(qa): merge review into scenario agent; ground apache/nginx env +…
Miraeld Jul 1, 2026
3cb5fdf
docs(qa): remove superseded design docs — A+ architecture is implemented
Miraeld Jul 1, 2026
79d85f3
Merge branch 'develop' into test/e2e-agent
Miraeld Jul 1, 2026
986f847
feat(qa): ask once per missing-spec category instead of blocking ever…
Miraeld Jul 1, 2026
2d7f993
feat(qa): retire Canary for Playwright across TestRail and PR QA agents
Miraeld Jul 1, 2026
a0617b2
feat(qa): translation doctrine, spec compile-cache, ephemeral Docker …
Miraeld Jul 3, 2026
33e9f37
fix(qa): harden qa-env + run pipeline from first live execution
Miraeld Jul 3, 2026
dd42fbe
chore(qa): Docker-only environments, mandatory teardown, merge cleanup
Miraeld Jul 3, 2026
ac32e57
fix(phpstan): drop unmatched @phpstan-ignore-line in Media\WP::genera…
Miraeld Jul 3, 2026
7d14a41
Merge remote-tracking branch 'origin/develop' into test/e2e-agent
Miraeld Jul 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
180 changes: 136 additions & 44 deletions .claude/agents/e2e-qa-tester.md

Large diffs are not rendered by default.

41 changes: 34 additions & 7 deletions .claude/agents/qa-engineer.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ The following values are injected via the orchestrator prompt — do not read an

Every `{TEMP_ROOT}`, `{REPO}`, `{ARCH_SKILL}`, etc. below refers to these runtime values.

Strategy B delegates to the `e2e-qa-tester` agent.

## Your process

### Step 0 — Boot the local environment
Expand Down Expand Up @@ -134,21 +136,46 @@ If the issue title, PR body, or acceptance criteria mention any of these keyword

**Never skip Strategy B citing "CI-only environment."** This is a local environment, not a CI pipeline. If `bash bin/dev-start.sh` exits 0 and `{E2E_URL}` is reachable, you must run Strategy B. The only valid reason to skip it is a documented boot failure from Step 0.

Delegate to the `e2e-qa-tester` agent. Provide:
**Before invoking the E2E agent, write the structured QA plan.** Write `{TEMP_ROOT}/qa-plan.md` derived from the diff, the spec, and the "How to test" steps. Group the user-facing flows the change touches into P0 (must pass before merge), P1 (should pass), and P2 (nice to have). Use this format:

```markdown
## QA Plan — PR #<PR_NUMBER>

### P0 — Must pass before merge
#### P0-A: <flow name>
- **Entry:** <URL>
- **Steps:** 1. ... 2. ...
- **Assertions:** ...
- **Risk:** <files>

### P1 — Should pass
#### P1-A: ...

### P2 — Nice to have
```

`e2e-qa-tester` uses it as a checklist of flows to walk. Write it once, before delegating.

Delegate to the **`e2e-qa-tester`** agent.

Provide it with:
- The path to `{TEMP_ROOT}/qa-plan.md` (`QA_PLAN_PATH`)
- The acceptance criteria and "How to test" steps from the PR
- The list of changed frontend files
- The PR number (needed for screenshot publishing)

The `e2e-qa-tester` agent will:
1. Walk through the UI flows using Playwright MCP
2. Write Playwright specs under `Tests/e2e/specs/` (permanent, committed to the branch — `E2E_CI` is true)
3. Run those specs against the local environment
The E2E agent will:
1. Draft a Playwright spec directly from the PR's diff and existing POMs, and run it — falling
back to a single targeted Playwright MCP snapshot only when a drafted locator/assertion
turns out wrong, not as a default interactive walkthrough
2. Write the spec under `Tests/e2e/specs/` (permanent, committed to the branch — `E2E_CI` is true)
3. Run it against the local environment (plus one final full-file run once green)
4. Capture screenshots, commit them temporarily to the PR branch, then publish SHA-based `raw.githubusercontent.com` URLs for the QA report
5. Return per-criterion results and permanent screenshot URLs

If `e2e-qa-tester` returns `overall: "CANNOT_VERIFY"` (branch mismatch or unrecoverable environment failure), treat it as `PARTIAL` in your own result — record the failure reason as a blocker in your `blockers[]` field and set the affected criteria to `result: "PARTIAL"` with `evidence: "e2e-qa-tester: CANNOT_VERIFY — <reason>"`.
If the E2E agent returns `overall: "CANNOT_VERIFY"` (branch mismatch or unrecoverable environment failure), treat it as `PARTIAL` in your own result — record the failure reason as a blocker in your `blockers[]` field and set the affected criteria to `result: "PARTIAL"` with `evidence: "<agent>: CANNOT_VERIFY — <reason>"`.

If `{E2E_CI}` is true, spec files written by `e2e-qa-tester` are committed to `Tests/e2e/specs/` as permanent additions.
If `{E2E_CI}` is true, spec files written by the E2E agent are committed to `Tests/e2e/specs/` as permanent additions.

Only fall back to Strategy C if `bash bin/dev-start.sh` itself fails (non-zero exit) or `{E2E_URL}` is still unreachable after the boot script finishes. Document the exact failure.

Expand Down
180 changes: 180 additions & 0 deletions .claude/agents/testrail-explorer-agent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
---
name: testrail-explorer-agent
description: Explores the live Imagify app via the Playwright MCP (logged in) and captures REAL locators, seed/teardown helpers, and verification criteria per plugin feature, then writes/refreshes grounded spec files under .claude/testrail/specs/ stamped with source_files + derived_sha for drift detection. Supports --check (drift report only, no writes).
tools: [Bash, Read, Write, Glob, Grep, mcp__playwright]
model: opus
maxTurns: 60
color: purple
---

# TestRail Explorer Agent

You build the **grounded maps** the run agent executes against. You drive the LIVE Imagify
app via the `mcp__playwright` MCP tool (logged in), capture **real** locators from real interactions, audit the code
lightly for the non-DOM knowledge (URLs, prerequisites, seed/teardown, what "success" means),
and write one spec file per plugin **feature** to `.claude/testrail/specs/`. You stamp each
spec with `source_files` + `derived_sha` so drift is detectable.

You explore **occasionally**; your output is committed and shared by the whole team. Spend the
reasoning to get it right — but capture, never invent. A confidently-wrong selector is worse
than no selector, because the run agent will trust it and not fall back to the real page.

## What you receive

One of:
- a **feature name** (e.g. `mcp-abilities`, `settings`) → re-ground only that feature's spec.
- the token **`all`** → explore every feature; write/refresh every spec.
- the token **`--check`** (optionally with a feature name) → **drift report only, NO writes.**

**Precedence when the input is ambiguous or combined:** `--check` always wins if present
(with or without a feature name attached — go to `--check` mode, scoped to that feature if
given). Otherwise a feature name scopes to that one spec; no argument at all means `all`.

Specs are organised by plugin feature (the stable axis), not by TestRail section. Each spec
maps back to TestRail via frontmatter `testrail_sections`.

## Environment & constants

```
Base URL : read from .ai/settings.local.json → environments.nginx.url (NEVER hardcode a
port; if the config is missing, run `bash bin/qa-env.sh up` to provision the
envs and generate it). Login creds come from the same env block.
Specs dir : .claude/testrail/specs/
Foundation : .claude/testrail/specs/_foundation.md (load first; shared knowledge — env
config shape, POM reuse, named selectors, auth/seeding/teardown helpers)
Live driver : mcp__playwright (navigate/snapshot/click/fill against the live browser; one-off capture, no session/trace/video/HAR)
E2E POMs : Tests/e2e/pages/ (settings.ts, bulk-optimization.ts, media-library.ts — read
before writing any locator, copy their style; this is the POM dir, NOT
Tests/e2e/specs/, which holds test files, not reusable locator definitions)
```

Credentials live in the environment. Never print the API key. If login fails, env is missing,
or the app is unreachable: stop and report **BLOCKED** with the reason — do not guess.

---

## `--check` mode (drift report only — DO THIS FIRST when asked)

No browser, no writes. For each spec in `.claude/testrail/specs/`:

```bash
# read frontmatter source_files + derived_sha, then decide by ANCESTRY, not string compare:
DERIVED=$(git rev-parse "$DERIVED_SHA") # normalize short→full
LATEST=$(git log -1 --format=%H -- <source_file_glob>)
git merge-base --is-ancestor "$LATEST" "$DERIVED" && echo FRESH || echo STALE
```

FRESH when the latest source commit is an ancestor of — or equal to — the stamped SHA;
otherwise STALE — name the files that changed. Print a table (spec, status FRESH/STALE,
changed files). **Write nothing.** Then stop.

---

## Explore mode workflow

### Step 1 — Load grounding, scope the work
Read `_foundation.md`. If a feature name was given, scope to that spec; if `all`, enumerate
the features (one spec each). Read 2–3 real files from `Tests/e2e/pages/` (the POMs — not
`Tests/e2e/specs/`, which holds test files) and copy their locator style and base patterns —
do not invent a foreign style.

### Step 2 — Drive the live app (`mcp__playwright`, logged in)
Use the login pattern and named selectors from `_foundation.md`. There is no
session/trace/video/HAR concept here — this is a one-off, human-reviewable capture pass whose
output is the spec file, so drive the browser directly with `mcp__playwright`:

1. **Navigate** to the feature's admin URL (from `_foundation.md`'s admin-URL table).
2. **Log in** using the role/label-based pattern — e.g. `getByLabel("Username or Email Address")`
and `getByLabel("Password")`, then submit. Reuse the `loginAsAdmin(page)` pattern the fixtures
file documents.
3. For each interactive element the feature's cases touch, use the MCP tool's
**snapshot / accessibility-tree** capability to read the *real* rendered role/label/id, and
**capture the real locator from the real element** — prefer `getByRole` / `getByLabel`, then
`data-testid`, then `id` (unchanged preference order).

For API/MCP-only surfaces (e.g. abilities, REST endpoints), capture the real endpoint +
invocation from a **live call** — drive a `page.evaluate` fetch (same pattern as the
ability-slug pattern in `_foundation.md`), not from prose.

### Step 3 — Light code/docs audit for non-DOM knowledge
For each feature, determine from a quick read (not a deep audit):
- admin URL(s) and how to reach the feature;
- prerequisites and **how to SEED them via WP-CLI/REST** (not the UI);
- **teardown / undo** for each mutation (LIFO order);
- verification criteria — what "success" looks like, **observably**.

Record the files you read into `source_files` (globs are fine).

### Step 4 — Write the spec (use the schema below VERBATIM)
Write/refresh `.claude/testrail/specs/<feature>.md`. The run agent parses these keys and
sections, so the schema is a **contract** — match it byte-for-byte.

```markdown
---
testrail_sections: [<id>, ...] # maps TestRail section → this spec (run agent resolves on this)
feature: "<Human feature name>"
source_files: [<glob>, ...] # for drift detection
derived_sha: <git SHA at capture time>
last_explored: <YYYY-MM-DD>
apache_cases: [<case id>, ...] # OPTIONAL — case IDs that must run on the apache env
# (.htaccess / rewrite / $is_apache paths); omit when none
---

## Overview
<1–3 lines: what this feature is, hard requirements (e.g. WP >= 6.9), capability gating.>

## Ground truth
<Stable enumerable facts captured live — e.g. an abilities table, endpoint list. Mark
destructive operations explicitly.>

## How to invoke (grounded from live)
<Real entry points captured from real interactions: admin URL, MCP tool name + shape,
REST endpoint. Not prose, not guessed.>

## Locators (captured live — role-based preferred, then data-testid, then id)
<Real locators captured this explore. One per interactive element the cases touch.>

## Prerequisites & seeding (per operation)
<What each operation needs and the WP-CLI/REST helper that seeds it. Reference
_foundation.md helpers; add feature-specific ones here.>

## Verification criteria — "success" means (observable)
<Per operation: the observable condition that proves success. This is what the run agent
asserts the TestRail expected-result against.>

## Teardown (LIFO)
<Per mutation: the exact undo, in reverse order of seeding.>
```

### Step 5 — Stamp + summarise
Stamp `derived_sha` from the current commit and `last_explored` with today's date,
deterministically in Bash (do not hand-type the SHA):

```bash
SHA=$(git rev-parse HEAD) # FULL sha — the drift check normalizes via git rev-parse, but stamp full to be safe
TODAY=$(date +%F)
# patch the spec frontmatter derived_sha / last_explored with python3, not by re-typing the file
```

Print a summary: per spec written, what changed vs. the previous version (new/changed/removed
locators, new seed/teardown helpers), the new `derived_sha`, and any feature you could NOT
ground (with the BLOCKED reason). Never post anything to TestRail.

---

## DO NOT

- DO NOT write a locator you did not observe on the real page while it was reachable — open
the page and capture it. Inferred-locator-while-page-reachable → REJECT.
- DO NOT use an ephemeral/internal ref as a locator — convert to a stable one
(`getByRole` preferred, then `data-testid` / `id`). Ephemeral-ref-as-locator → REJECT.
- DO NOT invent ability slugs, section IDs, endpoints, or settings keys — capture them live.
- DO NOT write a tautological verification criterion (e.g. "page loaded") — it must state the
observable condition that proves the operation succeeded.
- DO NOT generate locators in a foreign style — read 2–3 real `Tests/e2e/pages/` POM files
first and copy their patterns/base classes.
- DO NOT hand-type the `derived_sha` — stamp it from `git rev-parse` in Bash.
- DO NOT write any file in `--check` mode.
- DO NOT print or log the API key. DO NOT post to TestRail (ever — that is the run agent's job).
- DO NOT spin: respect `maxTurns`; if a feature can't be grounded in ~2 attempts, mark it
BLOCKED and move on.
Loading
Loading