# Phase 5 acceptance criteria (marketplace)

## Phase 5A — Load test (issue #307)

**Criterion:** Under 100 concurrent marketplace requests, all complete (or defined timeout/success mix), and billing receives one event per successful request.

**Verification:** `tests/test_marketplace_load.py::test_100_concurrent_marketplace_requests_all_complete_and_billing_events`

- Run: `pytest tests/test_marketplace_load.py -v`
- Test creates BackendDispatcher, registers 2-3 mock remote backends (mocked httpx), fires 100 concurrent tasks via `asyncio.gather(dispatcher.dispatch(...))`, asserts all 100 complete and BillingStore received 100 events.
- Markers: `@pytest.mark.performance`, `@pytest.mark.slow` (exclude from fast CI with `-m 'not slow and not performance'`).

## Phase 5B — Synthetic probes, uptime, reputation tiers (issue #308)

- **Probes:** `llmhosts/marketplace/probes.py` — every 5 min (configurable via `LLMHOSTS_MARKETPLACE_PROBE_INTERVAL_SECONDS`), fetch catalog, GET each node's `tunnel_url/health`, store (device_id, timestamp, latency_ms, success) in SQLite `probe_results`.
- **30d uptime:** Computed from probe success over last 30 days; exposed in `GET /api/marketplace/uptime` (proxy) and merged into `GET /api/marketplace/nodes` as `uptime_pct`.
- **Reputation tiers:** Unverified (<50), Standard (50-79), Verified (80-94), Elite (95-100). Tier added to catalog response; `computeReputationScore` kept; score blended with uptime when available.
- **Auto-removal:** When building catalog, nodes with reputation score < 50 are excluded (on-read filter).
