Generated banner for Convex Verify
Generated logo for Convex Verify

Convex Verify

ConvexOfficialPublished by Convex

  • Skill
  • Review and audit
  • Software development
  • Apache-2.0
  • No issues found

From the author

Prove a Convex feature works — seed, drive as multiple mocked users via convex-test, assert behavior including the negative authz cases (wrong user refused, data-scope enforced).
— the description by Convex

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

This skill provides a rigorous methodology for verifying Convex application features using the convex-test framework. It moves beyond simple unit testing by implementing a seed-drive-assert loop that validates both positive functionality and critical negative authorization cases. By simulating multiple identities—including owners, unauthorized users, and unauthenticated callers—within an in-process environment, it ensures that data-scope and access control logic are correctly enforced before deployment.

What it does

  • Configures the necessary testing environment for convex-test and vitest.
  • Seeds realistic multi-user data to enable cross-user access testing.
  • Simulates different user identities to verify function behavior.
  • Executes positive assertions to confirm expected data access and mutations.
  • Executes negative assertions to ensure unauthorized users are correctly blocked.
  • Reports findings and identifies potential authorization defects.

When to use it

  • When you need to prove a specific Convex query, mutation, or action works as intended.
  • When you want to verify that authorization logic correctly prevents unauthorized access.
  • When you need to ensure that data-scope constraints are strictly enforced.
  • When you want to catch authorization bugs that happy-path testing misses.

Advantages

  • Validates security and authorization logic in addition to functional correctness.
  • Runs in-process without requiring deployment, enabling fast feedback loops.
  • Catches critical authorization holes by testing negative scenarios.
  • Ensures consistent testing patterns across different user identities.

Usage examples

“Verify the new 'updateProfile' mutation for the user profile feature.”
The agent will set up the test environment, seed data for the owner and a second user, and run assertions to ensure the owner can update their profile while the second user is denied.
“Check if my 'getPrivateMessages' query is leaking data to other users.”
The agent will create a test case that seeds messages for two different users and asserts that the query only returns messages belonging to the authenticated caller.

Install

  1. Download the skill as a ZIP:

    https://mp-staging.autogineer.com/agent-skills/convex/convex-verify/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip convex-verify.zip -d .agents/skills

Documentation by Convex

<!-- GENERATED from convex-agents content/capabilities/convex-verify.json — do not edit by hand. -->

Prove a feature works — seed, drive, assert

A green typecheck proves the code parses; it does not prove a non-owner is actually denied, that a query returns the right rows, or that a mutation has the effect it claims. This capability closes that gap with the loop the whole field is missing: seed → drive → assert, run in-process with convex-test so it needs no deployment. Its highest-value assertions are the NEGATIVE ones — the caller who should be refused — because those are exactly the authz defects the 30-app corpus shows are the #1 real bug and the ones a happy-path demo never catches.

Workflow

  1. IDENTIFY the feature to prove: the specific exported query/mutation/action (or a small set) the user just built/changed, and its intended behavior — who should be allowed, what data should come back, what a mutation should change. If the intent is unstated, ask one focused question rather than guessing the contract.

  2. SET UP convex-test: ensure convex-test + vitest are dev deps AND a vitest.config.ts sets test.environment: "edge-runtime" with server.deps.inline: ["convex-test"] — WITHOUT that config, convexTest(schema) fails at runtime with import.meta.glob is not a function (verified). Also install @edge-runtime/vm. Then convexTest(schema) gives a t handle. Reuse the project's existing test setup if present (compose with the test capability, don't fork it).

  3. SEED realistic data through the app's OWN functions where possible (so the seed exercises the same validators/mutations a real user would), falling back to t.run(async (ctx) => ctx.db.insert(...)) for fixtures the public API can't create. Seed at least: the caller's own rows AND a second user's rows, so cross-user access is testable.

  4. DRIVE the feature as DIFFERENT identities with t.withIdentity({ subject, tokenIdentifier, ... }): call the function as (a) the legitimate owner, (b) a different authenticated user, and (c) unauthenticated (t with no identity). Use the real identity shape the app's auth uses (subject/tokenIdentifier), matching how ownership is resolved.

  5. ASSERT behavior — POSITIVE and NEGATIVE:

    • positive: the owner gets the expected rows / the mutation made the expected change (expect(await t.withIdentity(owner).query(api.x.y, args)).toEqual(...)).

    • NEGATIVE (the load-bearing half): a different user calling the same function is REFUSED — await expect(t.withIdentity(other).mutation(api.x.cancel, {id})).rejects.toThrow(/forbidden|not authorized|403/) — and an unauthenticated caller is refused where auth is required. A feature is not proven until the wrong caller is shown to be blocked.

    • data-scope: a list/query returns ONLY the caller's rows, never the second user's (assert the second user's row is absent).

  6. RUN the tests (npx vitest run) and report: what was proven (each positive + negative assertion that passed), and — critically — any assertion that FAILED, because a failed negative assertion is a real authz hole found before ship. Emit findings on the bus (specs/finding.schema.json, class authz/correctness, evidence kind probe-result with the exact failing call) for anything that didn't behave.

  7. Do NOT weaken a test to make it pass: if the owner-only query returns another user's row, the FIX is in the function (hand to convex-authz), not in the assertion. A test changed until it's green proves nothing.

Rules

  • Prove behavior, not compilation: every verification includes at least one NEGATIVE assertion (a caller who should be refused is refused) — the happy path alone is not proof.

  • Drive the feature as multiple identities with t.withIdentity (owner, other user, unauthenticated) using the app's real subject/tokenIdentifier shape.

  • Seed both the caller's rows AND a second user's rows so cross-user access and data-scope are actually testable.

  • A vitest.config.ts with environment 'edge-runtime' + convex-test inlined is REQUIRED for convex-test to run (import.meta.glob needs it); author it, don't just author the test file.

  • Run in-process with convex-test — no deployment needed; compose with the test capability's setup rather than forking it.

  • Never weaken an assertion to make it pass: a failing negative test is a real defect → hand the fix to convex-authz/convex-expert, don't edit the test until it's green.

  • Emit a bus finding for any assertion that failed (authz/correctness, evidence: the failing probe call) so a composite pass or self-heal can pick it up.

  • This drives a SPECIFIC built feature; a request to set up a test framework generally is the test capability.

Files

SKILL.md

<!-- GENERATED from convex-agents content/capabilities/convex-verify.json — do not edit by hand. -->

Prove a feature works — seed, drive, assert

A green typecheck proves the code parses; it does not prove a non-owner is actually denied, that a query returns the right rows, or that a mutation has the effect it claims. This capability closes that gap with the loop the whole field is missing: seed → drive → assert, run in-process with convex-test so it needs no deployment. Its highest-value assertions are the NEGATIVE ones — the caller who should be refused — because those are exactly the authz defects the 30-app corpus shows are the #1 real bug and the ones a happy-path demo never catches.

Workflow

  1. IDENTIFY the feature to prove: the specific exported query/mutation/action (or a small set) the user just built/changed, and its intended behavior — who should be allowed, what data should come back, what a mutation should change. If the intent is unstated, ask one focused question rather than guessing the contract.

  2. SET UP convex-test: ensure convex-test + vitest are dev deps AND a vitest.config.ts sets test.environment: "edge-runtime" with server.deps.inline: ["convex-test"] — WITHOUT that config, convexTest(schema) fails at runtime with import.meta.glob is not a function (verified). Also install @edge-runtime/vm. Then convexTest(schema) gives a t handle. Reuse the project's existing test setup if present (compose with the test capability, don't fork it).

  3. SEED realistic data through the app's OWN functions where possible (so the seed exercises the same validators/mutations a real user would), falling back to t.run(async (ctx) => ctx.db.insert(...)) for fixtures the public API can't create. Seed at least: the caller's own rows AND a second user's rows, so cross-user access is testable.

  4. DRIVE the feature as DIFFERENT identities with t.withIdentity({ subject, tokenIdentifier, ... }): call the function as (a) the legitimate owner, (b) a different authenticated user, and (c) unauthenticated (t with no identity). Use the real identity shape the app's auth uses (subject/tokenIdentifier), matching how ownership is resolved.

  5. ASSERT behavior — POSITIVE and NEGATIVE:

    • positive: the owner gets the expected rows / the mutation made the expected change (expect(await t.withIdentity(owner).query(api.x.y, args)).toEqual(...)).

    • NEGATIVE (the load-bearing half): a different user calling the same function is REFUSED — await expect(t.withIdentity(other).mutation(api.x.cancel, {id})).rejects.toThrow(/forbidden|not authorized|403/) — and an unauthenticated caller is refused where auth is required. A feature is not proven until the wrong caller is shown to be blocked.

    • data-scope: a list/query returns ONLY the caller's rows, never the second user's (assert the second user's row is absent).

  6. RUN the tests (npx vitest run) and report: what was proven (each positive + negative assertion that passed), and — critically — any assertion that FAILED, because a failed negative assertion is a real authz hole found before ship. Emit findings on the bus (specs/finding.schema.json, class authz/correctness, evidence kind probe-result with the exact failing call) for anything that didn't behave.

  7. Do NOT weaken a test to make it pass: if the owner-only query returns another user's row, the FIX is in the function (hand to convex-authz), not in the assertion. A test changed until it's green proves nothing.

Rules

  • Prove behavior, not compilation: every verification includes at least one NEGATIVE assertion (a caller who should be refused is refused) — the happy path alone is not proof.

  • Drive the feature as multiple identities with t.withIdentity (owner, other user, unauthenticated) using the app's real subject/tokenIdentifier shape.

  • Seed both the caller's rows AND a second user's rows so cross-user access and data-scope are actually testable.

  • A vitest.config.ts with environment 'edge-runtime' + convex-test inlined is REQUIRED for convex-test to run (import.meta.glob needs it); author it, don't just author the test file.

  • Run in-process with convex-test — no deployment needed; compose with the test capability's setup rather than forking it.

  • Never weaken an assertion to make it pass: a failing negative test is a real defect → hand the fix to convex-authz/convex-expert, don't edit the test until it's green.

  • Emit a bus finding for any assertion that failed (authz/correctness, evidence: the failing probe call) so a composite pass or self-heal can pick it up.

  • This drives a SPECIFIC built feature; a request to set up a test framework generally is the test capability.

Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Needs: Needs Node.js

Security

No issues found by our static scan· No issues found

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Published by
Convex
Publisher
Convex (Official)
Source
convex/agent-skills · skills/convex-verify · commit f60629d
Licence
Apache License 2.0 (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by Convex.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download convex-verify.zip