Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Plans and scaffolds e2e tests in packages/sdk/e2e for a new or changed public SDK API. Use when adding or modifying SDK functionality that is exposed to consumers. Enforces happy / sad / error coverage, deterministic model-output assertions, desktop/mobile/Electron consumer coverage, smoke-suite selection, and local validation with run:local.
.claude/skills/tetherto-qv-sdk-e2e-create/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-07 | ✗→✓ | ▲ Improved | 113% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 138% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 70% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 65% | 0% |
| case-12 | ✗→✓ | ▲ Improved | 121% | 0% |
Plan and scaffold e2e tests in packages/sdk/e2e for a new or changed SDK feature exposed through the public API.
Applies to SDK changes in packages/sdk/ that touch the public API surface.
Use when:
/qv-sdk-e2e-create.Do NOT use for:
packages/sdk/ (this skill covers only the e2e suite under packages/sdk/e2e).Investigate first, then propose a concrete plan. Only ask the user for information that cannot be recovered from code or context.
any existing examples under packages/sdk/examples/ or tests under packages/sdk/e2e/tests/.
tests/test-definitions.ts andits executor. Mirror its style unless there's reason to deviate.
and a specific prompt/input that makes the output deterministic enough to assert.
consumer constraints").
definitions sketch, placement decision, desktop/mobile/Electron registrations, platform concerns, smoke pick. Ask clarifying questions only where genuine ambiguity remains (e.g. expected model behaviour on an edge case, preferred tolerance for a numeric-range).
for every consumer. Always run run:local:electron --filter <feature>- to verify either the handler or an intentional skip for Electron/Snap.
For any feature that invokes a model, do not default to shape-only checks. type validation proves nothing about model correctness and must be a last resort.
Pick the strongest achievable strategy:
temperature: 0, fixed seed,top_k: 1) so a known token must appear. Assert with contains-all. _Example:_ prompt "Reply with only the word APPLE." → assert result contains APPLE.
contains-any.numeric-range. Pick bounds tolerant to minor model drift.
regex. Keep thepattern anchored and stable.
type with minLength. Flag as weak coverage in the plan.throws-error with a substring that is stable across SDK versions.function — use for deterministic but non-trivial checks like cosine similarity against areference vector.
If the model's output is inherently non-deterministic and cannot be constrained, say so in the plan and justify why shape-only or range-based coverage is the best achievable — do not silently ship a weak assertion.
Every public-API feature MUST have at minimum:
accepted input, unusual but valid locale, streaming vs non-streaming).
matchable message via throws-error.
More cases are encouraged for multi-branch features.
Executor placement (from .cursor/rules/sdk/e2e.mdc):
tests/shared/executors/.node:fs, node:path, process.cwd(), or other Node-only APIs → tests/desktop/executors/.Platform, bundled assets, or anything specific to React Native → tests/mobile/executors/.Never import node:* from tests/shared/ or tests/mobile/.
Placement under tests/shared/executors/ does not register an executor automatically. Explicitly check and update every compatible consumer:
tests/desktop/consumer.tstests/mobile/consumer.tstests/electron/consumer.ts — also used by the Snap consumerIf a consumer cannot support the tests, add a documented SkipExecutor; do not leave scheduled tests without a matching handler, which fails at runtime with No handler found.
Mobile concerns to address in the plan:
mobile, or a SkipExecutor entry.
node:fs is unavailable. Assets must be bundled via qvac-test.config.js →consumers.mobile.assets.patterns.
unsupported). Add a SkipExecutor at the top of tests/mobile/consumer.ts with a clear reason.
If the feature cannot run on mobile at all, document the skip reason. Evaluate Electron/Snap compatibility independently rather than treating desktop-only coverage as automatic.
suites: ["smoke"] if the feature has no existing smoke coverage.tests/<feature>-tests.ts)tsimport type { TestDefinition } from "@qvac/qvac-test-suite"; export const <feature>Tests: TestDefinition[] = [ { testId: "<feature>-happy", params: { /* canonical input */ }, expectation: { validation: "contains-all", contains: ["EXPECTED_TOKEN"] }, suites: ["smoke"], // only if this test qualifies metadata: { category: "<feature>", estimatedDurationMs: 10_000 }, }, { testId: "<feature>-edge", params: { /* boundary case */ }, expectation: { validation: "type", expectedType: "string" }, metadata: { category: "<feature>", estimatedDurationMs: 10_000 }, }, { testId: "<feature>-error", params: { /* invalid input */ }, expectation: { validation: "throws-error", errorContains: "specific message" }, metadata: { category: "<feature>", estimatedDurationMs: 2_000 }, }, ];
Register in tests/test-definitions.ts:
tsimport { <feature>Tests } from "./<feature>-tests.js"; // ... export const allTests: TestDefinition[] = [ // ... ...<feature>Tests, ];
Extend AbstractModelExecutor (base: tests/shared/executors/abstract-model-executor.ts) or use createExecutor with TestHandler for ad-hoc cases. Bind handlers per testId, and use ResourceManager.ensureLoaded("<resource-name>") to obtain model IDs.
Register the new executor in every compatible handlers: [...] array: desktop, mobile, and Electron (which also covers Snap). Add a documented SkipExecutor for intentionally unsupported consumers.
After scaffolding, provide the exact commands for every compatible local consumer. Do not mark the task complete until the user confirms those tests pass locally.
bashcd packages/sdk/e2e # If SDK source changed npm run install:build:full # Otherwise (only test code changed, SDK already built) npm run install:build npx qvac-test run:local:desktop --filter <feature>- npx qvac-test run:local:electron --filter <feature>- # verifies the handler or intentional skip
For mobile verification of a smoke candidate (required before tagging suites: ["smoke"]):
bashnpx qvac-test run:local:android --filter <feature>- # or run:local:ios
| Validation | Use for | Notes | | ------------------------------ | --------------------------------------------- | ------------------------------------------- | | contains-all / contains-any | Keyword or closed-set answers | Preferred over type when achievable | | regex | Structured output (JSON keys, date, lang ID) | Keep pattern anchored and stable | | numeric-range | Scores, latencies, embedding magnitude | Pick bounds tolerant to minor model drift | | type (+ minLength) | Last-resort shape check | Shallow; flag as weak coverage | | throws-error | Every error path | errorContains must be stable across bumps | | function | Complex deterministic checks | |
Before presenting the plan:
type.filesystem / platform concerns addressed.
--filter prefix.Before marking scaffolding complete:
tests/test-definitions.ts.when compatible (which also covers Snap).
.cursor/rules/sdk/e2e.mdc andpackages/sdk/e2e/README.md.
@qvac/qvac-test-suite dist/schemas/expectations.js.packages/sdk/e2e/tests/translation-salamandra-tests.ts(contains-any over expected Spanish tokens).
packages/sdk/e2e/tests/vision-tests.ts (throws-error with errorContains).packages/sdk/e2e/tests/completion-tests.ts (type: "string").| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-06 | pass→pass | 12,848 | 6,212 | -52% | 1 | 1 | 0% | 2,386 | 4,040 | +69% | 0 | 0 | — |
case-01 | fail→fail | 27,921 | 5,081 | -82% | 1 | 1 | 0% | 5,624 | 2,903 | -48% | 0 | 0 | — |
case-02 | fail→fail | 24,280 | 4,553 | -81% | 1 | 1 | 0% | 4,599 | 2,890 | -37% | 0 | 0 | — |
case-03 | fail→fail | 14,004 | 31,751 | +127% | 1 | 1 | 0% | 2,374 | 2,930 | +23% | 0 | 0 | — |
case-04 | pass→pass | 13,988 | 5,002 | -64% | 1 | 1 | 0% | 2,549 | 3,546 | +39% | 0 | 0 | — |
case-05 | pass→pass | 13,473 | 4,940 | -63% | 1 | 1 | 0% | 2,325 | 3,493 | +50% | 0 | 0 | — |
case-07 | fail→pass | 13,172 | 10,149 | -23% | 1 | 1 | 0% | 2,108 | 4,481 | +113% | 0 | 0 | — |
case-08 | pass→pass | 10,179 | 9,503 | -7% | 1 | 1 | 0% | 1,732 | 3,732 | +115% | 0 | 0 | — |
case-09 | fail→pass | 10,282 | 8,760 | -15% | 1 | 1 | 0% | 1,793 | 4,273 | +138% | 0 | 0 | — |
case-10 | fail→pass | 11,137 | 3,795 | -66% | 1 | 1 | 0% | 1,974 | 3,357 | +70% | 0 | 0 | — |
case-11 | fail→pass | 12,554 | 3,796 | -70% | 1 | 1 | 0% | 1,989 | 3,289 | +65% | 0 | 0 | — |
case-12 | fail→pass | 10,242 | 4,636 | -55% | 1 | 1 | 0% | 1,606 | 3,552 | +121% | 0 | 0 | — |
case-13 | fail→pass | 11,693 | 5,223 | -55% | 1 | 1 | 0% | 1,958 | 3,651 | +86% | 0 | 0 | — |
case-14 | pass→pass | 14,145 | 4,709 | -67% | 1 | 1 | 0% | 2,321 | 3,505 | +51% | 0 | 0 | — |
case-15 | fail→pass | 5,533 | 2,937 | -47% | 1 | 1 | 0% | 1,004 | 3,180 | +217% | 0 | 0 | — |
case-16 | fail→pass | 6,969 | 2,143 | -69% | 1 | 1 | 0% | 1,159 | 3,068 | +165% | 0 | 0 | — |
case-17 | pass→pass | 7,898 | 4,138 | -48% | 1 | 1 | 0% | 1,436 | 3,438 | +139% | 0 | 0 | — |
case-18 | fail→pass | 15,570 | 3,674 | -76% | 1 | 1 | 0% | 2,332 | 3,323 | +42% | 0 | 0 | — |
case-19 | fail→pass | 10,744 | 3,677 | -66% | 1 | 1 | 0% | 1,861 | 3,330 | +79% | 0 | 0 | — |
case-20 | fail→pass | 12,620 | 9,103 | -28% | 1 | 1 | 0% | 2,010 | 4,107 | +104% | 0 | 0 | — |
case-21 | pass→pass | 12,456 | 4,813 | -61% | 1 | 1 | 0% | 2,058 | 3,505 | +70% | 0 | 0 | — |
case-22 | fail→pass | 15,420 | 6,287 | -59% | 1 | 1 | 0% | 2,565 | 3,714 | +45% | 0 | 0 | — |
case-23 | fail→pass | 12,408 | 7,493 | -40% | 1 | 1 | 0% | 1,826 | 4,019 | +120% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 23 cases were attempted, and 20 counted toward the lift figure. The other 3 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +57 percentage points is the difference between those two pass rates over the 20 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.