agentleFS
greglas75greglas75/zuvo/skills/write-e2e/SKILL.md

write-e2e

Skill7 starsChanged 27 days agoMITView on GitHub

Generate Playwright E2E tests from codebase analysis. Discovers routes, scores user flows by criticality, writes .spec.ts files that assert causality, and reports what was actually proven: GENERATED, STATIC_CHECKED, VERIFIED_LOCAL or VALIDATED_LIVE. Modes: --scope <path>, --flow <name>, --output <dir>, --base-url <url>, --max-flows N, --live, --auto, --flows, --dry-run.

1 other project uses a version of this.

Adopted by

Most recently taken up by

The file

We split this skill into the rules it gives an agent

6 rules. About 0 are common, 1 is distinctive, and 5 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈1

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 5

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Record the verdicts: after triage: every finding is fixed, rejected (a false positive) or deferred — record each by its ID: in ONE call, …

    line 195
  2. Very specific:

    Not in PATH: ~/.zuvo/adversarial-review (stable.

    line 201
  3. Distinctive:

    When evidence is missing, record the LOWER state — a spec nobody ran is GENERATED however confident this run feels, and there is no …

    line 221
  4. Very specific:

    Run-verdict ceiling: when no spec in the run reached VERIFIED_LOCAL or VALIDATED_LIVE, the Run: verdict is at most WARN — static checks and …

    line 225
  5. Very specific:

    Run the curation protocol from knowledge-curate.md (WORK_TYPE = "implementation", CALLER = "zuvo:write-e2e", REFERENCE = <git SHA>), then …

    line 262
  6. Very specific:

    Append via wrapper (REQUIRED): Never >> directly to ~/.zuvo/runs.log — the wrapper is the gate that verifies a retro entry exists for this …

    line 284

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

zuvo:write-e2e — E2E Test Generation

Discover routes and user flows, rank them by criticality, generate Playwright specs that assert causality, and — when the project can actually run Playwright — execute them and report exactly what was proven; a spec is a draft until a state on the verification ladder says otherwise. Scope: web apps with routes, forms, interactions. Out of scope: unit tests (zuvo:write-tests), a suite audit (zuvo:test-audit), diagnosing flaky specs (zuvo:debug), batch anti-pattern repair (zuvo:fix-tests).

Argument Parsing

Parse $ARGUMENTS as: [--scope <path>] [--flow <name>] [--output <dir>] [--base-url <url>] [--max-flows N] [--live] [--auto] [--flows] [--dry-run] [--no-install] [--allow-external-origin] [--allow-destructive <ops>]

ArgumentModeFlowsDescription
--scope <path>SCOPED1Restrict discovery to one app root, directory or route file
[path]SCOPED1Deprecated positional alias for --scope — accepted, but prefer --scope <path>
--flow <name>NAMED1Generate exactly the named flow
--output <dir> / --base-url <url>OUTPUT / TARGET—Write specs under <dir> instead of the detected E2E root / name the origin the specs target, classified in Phase 0.5 before any request
--max-flows NLIMITNExplicit flow budget — the only way to ask for a large batch, 20 included
--liveLIVE—Permit live DOM/locator inspection against a permitted origin
--autoAUTO3Skip the selection prompt; take the highest-scored flows
--flows / --dry-runDISCOVER / PREVIEW0Print the scored list and stop / plan through scaffold and write nothing
--no-installNO-BOOTSTRAP—Forbid the BOOTSTRAP_REQUIRED dependency materialization (env alias: ZUVO_E2E_NO_BOOTSTRAP=1); the run caps at STATIC_CHECKED and verdict WARN
--allow-external-originCONSENT 1—Permits READ-ONLY execution against a non-local origin, and is the only thing (besides an exact ZUVO_E2E_STAGING_HOSTS match) that classifies an origin STAGING. Grants nothing mutating
--allow-destructive <ops>CONSENT 2—Authorizes the named mutating operations only, comma-separated. Independent of consent 1 — neither implies the other, and an unnamed operation stays BLOCKED

Both are parsed here because Phase 0.5 refuses to execute without them (semantics: references/live-validation.md). A flag the parser does not recognize is a flag the user cannot grant.

Volume is a safety property: scoped or named requests generate 1 flow, bare --auto generates 3, and a large batch happens only when a human asks by name (--max-flows 20) — 20 is never assumed. Codex, Cursor and Antigravity always behave as --auto (all three take [AUTO-DECISION] in env-compat's interaction row; Kimi does not — it has AskUserQuestion), so a 20-flow default let a non-interactive host emit twenty unreviewed specs with nobody deciding to.

Mandatory File Loading

Load at start, MISSING -> STOP: ../../rules/testing.md, ../../rules/file-limits.md, ../../shared/includes/run-logger.md, ../../shared/includes/env-compat.md, ../../shared/includes/codesift-setup.md, ../../shared/includes/knowledge-prime.md (WORK_TYPE = "implementation"), ../../shared/includes/knowledge-curate.md, ../../shared/includes/retrospective.md. The six references are LAZY — load each when its phase begins, never all at start:

  • Phase 0, Phase 0.5, Phase 4 — references/live-validation.md: preflight states, origin classes, consents, validation states, triage, registry
  • Phase 1 — references/discovery-and-scoring.md: discovery targets, five weighted signals, tiers, flow budget, confidence
  • Phase 2 — references/scaffold.md: write policy, output structure, page-object threshold, auth fixture, ID suggestions
  • Phase 3, per scenario — references/playwright-patterns.md: causality contract, oracles, locator hierarchy, gray-box labels, cleanup
  • Phase 3, when a spec touches the network — references/network-mocking.md: fail-closed policy, match key, allowed-host list, mutation contracts
  • Phase 3, after each spec is written — references/quality-gates.md: E2E-Q1..E2E-Q10, all critical, with evidence formats
  • Phase 4.5 — ../../shared/includes/test-quality-gate.md: the zuvo:test-audit → tier A gate (dispatched with --include-e2e)

Agent Routing

AgentPurposeModelTypePhase
Route DiscovererRoutes, components, API endpoints, auth patternsSonnetExplore1 (parallel)
Coverage AnalyzerExisting specs, already-covered flowsHaikuExplore1 (parallel)
Test WriterGenerate specs for a flow batchSonnetCode2-3

Phase 0: Preflight — can Playwright run here

Load references/live-validation.md and ask the helper, never guess: when $HOME/.zuvo/e2e-preflight is executable, ~/.zuvo/e2e-preflight probe <scope> prints exactly one of READY, GENERATE_ONLY or BOOTSTRAP_REQUIRED on stdout, reasons on stderr.

Helper absent (a Codex-only or Cursor-only install never ran install_zuvo_home) — reproduce the SAME three states, READY / GENERATE_ONLY / BOOTSTRAP_REQUIRED, from the four signals in that reference: config, dependency, runnable local binary, browser cache. Never invent a fourth outcome, never read a missing helper as READY, and never probe through unpinned npx playwright, which installs the thing it claims to detect.

  • READY — generate AND execute locally; VERIFIED_LOCAL is reachable.
  • GENERATE_ONLY — generate and static-check only; the ceiling is STATIC_CHECKED, and the report says so.
  • BOOTSTRAP_REQUIRED — the project DECLARES Playwright; only the binary/browsers are not materialized. Bootstrap it and run the tests — a spec nobody executed is unverified work, and materializing a project's own pinned dependencies is normal dev workflow, not an environment mutation. Protocol in references/live-validation.md (lockfile-respecting install + <pm> exec playwright install <browser>, then re-probe → READY). Hard limits unchanged: never add or upgrade a dependency, never mutate a lockfile, never unpinned npx — those stay a conscious human decision. Skip only on --no-install / ZUVO_E2E_NO_BOOTSTRAP=1, or when the install itself fails (sandbox, no network): then print the exact commands for the user to run, cap at STATIC_CHECKED, and cap the run VERDICT at WARN.

Also detect the CodeSift index (else repository search), Playwright MCP (which gates live DOM inspection ONLY — a local playwright test run needs no MCP), the framework and the auth provider, then print PREFLIGHT: <state> | CODESIFT: <..> | MCP: <..> | STACK: <..> | AUTH: <..>. Backlog snapshot. Per backlog-protocol.md "Filing from a skill", Committing what backlog-add wrote (read that section now, before the run's first finding): once, at the run's original start, before any backlog-add call, run R='<absolute repo root>'; git -C "$R" status --porcelain -uall -- memory/, R being the root of the checkout under test (a linked worktree's own root), assigned in the same shell call. Print the output verbatim under a backlog-snapshot: heading as a fenced block (an empty snapshot is an empty block; never one line). This skill has no resume path of its own: a run that cannot find a printed snapshot takes no new one, and if the snapshot command fails the run stages nothing at the end and lists its wrote= paths.

Phase 0.5: Origin gate — every run that executes anything

Load references/live-validation.md and resolve the EFFECTIVE base URL before a single request: --base-url when given, otherwise the project's own playwright.config.* (use.baseURL, and the webServer.url a run would start). A default, flagless run executes against whatever that config points at — staging or production included — so it is classified exactly like a --live run: Origin: LOCAL | STAGING | EXTERNAL_UNKNOWN:

  • LOCAL — matches the local shape AND resolves to a permitted local destination; a name is never evidence about where traffic lands.
  • STAGING — explicit only: --allow-external-origin, or an exact ZUVO_E2E_STAGING_HOSTS match. No hostname heuristics; adding one is a defect.
  • EXTERNAL_UNKNOWN — everything else, including hosts that merely look internal: read-only specs, and every mutating step is BLOCKED rather than warned about.

Two consents, never one: --allow-external-origin permits reaching a non-local origin read-only, --allow-destructive <ops> authorizes the named mutating operations. A resolved baseURL classified STAGING or EXTERNAL_UNKNOWN takes that same path whether or not --live was passed — read-only under the first consent, mutations only under the second, otherwise the run BLOCKS before executing anything. Classification is continuous — redo it on every navigation and redirect hop, and fail loudly on a transition into EXTERNAL_UNKNOWN instead of degrading into a quiet read-only run.

Phase 1: Discover and score

Load references/discovery-and-scoring.md. Run route discovery and coverage analysis in parallel — both finish before scoring, because "already covered" is a scoring input. Score each candidate on the five weighted signals (mutation 30, auth 20, sensitivity 20, traffic 15, uncovered 15) and label its confidence, which measures how well the flow is understood and never how the markup is written. Print the ranked list with score, confidence and a one-line reason, apply the flow budget, then take the selection. --flows stops here; --dry-run continues to the Phase 2 plan and stops before writing.

Phase 2: Scaffold

Load references/scaffold.md. New specs, fixtures and page objects are written directly; playwright.config.* changes are proposed, never auto-written; existing test files are never modified; production source is never edited to make a test pass, so suggested test IDs stay suggestions. --output <dir> overrides the detected E2E root, and an E2E layout the project already has wins over the default tree.

Phase 3: Generate

For each selected flow, in this order:

  1. Causality contract first — references/playwright-patterns.md: trigger, decisive event, pre-state, post-state, visible oracle, cleanup, filled in BEFORE any spec code. A scenario that cannot be filled in is a testability-gap finding, not something to improvise around, and specs reaching into internals are labeled CHARACTERIZATION/GRAYBOX.
  2. Network policy — references/network-mocking.md whenever the spec touches the network: fail-closed default, hostname+method+pathname match key, an explicit allowed-host list, mutation body/query/header validation, and no broad directory globs.
  3. Gate check — references/quality-gates.md: score each spec against E2E-Q1..E2E-Q10. All ten are critical — fix in-run, or emit the spec BLOCKED with the failure recorded. Every gate needs an evidence line; a gate with no evidence counts as NOT RUN.

Phase 3.5: Adversarial Review (MANDATORY — do NOT skip)

# Scoped review patch on stdout — the git index is NEVER touched (no staging).
# PATH args = the E2E specs and fixtures this run wrote.
# Quote each SEPARATELY — never one space-joined string or a bare $FILES: zsh does
# not word-split an unquoted expansion, so the helper gets the whole list as ONE
# path, matches nothing and exits 2. Use "${FILES[@]}" for an array.
# With NO PATH args the helper reviews the WHOLE dirty tree, untracked files
# included, and that content is sent to the external providers — always scope it.
# `|| _prc=$?` (never `; _prc=$?`): under `set -e` the plain form aborts the shell
# at the assignment, so exit 3 and the BLOCKED branch would never be reached.
# The BLOCKED branch ends in `false`, so the block's own exit status is non-zero:
# printing alone lets a `set -e` / `if ! …` caller sail past a review that never
# ran. `false`, not `exit`, so an inlining caller's shell is not killed.
if [ -x "$HOME/.zuvo/build-review-patch" ]; then
  _prc=0; _patch=$("$HOME/.zuvo/build-review-patch" "<spec-file-1>" "<fixture-file>") || _prc=$?
  if [ "$_prc" -eq 3 ]; then echo "adversarial review: skipped (no changes)"
  elif [ "$_prc" -ne 0 ]; then echo "BLOCKED: build-review-patch failed (rc=$_prc). Adversarial review did NOT run; do NOT proceed to commit and do NOT report this skill complete" >&2; false
  else printf '%s\n' "$_patch" | ~/.zuvo/adversarial-review --mode test; fi
else
  ~/.zuvo/adversarial-review --mode test --files "<changed files>"
fi

Record the verdicts after triage: every finding is fixed, rejected (a false positive) or deferred — record each by its ID: in ONE call, ~/.zuvo/adversarial-review --record-disposition ID-1 fixed --record-disposition ID-2 rejected …, per ../../shared/includes/adversarial-loop.md Step 4.9 (the review lists its IDs on stderr).

Dispatch follows ../../shared/includes/execution-policy.md through env-compat. Reuse existing authorization within that policy; session restrictions take precedence. Run each required gate and report its actual independence or an unmet requirement.

Not in PATH: ~/.zuvo/adversarial-review (stable; the versioned cache path breaks after any release). Wait for complete output, then act on findings: CRITICAL — confirm first (two or more lanes in its .agreement group, or your own check against the code), then fix immediately; an unconfirmed single-lane CRITICAL is triaged as a WARNING (adversarial-loop.md Step 4); WARNING — fix if localized (< 10 lines), else record it (Adversarial review, backlog-protocol.md "Filing from a skill"): ~/.zuvo/backlog-add --repo "$R" --class <C> --conf <N> --source write-e2e --file "$F" --line <n> [--anchor "$A"] --title "$T"; INFO — record, max 3. Confidence measures how sure the reviewer is, not how important the issue is, and "pre-existing" is no reason to skip a finding: in a file this run is already editing, fix it now; in any other file record it with ~/.zuvo/backlog-add --repo "$R" --class <C> --conf <N> --source drive-by:write-e2e --file "$F" --line <n> [--anchor "$A"] --title "$T" (drive-by-findings.md). Assign "$R", "$F" and "$T" before each call, and "$A" only when you pass --anchor (required for BUG, SEC or DATA), as "Free text goes through a variable" in that section says; an empty value you pass is a bug, and backlog-add refuses a missing required field (REFUSED missing-field:<f>).

Phase 4: Verification ladder

Load references/live-validation.md. A flow carries exactly ONE state, earned by evidence:

StateEarned byRequires
GENERATEDthe spec was written; nothing executed or parsed—
STATIC_CHECKEDit parses/typechecks/lints clean and passes the E2E-Q gatesalways attempted
VERIFIED_LOCALexecuted green with playwright testpreflight READY AND the Phase 0.5 classification of the resolved baseURL is LOCAL — no MCP
VALIDATED_LIVEexecuted green with live DOM/locator inspection confirming the oracles--live + Playwright MCP + an origin Phase 0.5 permits
BLOCKEDan origin, consent or preflight gate refused the worka stop, never a pass
FAILEDexecuted and went redtriage, never hide

When evidence is missing, record the LOWER state — a spec nobody ran is GENERATED however confident this run feels, and there is no "skipped" state. Triage red runs by category (locatorMiss / timing / data / auth / backend), which this skill diagnoses and does not auto-fix.

Run-verdict ceiling: when no spec in the run reached VERIFIED_LOCAL or VALIDATED_LIVE, the Run: verdict is at most WARN — static checks and source-quality gates, however clean, never make an unexecuted test a PASS.

Phase 4.5: Test Quality Gate (zuvo:test-audit --include-e2e → tier A)

The E2E-Q1..Q10 gates and the adversarial pass are in-run scoring — the same self-evaluation that ships weak tests elsewhere. After the verification ladder (states earned, behavior proven), run the gate from ../../shared/includes/test-quality-gate.md with two E2E-specific adjustments:

  • TEST_SCOPE = every spec and fixture file this run created or modified (write-e2e touches no production files, so there is no covering-tests half here). The dispatch MUST carry --include-e2e — test-audit excludes */e2e/* / *.e2e.* by default, so without the flag the audit silently scores nothing and a clean report is a lie: Skill(skill="zuvo:test-audit", args="<TEST_SCOPE files> --include-e2e --deep --read-only --commit=off").
  • A fixed spec re-earns its state. Editing a spec stales whatever the ladder recorded for it — after each fix iteration re-run at least the STATIC_CHECKED checks on the touched specs, and re-execute any spec that held VERIFIED_LOCAL/VALIDATED_LIVE (or honestly downgrade its row to GENERATED/STATIC_CHECKED in memory/e2e-coverage.md). A tier-A spec wearing a state its current content never earned trades one lie for another.

Everything else follows the include verbatim: fix below-A in-run (strengthening only; FIX_COMMIT_PREFIX = test(e2e): when this run commits, otherwise the fixes stay in the working tree with the specs), max 2 fix→re-audit iterations, [GATE: test-quality] PASS|WARN|N/A with the on-disk zuvo/audits/ report path as proof of dispatch — an inline E2E-Q rescoring pass reported as this gate is a substituted gate = INVALID. Below-A after the cap → WARN, each such file recorded once as the include's step 5 says (Generic write, whole-file), never silence. In --dry-run/--flows mode (nothing written) print [GATE: test-quality] N/A (no specs written). Closing commit. After the run's last backlog-add call (the Phase 3 adversarial review's and this gate's, the only call sites; nothing after Phase 4.5 calls it), a run that commits makes the one closing commit, as backlog-protocol.md "Filing from a skill", Committing what backlog-add wrote, says, against the backlog-snapshot: Phase 0 printed. A run that does not commit stages nothing and lists the wrote= paths in the report. Run by another skill, it makes no commit and prints one line, Backlog writes: <path>, … (or Backlog writes: none).

Artifact Contract

State lands in memory/e2e-coverage.md, which carries a State column. Row shape, exactly what the helper writes: | Flow ID | Name | Score | Confidence | Status | Spec File | Last Updated | State |. Write it with "$HOME/.zuvo/e2e-preflight" coverage-upsert --file memory/e2e-coverage.md --flow <id> --state <STATE> --spec <path> --score <N> --confidence <LEVEL>; when that helper is absent (the same Codex-only or Cursor-only install as Phase 0) append the row by hand in that exact format, so the table stays machine-readable and the completion gate stays satisfiable — the helper is never a prerequisite. A legacy row with no State cell reads as GENERATED, never back-filled.

Knowledge Curation and Retrospective (REQUIRED)

Run the curation protocol from knowledge-curate.md (WORK_TYPE = "implementation", CALLER = "zuvo:write-e2e", REFERENCE = <git SHA>), then the retrospective protocol from retrospective.md (gate check -> questions -> TSV emit -> markdown append); if it skips, print RETRO: skipped (trivial session) and proceed.

Completion Gate Check and Report

COMPLETION GATE CHECK
[ ] Preflight state printed; effective baseURL resolved and its origin classified before anything executed
[ ] Scored flow list printed with confidence and reasons; E2E-Q1..E2E-Q10 evidenced on every spec
[ ] Adversarial review ran (--mode test); every flow carries exactly one verification state
[ ] Test Quality Gate (Phase 4.5): [GATE: test-quality] PASS|WARN|N/A printed with a REAL zuvo/audits test-audit report path from a dispatch that carried --include-e2e (inline E2E-Q rescoring is a substituted gate = INVALID); below-A specs fixed in-run + states re-earned, or WARN with each one recorded through backlog-add, and the closing commit made, or none: nothing to stage, or the `Backlog writes:` line printed (run by another skill)
[ ] memory/e2e-coverage.md upserted via the helper, or hand-written in the row shape when it is absent
[ ] Run: line printed and appended to log

WRITE-E2E COMPLETE
Preflight: [READY | GENERATE_ONLY | BOOTSTRAP_REQUIRED]   Origin: [LOCAL | STAGING | EXTERNAL_UNKNOWN | n/a]
Flows:     [N] generated ([M] high, [K] medium) | [X] spec + [Y] fixture files | gates [N]/[N] evidenced
States:    [a] GENERATED, [b] STATIC_CHECKED, [c] VERIFIED_LOCAL, [d] VALIDATED_LIVE, [e] BLOCKED, [f] FAILED
Run: <ISO-8601-Z>	write-e2e	<project>	-	<Q>	<VERDICT>	<TASKS>	<DURATION>	<NOTES>	<BRANCH>	<SHA7>	<INCLUDES>	<TIER>

Append via wrapper (REQUIRED). Never >> directly to ~/.zuvo/runs.log — the wrapper is the gate that verifies a retro entry exists for this run. Order: retro bash executed → wrapper invoked → completion claimed, via printf '%b\n' "$RUN_LINE" | ~/.zuvo/append-runlog, which must print OK: appended to runs.log (retro verified for <skill> on <project>). On exit 2 with RETRO_REQUIRED, execute the retro bash from retrospective.md first; never bypass with ZUVO_SKIP_RETRO_GATE=1. After it succeeds, print a Logs: evidence line (tail -1 ~/.zuvo/retros.log, grep -c "^<!-- RETRO -->" ~/.zuvo/retros.md, tail -1 ~/.zuvo/runs.log). <DURATION>: N-flows; <Q>: gate score or -; <TASKS>: spec files written.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.