Skip to content

feat(qa): TestRail QA automation — Playwright pipeline with grounded specs, spec cache, and ephemeral Docker envs#1160

Open
Miraeld wants to merge 18 commits into
developfrom
test/e2e-agent
Open

feat(qa): TestRail QA automation — Playwright pipeline with grounded specs, spec cache, and ephemeral Docker envs#1160
Miraeld wants to merge 18 commits into
developfrom
test/e2e-agent

Conversation

@Miraeld

@Miraeld Miraeld commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Description

TestRail QA automation pipeline for Imagify, built on Playwright + ephemeral Docker environments (Canary was retired mid-branch; no Canary code remains). No product code is changed — the diff is entirely QA infrastructure (.claude/, Tests/e2e/, bin/, .distignore, .gitignore).

Note

Parts of this pipeline were AI-generated (Claude Code) and human-reviewed. The full run flow was executed live before this PR was refreshed — see "What was tested".

Two user-facing goals:

  1. /testrail-scenarios — generate TestRail test cases from merged PRs (staged as YAML for human review, published on explicit confirm, deduped via refs).
  2. /testrail-run — fetch a TestRail run (run ID / milestone / active), translate each human-written case into a Playwright .spec.ts, execute sequentially with trace/video evidence, keep a live results ledger, and post results back to TestRail only after user confirmation.

Type of change

  • New feature (non-breaking change which adds functionality).
  • Bug fix (non-breaking change which fixes an issue).
  • Enhancement (non-breaking change which improves an existing functionality).
  • Breaking change (fix or feature that would cause existing functionality to not work as before).
  • Sub-task of #(issue number)
  • Chore
  • Release

Detailed scenario

What was tested

Automated, live end-to-end run (2026-07-03): bin/qa-env.sh up (zip build → Docker up → WP install on nginx + apache → plugin install → API key seed → DB snapshot) → TestRail run 1283 resolved via API → cases C26359 and C26362 translated per the doctrine → compile gate → executed → both PASS (~1.1s each, trace + video captured) → results ledger written → DB snapshot reset verified → post-reset replay PASS → qa-env.sh down. Posting to TestRail was intentionally skipped (both cases already PASSED in the run). The two resulting cached specs are committed under Tests/e2e/testrail-cases/ as living proof.

Execution surfaced and fixed 5 bugs review alone would not have caught, notably: .distignore was missing /.ai, /node_modules, /generatedpackagesbin/build-zip.sh packed 240 MB of local artifacts (including browser profiles with cookies) into imagify.zip; now 2.9 MB / 1,280 files, verified clean. Also: Playwright cleans outputDir at test start (reporter JSON must live outside it), trace/video nest in per-test subdirs (resolved by glob), DB dump/restore moved inside the DB container (newer mariadb client refuses non-SSL TCP), Node ≥ 18 requirement documented.

How to test

Requirements: Docker Desktop, Node ≥ 18, TESTRAIL_USERNAME / TESTRAIL_API_KEY in the environment, optionally IMAGIFY_TESTS_API_KEY.

/testrail-run --run-id 1283 --cases 26359,26362   # env auto-provisioned AND torn down by the agent
/testrail-scenarios #<PR>                          # stage cases → review → publish (user-gated)
/testrail-setup --check                            # grounding drift report, no writes
bash bin/qa-env.sh up && bash bin/qa-env.sh down   # environment lifecycle standalone

The run agent never posts to TestRail without an explicit confirmation, and never deletes cached specs without a user choice.

Affected Features & Quality Assurance Scope

No runtime plugin feature is affected — zero PHP changes vs develop (verified: the diff excludes everything outside .claude/, Tests/, bin/, .distignore, .gitignore). QA scope is the pipeline itself: environment provisioning, TestRail API interaction (read + gated write), Playwright execution, and the distribution-zip content (.distignore change — see Risks).

Technical description

Documentation

Reading order for review:

  1. .claude/testrail/translation.mdtranslation doctrine: how prose TestRail cases become Playwright specs (oracle-first reading, step classification, POM → grounded-spec → live-observation noun resolution, plan-before-code, failure-interpretation hierarchy).
  2. .claude/agents/testrail-run-agent.md — the run flow: provision → resolve run → coverage pre-check → per-case (ground → cache check → seed → translate → lint/compile gate → execute → outcome → results ledger → LIFO teardown) → disposition → gated posting → env teardown.
  3. bin/qa-env.sh + Tests/e2e/qa-env/ — ephemeral environments: one nginx + one apache WordPress from bin/build-zip.sh's zip, generated .ai/settings.local.json, DB snapshots for per-case reset. Environments never outlive the run.
  4. .claude/testrail/specs/ — grounded feature specs (drift-stamped via derived_sha, mapped by testrail_sections, per-case apache_cases).
  5. Tests/e2e/testrail-cases/ — the spec compile-cache: one committed .spec.ts per case with a @steps-hash header; unchanged case + fresh grounding → reused with zero re-translation. End of run: user-gated promote-to-CI / keep / delete.

New dependencies

None in the plugin. Tooling-only: Docker (dev machine requirement for TestRail runs), mariadb/wordpress/nginx Docker images pulled at provision time. No composer/npm dependency changes.

Risks

  • .distignore change alters distribution-zip content — mitigated: verified the built zip (2.9 MB, 1,280 files) contains no .ai/, node_modules/, .claude/, or Tests/ paths and installs+activates correctly on both qa-env sites; wordpress.org CI is unaffected (fresh checkout, no npm step). This exclusion list fix should also be backported to develop as a standalone PR (flagged).
  • TestRail writes — gated behind explicit user confirmation in both agents; dedup via refs prevents duplicate cases; duplicate-section guard at publish.
  • Secrets — API keys live in env vars or the gitignored generated config; agents are instructed never to print them; the cookie-leaking zip path is closed.
  • Runaway automation — sequential execution only, pagination caps, bounded repair loops, mandatory env teardown at end of run.

Mandatory Checklist

Code validation

  • I validated all the Acceptance Criteria. If possible, provide screenshots or videos.
  • I triggered all changed lines of code at least once without new errors/warnings/notices.
  • I implemented built-in tests to cover the new/changed code.

Code style

  • I wrote a self-explanatory code about what it does.
  • I protected entry points against unexpected inputs.
  • I did not introduce unnecessary complexity.
  • Output messages (errors, notices, logs) are explicit enough for users to understand the issue and are actionnable.

Unticked items justification

Built-in tests: no product PHP changed, so there is nothing for PHPUnit to cover. The executable pieces were validated by live execution instead: qa-env.sh full lifecycle (up / reset / down), the Playwright config via a real two-case run with evidence, and the two committed specs under Tests/e2e/testrail-cases/ are runnable tests (executed green twice, including after a DB reset). Agent instruction files (.claude/*.md) are not unit-testable by nature.

Additional Checks

  • In the case of complex code, I wrote comments to explain it.
  • When possible, I prepared ways to observe the implemented system (logs, data, etc.)
  • I added error handling logic when using functions that could throw errors (HTTP/API request, filesystem, etc.)

🤖 Generated with Claude Code

@codacy-production

codacy-production Bot commented Jun 28, 2026

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Coverage ∅ diff coverage

Metric Results
Coverage variation Report missing for 3fb41021
Diff coverage diff coverage (50.00%)

View coverage diff in Codacy

Coverage variation details
Coverable lines Covered lines Coverage
Common ancestor commit (3fb4102) Report Missing Report Missing Report Missing
Head commit (7d14a41) 19533 629 3.22%

Coverage variation is the difference between the coverage for the head and common ancestor commits of the pull request branch: <coverage of head commit> - <coverage of common ancestor commit>

Diff coverage details
Coverable lines Covered lines Diff coverage
Pull request (#1160) 0 0 ∅ (not applicable)

Diff coverage is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified: <covered lines added or modified>/<coverable lines added or modified> * 100%

1 Codacy didn't receive coverage data for the commit, or there was an error processing the received data. Check your integration for errors and validate that your coverage setup is correct.

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

Miraeld and others added 3 commits June 29, 2026 01:16
…ess spec

Design-only documents for the upcoming QA automation deep audit:
- canary-testrail-spec.md: consolidated spec covering TestRail scenario generation,
  TestRail run execution via Canary, and Canary E2E integration in the pipeline
- canary-wp-spec.md: Canary × WordPress foundational spec (WP fixtures, agent
  inheritance pattern, session lifecycle reference)

No implementation — brainstorm/audit prep for week of 2026-06-30.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…line

New agents:
- canary-imagify-session-agent: Imagify-specific Canary session runner,
  extends canary-wp-session-agent (user-level) with Imagify URLs, selectors,
  ability slugs, and imagify-abilities fixture
- canary-e2e: drop-in replacement for e2e-qa-tester using Canary CLI;
  same JSON contract + canary_sessions field; reads qa-plan.md P0/P1 flows
- testrail-scenario-agent: analyses PRs → stages YAML → publishes to TestRail
  with refs-based dedup and section auto-creation on confirm
- testrail-run-agent: fetches TestRail run → executes cases via Canary →
  results table → posts pass/fail/blocked on user confirm

New skills:
- /testrail-scenarios: entry point for PR → TestRail scenario generation
- /testrail-run: entry point for TestRail run execution via Canary

Modified:
- qa-engineer: add e2e_mode routing (playwright/canary), write qa-plan.md
  before E2E delegation, route to canary-e2e or e2e-qa-tester accordingly
- orchestrator: add e2e_mode startup selection with canary signal detection

Spec: canary-testrail-spec.md updated with all 13 audit questions resolved.
Also archiving testrail-automation.md (superseded by canary-testrail-spec.md).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…session end

Canary has no --output flag, so artifacts always land in ~/.canary/sessions/<id>/
first. After each session end the agents now mv the directory to
.ai/{issue_N}/canary/<id>/, patch artifactsDir in session.json, and leave a
symlink at the original path so `npx @usecanary/ui` continues to work.

- canary-e2e: add relocation block in Step 3, update all path refs throughout
- canary-imagify-session-agent: add relocation step in Workflow with both
  caller conventions (canary-e2e uses QA_PLAN_PATH, testrail-run-agent uses
  TESTRAIL_RUN_ID)
- canary-wp-spec.md: trim §1 Problem + §2 Vision (historical rationale, no
  value to future sessions), renumber §3-11 → §2-10, add artifact relocation
  note to the primer

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Miraeld and others added 13 commits June 29, 2026 01:29
Remove everything that is explanation-for-humans rather than operative
instruction for AI: design rationale sections, "why we did this" blocks,
stale file-lists and open-question tables, duplicate architecture prose,
implementation-order checklists, and obsolete "optional future" sections.

- canary-wp-spec.md: §2 Architecture collapsed to 3 tight subsections
  (routing, inheritance, artifact locations); removed §7 GitHub Actions,
  §8 open questions, §9 file list; trimmed §5 screenshot shell block,
  §6 TS example, "Why GET not POST" aside
- canary-testrail-spec.md: removed pipeline context block, "How they
  connect" prose, name-change note, both Goal subsections, dedup fallback,
  §6.1/6.2/6.4 (Problem/Vision/design now in agent files), §9 file list,
  §11 implementation order; trimmed §8 replay, §7.1 "why this split",
  §7.3 trust-guard (updated: guard not present); updated artifact paths
  to .ai/testrail/$RUN_ID/canary/
- testrail-run-agent.md: add relocation step after session end (move to
  .ai/testrail/$RUN_ID/canary/); update all path references
- testrail-scenarios/SKILL.md, testrail-run/SKILL.md: remove meta-sentences
- testrail-automation.md: deleted (superseded by canary-testrail-spec.md)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add a TestRail scenario reviewer that runs between generation and publish:
- testrail-review-agent + /testrail-review skill: prune low-signal cases,
  collapse near-duplicates, fix smoke-test flags, and enforce cross-file
  section coherence; moves reviewed YAML pending/ -> reviewed/.
- testrail-scenario-agent: add "quality over quantity" rules (no unit-test
  runner cases, no source-inspection cases, no near-duplicates, smoke-test
  discipline); publish mode now reads from reviewed/.
- testrail-scenarios skill: offer the reviewer after staging; publish reads
  reviewed/.

Add .claude/testrail/A-plus-design.md — the design for making the
TestRail -> browser execution step reliable (grounded Explorer map +
state isolation + anti-hallucination guards + TestRail expected-results as
the assertion oracle). Design only; implementation deferred.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…gent

Implements the A+ design (.claude/testrail/A-plus-design.md): replace the
fragile run-time "prose -> Playwright" guess with grounded, closed-loop
execution.

- testrail-explorer-agent (NEW, opus): drives the live app via Canary,
  captures real locators + seed/teardown + verification criteria per feature,
  writes/refreshes .claude/testrail/specs/*.md stamped with source_files +
  derived_sha; --check drift mode writes nothing.
- /testrail-setup skill (NEW): thin entry point (all / <feature> / --check).
- specs/_foundation.md (NEW): shared non-DOM base, pre-filled with known
  constants; live rows + unverified API-key path marked "captured by Explorer".
- testrail-run-agent (EDIT, sonnet): resolve case -> feature spec via
  whole-element section match, load _foundation + feature spec as grounding,
  seed prerequisites + LIFO teardown (state isolation), assert against the
  TestRail expected-result (oracle), warn on stale grounding.

Reviewed by workflow-reviewer (NEEDS_REVISION -> fixed): anchored the
section-id resolution to whole array elements (872 no longer matches 8724),
canonicalised the "Ground truth" section across explorer/run-agent/design so
destructive markers are actually consumed, documented _foundation as the
always-loaded non-resolvable base, and moved Canary step scripts to
per-session /tmp dirs.

Authoring only — Explorer not yet run; mcp-abilities feature spec is the next
step (needs live wp-env + Canary). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ve explore)

Explorer confirmed: API key lives in `imagify_settings` WP option (not settings.local.json).
Adds authenticated curl helpers for REST/MCP seeding, real attachment IDs, and live setting keys.
Stamps derived_sha=4bc0e342 / last_explored=2026-06-30.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
settings.md: grounded live (Canary session mr0faaqg). Covers sections 32
(Settings) and 7689 (general settings). Real locators captured:
- auto_optimize: getByLabel("Auto-Optimize images on upload"), #imagify_auto_optimize
- save: #submit
- notice: #setting-error-settings_updated (C14169 escaping via wp_kses)
- C174 documented as BLOCKED (requires multisite)
Browser fix: chromium-1208 symlinked to chromium-1217 (extraction bug workaround).

testrail-run skill + agent: add --cases filter to execute only specific
case IDs (comma-separated), enabling targeted runs without burning tokens
on all 66 cases.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… config file loading

- Fold testrail-review-agent into testrail-scenario-agent as a third mode
  (generate/review/publish) — same artifact, same rules, previously duplicated
  near-verbatim between the two files.
- Update testrail-review and testrail-scenarios skills to spawn the merged agent.
- Ground testrail-run-agent on dual nginx/apache environments, config loaded from
  .ai/settings.local.json, WP-CLI env setup, and batch result posting.
- Add mcp-abilities.md and mixpanel-tracking.md grounded specs.
A-plus-design.md's core architecture (Explorer/Executor split, grounded specs,
drift detection, state isolation) is now built in testrail-explorer-agent.md and
testrail-run-agent.md. canary-testrail-spec.md and canary-wp-spec.md were
pre-build design drafts, superseded by the same implementation.
…y case

Add a coverage pre-check (Step 2b) to testrail-run-agent that resolves spec
coverage for the whole filtered test list up front, then asks once per
missing/ambiguous TestRail section whether to generate the spec or block and
continue — instead of surfacing the same BLOCKED reason on every affected case.

The run agent has no Agent-spawning tool by design, so the generate path is
handled by the testrail-run skill: it spawns testrail-explorer-agent for the
named feature(s), then re-invokes testrail-run-agent on the same run/case
selection once grounding is done.
Canary's QuickJS scripting API has near-zero training-data representation
compared to @playwright/test, causing more script-authoring errors than a
well-known tool would. Rewrites testrail-run-agent to generate one real
.spec.ts per TestRail case (test.step()/expect.soft(), executed via
`npx playwright test --reporter=json`) with results published as a live
Artifact dashboard, and testrail-explorer-agent to discover locators via
mcp__playwright instead of the Canary CLI. Deletes canary-e2e.md and
canary-imagify-session-agent.md; e2e-qa-tester (already Playwright-MCP-based)
is now the sole PR-QA path, with e2e_mode/canary-e2e branching removed from
qa-engineer and the orchestrator.

Also rewrites e2e-qa-tester to draft its spec directly from the PR's diff and
existing POMs and run it via the Playwright Test runner, falling back to a
single targeted mcp__playwright snapshot only when a drafted locator or
assertion doesn't hold — cutting the per-action MCP round-trips of a full
interactive walkthrough down to the rare case an assumption is actually wrong.
…QA envs

- .claude/testrail/translation.md: prose→spec.ts thinking protocol the run
  agent must read before translating (oracle-first reading, step
  classification, three-source noun resolution, plan-before-code, failure
  interpretation hierarchy)
- Spec compile-cache: generated case specs live in Tests/e2e/testrail-cases/
  (committed corpus, @steps-hash header decides reuse vs re-translation);
  end-of-run user-gated promote/keep/delete with CI promotion shortlist
- Ephemeral QA envs: bin/qa-env.sh + Tests/e2e/qa-env/ (Docker compose, one
  nginx + one apache WordPress, installs build-zip output, generates
  .ai/settings.local.json, DB snapshots for per-case reset); run agent
  provisions automatically when no reachable config exists
- Live results ledger .ai/testrail/<run>/results.md updated per case
  (replaces per-case Artifact redeploys; crash/turn-budget resumable)
- Audit fixes: trace/video resolved by glob (Playwright nests per-test dirs),
  apache_cases per-case frontmatter (schema + settings.md), empty-apache-path
  guard, merge-base ancestry drift checks, qa-plan contract closed in
  e2e-qa-tester, compile gate before execution, ability slugs corrected to
  the 7 that exist
- Knowledge consolidation: imagify-playwright-fixtures.md folded into
  _foundation.md; hardcoded ports/creds replaced by config-driven $E2E_URL
- Scenario agent: live section fetch + duplicate-section guard at publish;
  automation_hints captured from the PR diff into .claude/testrail/hints.yml

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Live smoke (run 1283, cases 26359/26362 — both PASS with trace+video) surfaced:
- .distignore lacked /.ai, /node_modules, /generatedpackages, /.e2e-screenshots,
  /.e2e-temp — build-zip.sh was packing 240MB of local artifacts (incl. Canary
  browser profiles with cookies) into imagify.zip; now 2.9MB
- compose mounted ../../generatedpackages (= Tests/generatedpackages) — needs
  three levels up to repo root
- TESTRAIL_OUTPUT_DIR must be $OUT/artifacts, never $OUT: Playwright cleans
  outputDir at test start and was deleting results.json mid-run (bug existed in
  the original design too) — run agent + config header updated
- DB snapshot/restore moved inside the DB container (mariadb-dump/mariadb over
  local socket): the wordpress:cli image's newer client refuses non-SSL TCP and
  WP-CLI doesn't reliably forward ssl flags
- qa-env.sh preserves an existing imagify.api_key from the old config when
  IMAGIFY_TESTS_API_KEY is unset
- run agent documents the Node >= 18 requirement (nvm-pinned Node 16 shadows
  Homebrew's Node on Local-user machines)

First two cached case specs committed under Tests/e2e/testrail-cases/
(@status green 2026-07-03), translated per .claude/testrail/translation.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Run agent Step 0 is Docker-only: always provision via bin/qa-env.sh (the
  generated .ai/settings.local.json is the only config source; wp_path /
  hand-maintained Local-site support removed everywhere)
- New Step 0-end: qa-env.sh down is mandatory at the end of every run
  (posting done/declined, disposition, or turn-budget stop) — environments
  never outlive the run; evidence under .ai/ survives teardown
- Specs cleaned of pre-Docker environment references: 'Local site shell' →
  $WPCMD, stale admin/admin creds → loginAsAdmin + generated config, personal
  machine path removed from mcp-abilities source-of-truth note (re-ground via
  /testrail-setup mcp-abilities after 2.3.0 merge)
- _foundation.md env section rewritten for the ephemeral qa-env

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Miraeld Miraeld changed the title feat(qa): Canary + TestRail QA automation pipeline feat(qa): TestRail QA automation — Playwright pipeline with grounded specs, spec cache, and ephemeral Docker envs Jul 3, 2026
Miraeld and others added 2 commits July 3, 2026 03:03
…te_thumbnails

Newer wordpress-stubs no longer flag the wp-admin/includes/image.php
require_once, and PHPStan treats an unmatched ignore as an error
(ignore.unmatchedLine, non-ignorable). composer.lock is gitignored, so CI
installs fresh deps — this was about to start failing on develop too.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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