Flows
F 07 Test Run
Status: 🟢 ready to test Owner: Adilet Last updated: 2026-05-08 Last verified on production: —
Goal
A tester opens an active cycle, clicks "Start run" to capture their environment, files issues during the run, drops a session recording or log into the run pack, then clicks "End run" to mark it complete (which fires the AI run summary).
Actors
- Primary — Tester (cycle member, role
testerorlead) - Secondary — AI run-summary agent (fire-and-forget)
Preconditions
- Cycle is
active(F-05) - Tester is a member (
testersrow exists) - Tester is signed in
Trigger
Tester opens /app/test-cycles/:id, scrolls to "My runs", clicks
"Start run".
Happy path — start run
| # | Actor | UI surface | Action | API call | DB writes | Side effects | Suggested event |
|---|---|---|---|---|---|---|---|
| 1 | Tester | cycle detail "My runs" | Click "Start run" | POST /api/test-cycles/:id/runs | INSERT INTO test_runs (status=in_progress, environment auto-captured from UA / OS / browser / screen) | — | run.started |
| 2 | Frontend | "My runs" list | New row at top, status in_progress | — | — | — | — |
Happy path — file issues during the run
See F-08. Each issue filed during the run gets
run_id set automatically (the cycle page knows which run is open).
Happy path — attach run pack media
| # | Actor | UI surface | Action | API call | DB writes | Side effects | Suggested event |
|---|---|---|---|---|---|---|---|
| 1 | Tester | RunPackPanel (expanded) | Drop screen recording / HAR / console.log | POST /api/test-cycles/:id/attachments body { targetType: 'test_run', targetId: runId } | INSERT INTO attachments | DO Spaces upload | attachment.uploaded |
| 2 | Frontend | run pack tile gallery | Tile appears | — | — | — | — |
Happy path — complete run
| # | Actor | UI surface | Action | API call | DB writes | Side effects | Suggested event |
|---|---|---|---|---|---|---|---|
| 1 | Tester | run row | Click "End run" | PATCH /api/test-cycles/:id/runs/:runId body { status: 'completed', notes: '…' } | UPDATE test_runs SET status='completed', ended_at = NOW(), notes = … | AI run-summary agent fires (fire-and-forget) | run.completed |
| 2 | AI | background | Reads run + issues filed during run | (none, just Anthropic API call) | INSERT INTO ai_runs, UPDATE test_runs SET ai_summary = … | — | ai.run_summary_completed (or _failed) |
Acceptance criteria
- AC-1 — Given the tester clicks "Start run", when the POST returns,
then
test_runs.environmentis populated from the request headers (UA + browser + OS + screen + locale). - AC-2 — Given an open run exists, when the tester files an issue
via F-08, then
issues.run_idis set to the open run. - AC-3 — Given a run is in progress, when the tester completes it,
then
test_runs.statusflips tocompletedandended_atis set. - AC-4 — Given a run is completed and Anthropic is configured, then
within 30s
test_runs.ai_summaryis populated (orai_summary_failure_reasonif the agent failed). - AC-5 — Given a tester abandons a run (closes tab without ending),
the run stays
in_progressindefinitely. The cronsync-statusesendpoint may auto-abandon stale runs (verify policy). - AC-6 — Given a non-member calls
POST .../runs, then API returns 403.
Test data / fixtures
- Baseline seed
- An active cycle with at least one tester
Negative paths
| # | Scenario | Expected behavior |
|---|---|---|
| N-1 | Start a second run while one is in progress | Allowed (multi-run support); UI shows both |
| N-2 | End a run that's already completed | API 400 "run already completed" |
| N-3 | Upload to a run that doesn't belong to the caller | API 403 |
| N-4 | Anthropic key missing | Agent short-circuits via isAiEnabled(); run completes normally with no summary |
| N-5 | Anthropic API error (rate limit, etc.) | Logged in ai_runs.failure_reason; run still completes |
Manual QA checklist
- As a tester member, open an active cycle
- Click "Start run" → row appears in My runs with status
In progress - Verify the user agent / OS / browser is captured (inspect run detail)
- Expand the run pack panel; drop a small video file → uploads, tile appears
- File an issue via "+ File issue" while run is open (F-08) → issue has
run_idpopulated - Click "End run" → status flips to
Completed,ended_atset - Wait 30s, refresh →
ai_summarypopulated (if Anthropic configured) - Verify a row exists in
ai_runswithkind='run_summary'andstatus='done'
Automated test outline
test.describe("F-07 test run", () => {
test("start → end run lifecycle", async ({ page }) => { … });
test("environment captured", async ({ request }) => { … });
test("ai summary populates within 30s", async ({ request }) => { … });
test("non-member 403", async ({ request }) => { … });
});Code references
- Pages:
src/app/app/test-cycles/[id]/page.tsx(tabs: Brief / Docs / Issues / My runs / Testers) - UI:
src/components/test-cycles/RunPackPanel.tsx - API:
src/app/api/test-cycles/[id]/runs/route.ts(GET, POST)src/app/api/test-cycles/[id]/runs/[runId]/route.ts(GET, PATCH)src/app/api/test-cycles/[id]/attachments/route.ts
- Hooks:
src/hooks/test-cycles/runs.ts - Lib:
src/lib/ai/run-summary.ts - Schema:
test_runs(incl.environmentjsonb,ai_summary,ai_summary_run_id,ai_summary_failure_reason)
Events emitted (proposed)
run.started—{ cycle_id, run_id, user_id, environment }run.completed—{ cycle_id, run_id, duration_seconds, issues_filed }run.abandoned—{ cycle_id, run_id, reason }ai.run_summary_completed—{ run_id, latency_ms, cost_usd_cents }ai.run_summary_failed—{ run_id, failure_reason }
Open questions / known gaps
- No "abandoned" UI button — only
cron/test-cycles/sync-statusesflips stale runs (verify cron is wired). Consider an explicit "Abandon" action. - Run summary AI is single-shot — no retry button (intake has retry, summary doesn't yet).
- No "re-open completed run" path (forces tester to start a fresh run if they want to add more issues afterward).