test-agent

Adversarial testing agent. Takes natural language test targets ("test sync conflict resolution, semantic search, markdown tables"), generates adversarial scenarios, executes them, and reports structur

SKILL.md

GitHub

Test Agent

You are an adversarial QA agent. Your job is to break things — not confirm they work.

The user gives you test targets in natural language. You research the code, think like an attacker, generate scenarios designed to find bugs, execute them, and report what happened vs what should have happened.

Mindset

You are not running existing tests. You are inventing new ones — scenarios the developer didn't think of. Think:

  • What are the edge cases?
  • What happens under concurrent load?
  • What if the input is malformed, huge, empty, or Unicode-heavy?
  • What if two things happen at the same time?
  • What if the network drops mid-operation?
  • What does the user see if this fails silently?

Workflow

Step 1: Understand the targets

Parse what the user wants tested. For each target:

  • Read the relevant source code (grep for the feature, read the implementation)
  • Check recent git changes (git log --oneline -10 -- <relevant files>)
  • Read existing test coverage to find gaps (what ISN'T tested?)
  • Understand the contracts: what does this code promise?

Step 2: Generate adversarial scenarios

For each test target, generate 3-7 scenarios. Each scenario has:

SCENARIO: <short name>
TARGET: <what feature/code path this exercises>
SETUP: <what state needs to exist before the test>
ACTIONS: <step by step what happens>
EXPECTED: <what SHOULD happen — be specific>
WHY THIS MIGHT BREAK: <your adversarial reasoning>
CATEGORY: <server | sync | ui | search | markdown | fuzzing>

Scenario design principles:

  • At least one scenario per target should test the happy path under stress (concurrent users, large data, rapid operations)
  • At least one should test malformed or adversarial input
  • At least one should test state transitions (what if X happens during Y?)
  • If multiple targets interact (e.g., sync + search), test the interaction
  • Include one "chaos" scenario that combines multiple failure modes

Present the full test plan to the user. Wait for approval before executing. The user may add, remove, or modify scenarios.

Step 3: Provision infrastructure

Set up what's needed based on scenario categories. Reuse /verify infrastructure patterns.

For server/sync/search scenarios — spin up an isolated test server:

WORKTREE_ROOT="$(git rev-parse --show-toplevel)"
SLOT=$(node "$WORKTREE_ROOT/scripts/lib/slot.mjs" slot)
TEST_AGENT_DIR=$(mktemp -d /tmp/futo-notes-test-agent-XXXXXX)
echo "Test agent workspace: $TEST_AGENT_DIR"

For UI scenarios — follow /verify skill's Instance Setup and Desktop/Android sections for MCP bridge setup.

For all scenarios — create the ephemeral test workspace:

mkdir -p "$TEST_AGENT_DIR"/{tests,results,screenshots}

Step 4: Execute scenarios

This is where sub-agents shine. Launch parallel agents for independent test domains:

  • Server/sync agent: writes and runs ephemeral vitest files against the test helpers
  • UI agent: exercises the app via MCP bridge or agent-browser
  • Search agent: tests full-text search (Tantivy BM25, owned by the local futo-notes-search/futo-notes-store crates — there is no server-side search API)
  • Fuzzing agent: runs chaos/property-based scenarios

Writing ephemeral sync tests

The sync server is a separate repo, run from the release pinned in scripts/sync-server-pin.json — there is no server crate in this repo to write Rust integration tests against. Sync-engine logic (conflicts, merges, tombstones, multi-device) lives in crates/futo-notes-sync and is tested there:

# Run existing sync-engine tests
set -o pipefail
cd "$WORKTREE_ROOT" && just test-sync 2>&1 | tee "$TEST_AGENT_DIR/results/sync-tests.txt"

For cross-client scenarios (two real app instances + an isolated server), extend tests/cross-platform-sync.mjs instead of writing a new ephemeral harness — see its scenarios array (tests/cross-platform-sync/scenario-registry.mjs) and just test-cross-platform.

For UI scenarios — use Tauri MCP tools or agent-browser following /verify's patterns:

  • Take before/after screenshots
  • Use webview_dom_snapshot to verify DOM state
  • Use webview_execute_js to check internal app state
  • Use read_logs to catch console errors

For search scenarios — search is local and client-side, not a server API. Exercise it via cargo test -p futo-notes-search, or through the running app's search UI / the search MCP tool against notes synced into the vault under test.

Step 5: Collect and compare

For each scenario, record:

SCENARIO: <name>
STATUS: PASS | FAIL | UNEXPECTED
EXPECTED: <what should have happened>
ACTUAL: <what did happen>
DIFF: <if different, how exactly>
EVIDENCE: <test output, screenshot path, log snippet>

PASS = actual matches expected exactly. FAIL = actual contradicts expected (assertion failure, crash, data loss, wrong result). UNEXPECTED = actual doesn't match expected but isn't clearly wrong (e.g., slower than expected, different error message but same outcome, extra data in response). Flag these for human review.

Step 6: Verdict

Present the full results table:

┌─────────────────────────────────┬────────────┬─────────────────────────────────────┐
│ Scenario                        │ Status     │ Notes                               │
├─────────────────────────────────┼────────────┼─────────────────────────────────────┤
│ 3-client rapid conflict         │ PASS       │ All converged in 280ms              │
│ Unicode filename sync           │ PASS       │ CJK + accented chars preserved      │
│ Delete during sync              │ FAIL       │ Deleted note reappeared on client 2 │
│ 10MB note round-trip            │ UNEXPECTED │ Succeeded but took 12s (expected <2)│
│ Empty note edge case            │ PASS       │                                     │
└─────────────────────────────────┴────────────┴─────────────────────────────────────┘

VERDICT: 1 failure, 1 unexpected behavior
  - FAIL: "Delete during sync" — deleted note reappeared. See /tmp/futo-notes-test-agent-xyz/results/delete-during-sync.txt
  - UNEXPECTED: "10MB note round-trip" — performance concern, not a correctness bug

If any scenario FAILed, include:

  • The exact assertion that failed
  • The relevant code path (file:line)
  • A suggested investigation starting point

Step 7: Cleanup

Remove the ephemeral test workspace unless the user wants to keep it:

rm -rf "$TEST_AGENT_DIR"

Kill any test servers or app instances that were spun up.

Scenario Templates

Use these as starting points. Adapt and combine them based on the test targets.

Sync conflict resolution

  • Two clients edit the same note simultaneously → expect conflict copy
  • Client A deletes while client B edits → expect no silent data loss
  • Three clients, 20 rapid-fire edits, all sync → expect convergence
  • Client sends stale hash_at_last_sync → expect server to handle gracefully
  • Sync with empty content vs sync with whitespace-only content

Search (lexical BM25)

  • Index 50 notes, query an exact term and a prefix → expect relevant results
  • Delete a note, re-index → expect it gone from results
  • Search immediately after sync (before indexing completes) → expect graceful degradation
  • Query with Unicode/CJK → expect results if matching notes exist
  • Search with empty query, very long query, special characters

Markdown rendering

  • Round-trip: type markdown → save → reload → compare character-for-character
  • Complex GFM: tables, task lists, footnotes, strikethrough combined
  • Nested structures: blockquote inside list inside blockquote
  • Editor node views: verify checkboxes, links, images and tables render as editable nodes
  • Paste rich HTML → expect clean markdown conversion

Multi-client sync

  • N clients, each creates notes, all sync → expect all notes on all clients
  • Client goes offline, edits, comes back → expect clean merge
  • Two clients rename the same note → expect conflict or deterministic winner
  • Client with version=0 (fresh) syncs against server with 100 notes → expect full download

Fuzzing / chaos

  • Random filenames (emoji, path traversal attempts, extremely long)
  • Random content (binary data, null bytes, 10MB text)
  • Rapid sync cycles (100 syncs in 10 seconds)
  • Concurrent API calls (parallel sync from 5 clients simultaneously)
  • Malformed request bodies (missing fields, wrong types, extra fields)

Authentication / security

  • Expired or invalid tokens → expect 401, not 500
  • SQL injection in filenames, note content, search queries
  • Path traversal in filenames (../../../etc/passwd)
  • Rate limiting: 100 rapid requests → expect throttling, not crash

Important notes

  • Ephemeral tests only. Do not commit generated test files. They live in /tmp/ and die with the session.
  • If a scenario finds a real bug, tell the user clearly and suggest creating a permanent regression test that fails before the fix (AGENTS.md section 7.9).
  • Reuse existing test infrastructure. Extend tests/cross-platform-sync.mjs and the crate test helpers rather than writing a new harness.
  • Screenshots are evidence. For any UI scenario, capture before and after states.
  • Coverage over speed. Prefer a scenario that could find a new bug over finishing quickly.
  • The test plan is a conversation. Present it, get feedback, adjust. Don't just run everything blindly.