Public agent docs
QA Agent API
QA Agent API
The agent integration uses Supabase Edge Functions as a gateway. Agents never
receive a user's Supabase session token. The agent requests an activation URL,
the user signs in at the app and activates the claim, then the agent exchanges
its one-time request secret for a scoped qa_agent_... token.
Public app URL: https://qa.felipeotarola.com
Public agent docs: https://qa.felipeotarola.com/qa-agent-api.md
Index
Start here:
- [Flow](#flow) - access approval and token exchange overview.
- [Recommended Agent Loop](#recommended-agent-loop) - the full project-first
loop agents should follow for real QA work.
Access:
- [Request Access](#request-access) - create an activation claim.
- [Exchange Approval](#exchange-approval) - exchange an approved request for a
scoped qa_agent_... token.
Projects and workspace memory:
- [List Projects](#list-projects)
- [Create Project](#create-project)
- [Project Brief](#project-brief) - one-call project intelligence for Sladdis
before planning a run.
- [QA Workspace](#qa-workspace) - durable project context, safe test data,
and retest queue.
- [Read QA Workspace](#read-qa-workspace)
- [Upsert Project Context](#upsert-project-context)
- [Create Test Data And Retest Items](#create-test-data-and-retest-items)
- [Create Agent Field Notes](#create-agent-field-notes) - visible notes in
/agent-notes.
- [QA Work Board](#qa-work-board)
- [List Work Items](#list-work-items)
- [Update Work Item QA State](#update-work-item-qa-state)
- [Automation Packages V1](#automation-packages-v1)
- [List Automation Packages](#list-automation-packages)
- [Create Automation Package](#create-automation-package)
- [Create Automation Package Version](#create-automation-package-version)
- [Link Automation Assertion to Test Case](#link-automation-assertion-to-test-case)
- [List Automation Runs](#list-automation-runs)
- [Queue Automation Run](#queue-automation-run)
- [API Testing V1](#api-testing-v1)
- [Create API Target](#create-api-target)
- [Create API Auth Profile](#create-api-auth-profile)
- [Create API Test Case](#create-api-test-case)
- [Run API Test Case](#run-api-test-case)
- [Mobile Testing V1](#mobile-testing-v1)
- [Create Mobile Device](#create-mobile-device)
- [Create Mobile Test Case](#create-mobile-test-case)
- [Run Mobile Test Case](#run-mobile-test-case)
Product model:
- [Application Map](#application-map)
- [List Application Map](#list-application-map)
- [Upsert Application Map Node](#upsert-application-map-node)
- [Upsert Application Map Edge](#upsert-application-map-edge)
- [Link Test Case To Application Map Node](#link-test-case-to-application-map-node)
- [Generate Application Map From Test Cases](#generate-application-map-from-test-cases)
Test cases and evidence:
- [Test Suites](#test-suites)
- [List Test Cases](#list-test-cases)
- [Create Test Case](#create-test-case)
- [Update Test Case](#update-test-case)
- [Upload Test Case Screenshot](#upload-test-case-screenshot)
Runs and results:
- [List Test Runs](#list-test-runs)
- [Run Plan](#run-plan)
- [Create Run](#create-run)
- [Update Run](#update-run)
- [Add Result](#add-result)
- [Create Triage Action](#create-triage-action)
Flow
1. Agent requests access with agent-request-access.
2. User opens the returned activationUrl, for example /activate?claim=....
3. User signs in when needed and the activation page approves the claim.
4. Agent polls agent-exchange-approval with requestId and requestSecret.
5. Agent uses the returned token to find or create the QAA project first, then
read workspace context, read test cases/runs, claim queued work requests,
write QA runs/results, and report triage decisions.
Request Access
curl -X POST "$SUPABASE_URL/functions/v1/agent-request-access" \
-H "Content-Type: application/json" \
-d '{
"agentName": "Playwright QA Agent",
"userEmail": "user@example.com",
"appOrigin": "https://qa.felipeotarola.com",
"scopes": ["projects:read", "projects:write", "test_cases:write", "test_runs:write", "test_results:write", "qa_manager:write"]
}'The response contains requestId, requestSecret, claim, and
activationUrl. The agent should show the app-domain activation URL to the
user. A projectId may still be provided to request access to only one project;
when omitted, the approved token can list available QA projects.
Exchange Approval
curl -X POST "$SUPABASE_URL/functions/v1/agent-exchange-approval" \
-H "Content-Type: application/json" \
-d '{
"requestId": "REQUEST_UUID",
"requestSecret": "qa_req_..."
}'Pending approvals return 202. Approved requests return agentToken.
List Projects
curl -X POST "$SUPABASE_URL/functions/v1/qa-list-projects" \
-H "Authorization: Bearer qa_agent_..." \
-H "Content-Type: application/json"Requires projects:read. Returns active projects unless the token is scoped to
one project, in which case only that project is returned.
Create Project
curl -X POST "$SUPABASE_URL/functions/v1/qa-create-project" \
-H "Authorization: Bearer qa_agent_..." \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Storefront",
"key": "acme-storefront",
"publicSlug": "acme-storefront",
"description": "Public storefront regression suite",
"baseUrl": "https://acme.example.com",
"status": "Active"
}'Requires projects:write. Project-scoped agent tokens cannot create projects;
the token must be workspace-wide. If key is omitted, the endpoint creates a
slug from name. If publicSlug is omitted, the endpoint uses the project key.
status defaults to Active. The endpoint also accepts slug, public_slug,
publicUrlSlug, public_url_slug, and publicProjectSlug as public slug
aliases.
Response shape:
{
"project": {
"id": "PROJECT_UUID",
"name": "Acme Storefront",
"key": "acme-storefront",
"description": "Public storefront regression suite",
"base_url": "https://acme.example.com",
"status": "Active",
"visibility": "private",
"public_slug": "acme-storefront",
"public_share_title": "Acme Storefront QA Report",
"public_share_description": "Read-only QA report for Acme Storefront.",
"public_allow_indexing": false,
"created_at": "2026-06-16T17:00:00.000Z",
"updated_at": "2026-06-16T17:00:00.000Z"
}
}Use this endpoint as the first durable setup step when the agent is working on
a project that does not already exist in QAA. Creating the project first gives
the agent a stable projectId for workspace context, test data, application
map nodes, test cases, runs, results, defects, and field notes. For existing
projects, call qa-list-projects first and reuse the matching id.
Project Brief
Use qa-agent-project-brief immediately after selecting or creating the QAA
project. This is the project-first read model for Sladdis: one call returns the
QAA state needed to decide whether the project is safe to release, what needs
attention, and what should be tested next.
curl -X POST "$SUPABASE_URL/functions/v1/qa-agent-project-brief" \
-H "Authorization: Bearer qa_agent_..." \
-H "Content-Type: application/json" \
-d '{
"projectId": "PROJECT_UUID"
}'Requires projects:read. If the token is scoped to one project, projectId is
optional and any other project id is rejected.
The signed-in app route is also available:
curl "https://qa.felipeotarola.com/api/qa-project-brief?projectId=PROJECT_UUID" \
-H "Cookie: authenticated_app_session=..."Response shape:
{
"brief": {
"projectId": "PROJECT_UUID",
"project": {
"id": "PROJECT_UUID",
"name": "Acme Storefront",
"key": "acme-storefront"
},
"decision": {
"releaseState": "blocked",
"testConfidence": 78,
"confidenceDelta": null,
"blockingCount": 2,
"advisoryCount": 1,
"dataQualityWarningCount": 0,
"lastVerifiedAt": "2026-06-16T17:00:00.000Z",
"freshnessState": "fresh",
"ageHours": 2
},
"attention": [
{
"id": "coverage-checkout",
"type": "coverage_gap",
"impact": "release_blocking",
"priority": "critical",
"title": "Checkout",
"reason": "critical gap ยท 1 linked tests",
"href": "/application-map"
}
],
"context": {
"product_profile": "Public storefront for trial signups.",
"key_flows": ["Homepage", "Signup", "Pricing"],
"risk_areas": ["Lead capture", "Mobile navigation"]
},
"readiness": {
"safeTestDataCount": 1,
"openRetestCount": 2,
"contextSignalCount": 7
},
"coverage": {
"counts": {
"covered_passing": 4,
"covered_failing": 1,
"partial": 2,
"untested": 3,
"blocked": 1,
"critical_gap": 1
},
"gaps": [
{
"label": "Checkout",
"coverageState": "covered_failing",
"priority": "high",
"failedCount": 2,
"testCaseIds": ["ACME-CHECKOUT-001"]
}
]
},
"tests": {
"total": 27,
"statusCounts": {
"Passed": 18,
"Failed": 7,
"Blocked": 1,
"Not run": 1
},
"top": []
},
"runs": {
"health": {
"releaseHealth": "failing",
"totals": {
"runs": 7,
"failedRuns": 5,
"blockedTests": 1,
"failingTests": 9
}
},
"recentFailures": []
},
"work": {
"activeItems": [],
"activeDefects": []
},
"retests": {
"open": [],
"totalOpen": 2,
"totalRetesting": 0
},
"runPlans": {
"active": [],
"recentCompleted": []
},
"notes": {
"recent": [],
"memory": [],
"nextTest": []
}
}
}The brief intentionally composes existing QAA primitives instead of creating a
parallel intelligence store. Do not create duplicate "project intelligence",
"coverage heatmap", or "memory" tables before this brief shows a real schema
gap. Current source-of-truth mapping:
- Project/product facts: qa_project_context.
- Test data and safe rules: qa_test_data and qa_project_context.
- Coverage heatmap: qa_app_map_nodes, qa_test_case_app_nodes, and latest
test status.
- Execution health: qa_test_runs and qa_test_run_results.
- Reproducible evidence: qa_test_evidence, qa_api_run_evidence, and
qa_mobile_run_evidence.
- Bugs/action handoff: qa_work_items with item_type = bug.
- Retests: qa_retest_items.
- Saved pre-run intent and plan-vs-actual comparison: qa_run_plans.
- Narrative, memory, and next-test notes: qa_agent_field_notes.
- Triage/audit decisions: qa_triage_actions and qa_agent_audit_logs.
The /qa-overview app page should become Project Intelligence rather than a
separate overview dashboard. It should be built from this brief and show the
product profile, key flows, risk areas, known weak spots, coverage map, active
defects, retest queue, active run plans, and confidence score per area. Keep
QAA as the data layer and review surface; Sladdis should do the reasoning and
write the resulting plan, evidence, and notes back into these primitives.
The recommended rollout is:
1. Use this brief as Sladdis' first read after project selection.
2. Build coverage heatmap UI from brief.coverage.
3. Use Retest Queue V2 links on qa_retest_items: test_case_id, run_id,
result_id, work_item_id, resolved_by_run_id, and resolved_at.
4. Save pre-run intent in qa_run_plans before execution and compare it with
the linked qa_test_runs actual outcome after execution.
5. Generate next-best-test recommendations from the brief first; persist them
later only if history/review becomes useful.
QA Workspace
QAA is the system of record for project Quality Assurance. If an agent has QAA
access, it should read project workspace context before testing and persist
workspace updates after testing. Do not only report findings in chat when a QAA
project is available.
The workspace contains durable project context, safe test data, agent
instructions, and retest queue items. This is what lets Sladdis act as the QA
owner for a project instead of a one-off URL checker.
Use qa-create-agent-note in [Create Agent Field Notes](#create-agent-field-notes)
for all QA narrative, observations, reasoning, and memory that should appear in
/agent-notes.
### Read QA Workspace
curl -X GET "$SUPABASE_URL/functions/v1/qa-workspace?projectId=PROJECT_UUID" \
-H "Authorization: Bearer qa_agent_..."Requires projects:read. Returns context, testData, and retestItems.
Agents should call this before planning a run.
Response shape:
{
"projectId": "PROJECT_UUID",
"context": {
"project_id": "PROJECT_UUID",
"product_profile": "Public storefront for trial signups.",
"key_flows": ["Homepage", "Signup", "Pricing", "Contact"],
"risk_areas": ["Auth", "Lead capture", "Mobile navigation"],
"testing_rules": [
"Do not submit real orders",
"Stop before payment capture"
],
"agent_instructions": "Run safe smoke first, persist evidence, queue retests for failures.",
"updated_by": "Sladdis"
},
"testData": [
{
"id": "TEST_DATA_UUID",
"label": "Login test user",
"data_type": "credential",
"sensitivity": "sensitive",
"usage_notes": "Use only for login smoke testing when explicitly requested.",
"status": "active"
}
],
"retestItems": [
{
"id": "RETEST_UUID",
"title": "Retest checkout validation after fix",
"status": "open",
"priority": "high",
"source_type": "result",
"source_id": "RUN_RESULT_UUID"
}
]
}The app-session route is also available for the signed-in UI:
curl "https://qa.felipeotarola.com/api/qa-workspace?projectId=PROJECT_UUID" \
-H "Cookie: authenticated_app_session=..."### Upsert Project Context
curl -X POST "$SUPABASE_URL/functions/v1/qa-workspace" \
-H "Authorization: Bearer qa_agent_..." \
-H "Content-Type: application/json" \
-d '{
"action": "upsert-context",
"projectId": "PROJECT_UUID",
"productProfile": "Public storefront for trial signups.",
"keyFlows": "Homepage\nSignup\nPricing\nContact",
"riskAreas": "Auth\nLead capture\nMobile navigation",
"testingRules": "Do not submit real orders\nStop before payment capture",
"agentInstructions": "Run safe smoke first, persist evidence, queue retests for failures."
}'Requires test_cases:write.
### Create Test Data And Retest Items
Use the same endpoint with action set to create-test-data,
create-retest-item, or update-retest-status. Test data must be classified as
safe, sensitive, temporary, or do_not_reuse. Retest status values are
open, retesting, resolved, and ignored.
For notes, observations, reasoning, and project memory, use the separate
qa-create-agent-note Edge Function so the content appears in /agent-notes.
Retest Queue V2:
- Link every retest to the failed evidence when possible: testCaseId,
runId, resultId, and workItemId.
- Use sourceType: "result" and sourceId: "RUN_RESULT_UUID" when the retest
comes from a failed or blocked result.
- When a retest passes, call update-retest-status with status: "resolved"
and resolvedByRunId. QAA stores resolved_at and can show "fixed after run
X".
- Keep qa_work_items as the bug/action handoff and qa_retest_items as the
verification queue. Do not create a second retest model in Agent Notes or
triage actions.
Example: saving credentials or other user-provided test data:
curl -X POST "$SUPABASE_URL/functions/v1/qa-workspace" \
-H "Authorization: Bearer qa_agent_..." \
-H "Content-Type: application/json" \
-d '{
"action": "create-test-data",
"projectId": "PROJECT_UUID",
"label": "Login test user",
"dataType": "credential",
"value": "email=test-user@example.com\npassword=EXAMPLE_ONLY_DO_NOT_USE",
"sensitivity": "sensitive",
"usageNotes": "Example only. Store real credentials only when the user explicitly provides test credentials and the project rules allow authenticated checks."
}'Example: creating a linked retest from a failed result:
curl -X POST "$SUPABASE_URL/functions/v1/qa-workspace" \
-H "Authorization: Bearer qa_agent_..." \
-H "Content-Type: application/json" \
-d '{
"action": "create-retest-item",
"projectId": "PROJECT_UUID",
"title": "Retest checkout validation after fix",
"reason": "Checkout failed at step 2 with validation error.",
"priority": "critical",
"sourceType": "result",
"sourceId": "RUN_RESULT_UUID",
"testCaseId": "ACME-CHECKOUT-001",
"runId": "run-checkout-regression",
"resultId": "RUN_RESULT_UUID",
"workItemId": "WORK_ITEM_UUID"
}'Example: resolving a retest after a passing rerun:
curl -X POST "$SUPABASE_URL/functions/v1/qa-workspace" \
-H "Authorization: Bearer qa_agent_..." \
-H "Content-Type: application/json" \
-d '{
"action": "update-retest-status",
"projectId": "PROJECT_UUID",
"id": "RETEST_UUID",
"status": "resolved",
"resolvedByRunId": "run-checkout-rerun",
"resolutionNotes": "Checkout validation passed after deploy."
}'When a user tells the agent "this is test data", the agent should save it here
before or after the run, classify sensitivity conservatively, and reference it
in future project QA planning.
Agent rules for test data:
- Default to sensitive for credentials, tokens, personal data, customer data,
production identifiers, or anything that could grant access.
- Use temporary for values the agent creates during a single run and may
delete or ignore after the run.
- Use do_not_reuse for one-time codes, expired links, destructive data, or
user-provided values that should not be tried again.
- Do not paste sensitive test data into Slack, public reports, screenshots,
defect titles, or run summaries. Reference the test data by label or id
instead.
- If the user says "this is test data", save it to the workspace before or
immediately after the run, unless doing so would store secrets outside the
agreed QAA boundary.
- Never invent real credentials. Generated credentials must be clearly marked as
fake, temporary, or example-only.
QA Work Board
The QA Work Board is the human-to-agent delivery board in QAA. It is separate
from durable test coverage: humans own the product scope, title, description,
priority, due date, and acceptance criteria; the agent reads those fields,
chooses relevant QAA tests, runs them, and writes back QA state.
Board statuses:
- backlog
- in_progress
- ready_for_qa
- testing
- tested
- done
Agents should normally start from ready_for_qa, move a card to testing
while execution is running, then move it to tested or back to in_progress
with a concise QA summary and linked run id. When QA finds a bug, the ticket is
not complete: set qaStatus to failed, write the bug fields, and move
status back to in_progress. If the agent cannot test because of a blocker,
set qaStatus to blocked, explain the blocker in qaSummary, and move or
leave status as in_progress. done is the final accepted state after the
work has passed QA and been handed off.
Agent-writable fields:
- status
- qaStatus
- qaRunId
- qaSummary
- qaRisk
- qaAgentNotes
- agentPrompt
- bugTitle
- bugDescription
- bugSeverity
- releaseImpact
- releaseImpactReason
Do not edit title, description, acceptance criteria, priority, owner, due date,
or product scope from the agent.
### List Work Items
curl -X POST "$SUPABASE_URL/functions/v1/qa-list-work-items" \
-H "Authorization: Bearer qa_agent_..." \
-H "Content-Type: application/json" \
-d '{
"projectId": "PROJECT_UUID",
"status": "ready_for_qa",
"limit": 50
}'Requires projects:read. If the token is scoped to one project, projectId
is optional and any other project id is rejected. Supported filters:
projectId, status, qaStatus, itemType, search, and limit.
Response shape:
{
"workItems": [
{
"id": "WORK_ITEM_UUID",
"project_id": "PROJECT_UUID",
"title": "Mobile login smoke reaches welcome screen",
"description": "Run Android real-device smoke for package com.example.admin.",
"status": "ready_for_qa",
"priority": "high",
"item_type": "qa",
"acceptance_criteria": [
"App launches on a safe Android device",
"Welcome text appears after login"
],
"target_url": "com.example.admin",
"app_area": "Mobile Auth",
"linked_test_case_ids": [],
"qa_status": "not_tested",
"qa_run_id": null,
"qa_summary": "",
"qa_risk": "medium",
"release_impact": "informational",
"release_impact_reason": "",
"qa_agent_notes": "",
"agent_prompt": ""
}
]
}### Update Work Item QA State
curl -X PATCH "$SUPABASE_URL/functions/v1/qa-update-work-item" \
-H "Authorization: Bearer qa_agent_..." \
-H "Content-Type: application/json" \
-d '{
"id": "WORK_ITEM_UUID",
"projectId": "PROJECT_UUID",
"status": "in_progress",
"qaStatus": "failed",
"qaRunId": "run-mobile-login",
"qaSummary": "Login smoke failed because Welcome text never appeared.",
"qaRisk": "high",
"qaAgentNotes": "Reproduced on Android staging device. Screenshot evidence is attached to the run result.",
"agentPrompt": "You are fixing a QA failure. Reproduce the Android login smoke, inspect the auth flow, implement the smallest fix, add regression coverage, rerun QAA, and update this ticket.",
"bugTitle": "Android login does not reach Welcome screen",
"bugDescription": "After tapping Login with approved test data, the app remains on the login screen without a crash signal.",
"bugSeverity": "P2",
"releaseImpact": "non_blocking",
"releaseImpactReason": "The defect needs repair but does not invalidate the current release-critical flows."
}'Requires test_results:write. Allowed agent transitions are intentionally
limited to QA handoff movement:
- backlog -> in_progress | ready_for_qa
- in_progress -> ready_for_qa
- ready_for_qa -> testing | in_progress
- testing -> ready_for_qa | in_progress | tested
- tested -> ready_for_qa | testing | done
- done -> ready_for_qa | testing
Use qaRunId whenever the update is based on a QAA run. If a bug is found,
move the card back to in_progress, set qaStatus to failed, and write a
short bugTitle, reproducible bugDescription, and severity P1, P2, P3,
or P4. agentPrompt is the repair prompt for a coding agent: include the
failed behavior, reproduction target, expected outcome, and required checks,
but do not include secrets, raw logs, or sensitive screenshot contents. Attach
redacted evidence to the run instead. If the test is blocked, use
status: "in_progress" with qaStatus: "blocked" rather than a board column.
Set releaseImpact explicitly to release_blocking, non_blocking,
data_quality, or informational, and explain the decision in
releaseImpactReason. Do not infer release impact from severity alone when the
product context provides a more precise answer.
Every work item update creates an append-only qa_work_item_events history row
with the changed fields, previous values, new values, actor, and source. Do not
erase older bug feedback to describe a new retest. Update the current summary
fields with the latest state; QAA preserves prior developer fixes, failed
retests, severity changes, and handoff movement in ticket history.
Automation Packages V1
Automation packages are versioned, project-linked source assets owned by QAA.
V1 supports QAA-managed Playwright drafts, code inspection, and zip export.
The web runner capability is configured when the isolated host worker can
claim validation jobs. Agents must still use the persisted run result rather
than treating a queued or running draft as executed.
Core rules:
- Match or create the QAA project before creating a package.
- Use playwright for QAA-managed V1 packages.
- Keep package paths relative and bounded; never include secrets.
- Begin at draft. Validation and execution are separate capabilities.
- Treat queued/running packages as not-run; only a persisted terminal run is evidence.
- Treat browser load checks as bounded capacity probes, not unrestricted stress
tests. Use a separate package, read-only navigation, one queued run initially,
no authenticated or mutating flows, and explicit concurrency/request limits.
- Record the runner container limits with the result. Do not present a
container-limited run as whole-host capacity evidence.
- The default ceiling for an initial capacity probe is four concurrent pages,
25 total navigations, 30 seconds per test, and one run. Raising any ceiling
requires reviewing target ownership, worker headroom, and stop conditions.
- Stop escalation on timeouts, HTTP 429/5xx responses, runner blocking, OOM,
swap growth, or sustained host pressure.
- Requires projects:read to list and test_cases:write to create.
- Sladdis owns package selection, queueing, and interpretation. The isolated
host worker only claims and executes the persisted job.
- GitHub export is a reviewed external mutation. A project owner/admin connects
a repository token in QAA, where it is encrypted in Supabase Vault and never
returned to an agent or browser. Sladdis may prepare the immutable package
version and target path, but a signed-in project member must explicitly
approve creation of the branch and pull request in QAA.
- QAA remains the system of record for QA history and evidence. After an
exported pull request is merged, Git is the source of truth for that
repository-managed test source.
- PR, deployment, and schema triggers are persisted disabled by default. An
owner/admin receives the signing secret once, configures the provider
webhook, and can activate only after the latest three terminal package runs
all passed. Webhooks are HMAC-SHA256 verified and delivery-id deduplicated.
Agents must not activate triggers or handle their signing secrets.
### List Automation Packages
curl "$SUPABASE_URL/functions/v1/qa-automation-packages?projectId=PROJECT_UUID" \
-H "Authorization: Bearer qa_agent_..."Pass id=PACKAGE_UUID to read one package including its latest version and
source files.
### Create Automation Package
curl -X POST "$SUPABASE_URL/functions/v1/qa-automation-packages" \
-H "Authorization: Bearer qa_agent_..." \
-H "Content-Type: application/json" \
-d '{
"projectId": "PROJECT_UUID",
"name": "Public site smoke",
"description": "Stable read-only checks",
"targetUrl": "https://example.com",
"framework": "playwright",
"files": [
{
"path": "playwright.config.ts",
"mediaType": "text/typescript",
"content": "import { defineConfig } from \"@playwright/test\"; export default defineConfig({ workers: 1 });"
},
{
"path": "tests/homepage.spec.ts",
"mediaType": "text/typescript",
"content": "import { test, expect } from \"@playwright/test\"; test(\"home\", async ({ page }) => { await page.goto(\"https://example.com\"); await expect(page).toHaveTitle(/Example/); });"
}
]
}'The response returns the immutable draft version and
runnerCapability: "configured". Human users can inspect and download the
same package from /automation-packages.
### Create Automation Package Version
Create an immutable next revision of an existing QAA-managed package:
curl -X POST "$SUPABASE_URL/functions/v1/qa-automation-packages" \
-H "Authorization: Bearer $QAA_AGENT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"action": "create-version",
"projectId": "PROJECT_UUID",
"packageId": "PACKAGE_UUID",
"baseVersionId": "CURRENT_VERSION_UUID",
"changelog": "Add navigation regression coverage",
"files": [{"path":"tests/navigation.spec.ts","mediaType":"text/typescript","content":"..."}]
}'baseVersionId is an optimistic concurrency guard. QAA rejects stale edits and
no-op revisions. Success creates vN+1; it never modifies earlier source,
hashes, runs, or artifacts. New runs pin the latest version while historical
runs remain pinned to the version they executed.
### Link Automation Assertion to Test Case
Link an exact Playwright file::title key to an existing QAA test case:
curl -X POST "$SUPABASE_URL/functions/v1/qa-automation-packages" \
-H "Authorization: Bearer $QAA_AGENT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"action": "link-test-case",
"projectId": "PROJECT_UUID",
"packageId": "PACKAGE_UUID",
"testCaseId": "WEB-HOME-001",
"testKey": "tests/homepage.spec.ts::homepage responds"
}'The package and test case must belong to the same project. * is supported
only as a single-test package fallback. A terminal run materializes linked
assertions into the normal QAA test run and result history. A failure creates
one deduplicated Work Board finding plus an open retest; repeated failures
update that handoff, and a later pass resolves both the retest and finding.
### List Automation Runs
curl "$SUPABASE_URL/functions/v1/qa-automation-runs?projectId=PROJECT_UUID&packageId=PACKAGE_UUID" \
-H "Authorization: Bearer qa_agent_..."Requires projects:read. Treat queued and running as not-run. Only
passed, failed, or blocked is terminal evidence. A blocked run means
the execution engine or policy could not produce a valid Playwright result; it
must not be reported as a product failure.
### Queue Automation Run
curl -X POST "$SUPABASE_URL/functions/v1/qa-automation-runs" \
-H "Authorization: Bearer qa_agent_..." \
-H "Content-Type: application/json" \
-d '{
"action": "queue",
"packageId": "PACKAGE_UUID"
}'Requires test_results:write. The runner selects the newest runnable immutable
version, applies exact-origin and resource policy, and persists status,
statistics, a bounded log excerpt, package version, target, and runner version.
It uploads private screenshots, traces, failure videos, JSON reports, and HTML
reports when Playwright produces them. Every artifact is project/run scoped,
size and type limited, SHA-256 verified after upload, inventoried in
qa_automation_run_artifacts, and available to signed-in project members from
/automation-packages. Do not claim an artifact exists unless it appears on
the persisted terminal run.
API Testing V1
API testing is a first-class test type beside web testing. Agents must not use
free-form fetch access for project APIs. API requests should be represented as
API targets, auth profiles, API test cases, and controlled runner executions so
QAA owns safety policy, auth injection, redaction, and evidence persistence.
Core rules:
- Create an API target before creating API cases.
- Store only secret references in QAA. Do not write secret values into test
data, notes, case bodies, or reports.
- Production mutation policy should be read_only or approval_required.
- Runner evidence is redacted before persistence.
- Public reports only show redacted API summaries.
Mutation policies:
- read_only: only GET, HEAD, and OPTIONS.
- safe_mutations: mutations only when the target is marked safe.
- approval_required: mutations require explicit human approval.
- blocked: mutations are never allowed.
Response body storage:
- none: store no body.
- excerpt: store a redacted excerpt.
- full_redacted: store full redacted body only when the target is marked as a
safe environment; otherwise the runner downgrades to excerpt.
### Create API Target
curl -X POST "$SUPABASE_URL/functions/v1/qa-workspace" \
-H "Authorization: Bearer qa_agent_..." \
-H "Content-Type: application/json" \
-d '{
"action": "create-api-target",
"projectId": "PROJECT_UUID",
"name": "Customer API",
"environment": "staging",
"baseUrl": "https://api.example.com/v1",
"mutationPolicy": "approval_required",
"allowedMethods": "GET\nHEAD\nOPTIONS",
"forbiddenPaths": "/admin\n/billing",
"storeResponseBody": "excerpt",
"timeoutMs": "15000",
"isSafeEnvironment": "false"
}'### Create API Auth Profile
Auth profiles store metadata and secret references only. Secret values are
resolved by the runner environment.
curl -X POST "$SUPABASE_URL/functions/v1/qa-workspace" \
-H "Authorization: Bearer qa_agent_..." \
-H "Content-Type: application/json" \
-d '{
"action": "create-api-auth-profile",
"projectId": "PROJECT_UUID",
"name": "Staging bearer token",
"authType": "bearer",
"secretRef": "staging_api_token",
"placement": "header",
"headerName": "Authorization"
}'### Create API Test Case
Creates a normal QAA test case with test_type = api and the linked
qa_api_test_cases runner details.
curl -X POST "$SUPABASE_URL/functions/v1/qa-workspace" \
-H "Authorization: Bearer qa_agent_..." \
-H "Content-Type: application/json" \
-d '{
"action": "create-api-test-case",
"projectId": "PROJECT_UUID",
"targetId": "API_TARGET_UUID",
"title": "Customer API returns current profile",
"area": "Customers API",
"method": "GET",
"path": "/customers/me",
"expectedStatus": "200",
"authMode": "target_default",
"queryTemplate": "{}",
"headersTemplate": "{\"accept\":\"application/json\"}",
"assertions": "[{\"type\":\"status_equals\",\"value\":200},{\"type\":\"json_path_exists\",\"path\":\"data.id\"}]",
"steps": [
{
"action": "Send GET request to /customers/me.",
"expected": "The request is executed against the configured API target policy."
},
{
"action": "Validate the HTTP response status and response payload.",
"expected": "Status is 200 and JSON path data.id exists."
}
]
}'If steps is omitted, QAA creates a small default API procedure from the
stored method, path, expected status, and assertions. Prefer sending explicit
steps when the case documents business meaning beyond a raw endpoint check.
### Run API Test Case
The Edge Function runs the controlled API runner, applies QAA target policy,
persists the run result, and stores redacted API evidence. Agents must not send
URL, method, headers, body, or auth secrets to this endpoint. The runner loads
those details from the saved QAA API target and API test case.
curl -X POST "$SUPABASE_URL/functions/v1/qa-run-api-test-case" \
-H "Authorization: Bearer qa_agent_..." \
-H "Content-Type: application/json" \
-d '{
"projectId": "PROJECT_UUID",
"testCaseId": "API-CUSTOMER-...",
"runId": "RUN_ID",
"approvedMutation": false
}'Requires test_results:write. Project-scoped tokens can only run API test
cases for their own project. Failed API runner results create the same linked
QA Work Board bug ticket and agent_prompt as qa-add-result. The response
includes a compact runner summary:
{
"result": { "status": "Passed" },
"workItem": null,
"workItemError": null,
"autoTicket": {
"functionVersion": "qa-run-api-test-case:auto-ticket-v2",
"normalizedStatus": "Passed",
"attempted": false,
"createdOrReused": false,
"error": null
},
"runner": {
"request": {
"method": "GET",
"url": "https://api.example.com/customers/me?api_key=%5BREDACTED%5D"
},
"response": { "status": 200, "bodyStorage": "excerpt" },
"assertions": [
{ "type": "status_equals", "passed": true, "message": "Status was 200." }
]
}
}The signed-in app route may still exist as a UI-only/internal runner surface,
but agent automation should use the Edge Function above.
Runner safety checks:
- Method whitelist is strict.
- Paths must be relative to the target base URL and cannot be absolute or
protocol-relative.
- Redirects are not followed automatically.
- Bodies are not sent for GET or HEAD.
- Assertions run even when the expected status is non-2xx.
- Headers, query values, request bodies, response headers, and response bodies
are redacted before persistence.
Mobile Testing V1
Mobile testing is a first-class test type beside web and API testing. QAA owns
the project configuration, safe device policy, mobile test case metadata, run
status, and redacted evidence. The ADB execution engine lives in the harness or
agent runtime, not in the app UI.
Agents must not run arbitrary ADB commands from QAA. A mobile run is limited to
the stored mobile test case contract and the configured device policy.
Core rules:
- Create a mobile device before creating mobile cases.
- Only Android is supported in V1.
- Do not require Appium or Playwright for the first mobile path.
- Devices must be enabled and is_safe_device = true before execution.
- App packages must match the device allowed_packages list when configured.
- Raw screenshots, UIAutomator dumps, and logcat output belong in the harness
first. QAA stores only paths and redacted excerpts.
- Harness-provided mobile screenshot URLs are mirrored into the same
qa_test_evidence gallery used by web runs when they are public-renderable
http(s) artifact URLs.
- Public reports show only a redacted mobile summary by default. Do not expose
logcat, UI dumps, secrets, or unapproved screenshots publicly.
Supported mobile steps:
[
{ "type": "tap", "target": "text=Login" },
{ "type": "tap", "x": 120, "y": 600 },
{ "type": "text", "valueRef": "test_user_email" },
{ "type": "swipe", "direction": "up" },
{ "type": "press", "key": "BACK" },
{ "type": "wait_for_text", "value": "Welcome" },
{ "type": "screenshot" }
]Harness implementations should prefer UIAutomator selectors from text,
resource-id, and content-desc before falling back to coordinates.
Supported mobile assertions:
[
{ "type": "text_visible", "value": "Welcome" },
{ "type": "text_not_visible", "value": "Crash" },
{ "type": "element_exists", "selector": "resource-id=com.app:id/login" },
{ "type": "current_package_equals", "value": "com.example.app" },
{ "type": "no_crash_in_logcat" },
{ "type": "screen_changed" }
]### Create Mobile Device
Use the workspace endpoint to register a safe Android test device and policy.
curl -X POST "$SUPABASE_URL/functions/v1/qa-workspace" \
-H "Authorization: Bearer qa_agent_..." \
-H "Content-Type: application/json" \
-d '{
"action": "create-mobile-device",
"projectId": "PROJECT_UUID",
"name": "Pixel staging device",
"deviceSerial": "emulator-5554",
"model": "Pixel 8",
"osVersion": "Android 15",
"appPackage": "com.example.app",
"environment": "staging",
"isSafeDevice": "true",
"allowedPackages": "com.example.app\ncom.example.app.staging",
"forbiddenActions": "",
"notes": "Dedicated staging Android device. Do not use against production accounts."
}'### Create Mobile Test Case
Creates a normal QAA test case with test_type = mobile and the linked
qa_mobile_test_cases runner details.
curl -X POST "$SUPABASE_URL/functions/v1/qa-workspace" \
-H "Authorization: Bearer qa_agent_..." \
-H "Content-Type: application/json" \
-d '{
"action": "create-mobile-test-case",
"projectId": "PROJECT_UUID",
"deviceId": "DEVICE_UUID",
"title": "Android login smoke reaches welcome screen",
"area": "Mobile Auth",
"appPackage": "com.example.app",
"launchMode": "package",
"preconditions": "{\"account\":\"test_user_email\"}",
"steps": "[{\"type\":\"tap\",\"target\":\"text=Login\"},{\"type\":\"text\",\"valueRef\":\"test_user_email\"},{\"type\":\"tap\",\"target\":\"text=Continue\"},{\"type\":\"wait_for_text\",\"value\":\"Welcome\"}]",
"assertions": "[{\"type\":\"text_visible\",\"value\":\"Welcome\"},{\"type\":\"no_crash_in_logcat\"}]",
"safetyLevel": "safe_interaction",
"timeoutMs": "60000"
}'Safety levels:
- read: capture screen/UI tree/log signals only.
- safe_interaction: taps, text input using approved test data, swipes, back,
waits, and screenshots.
- destructive: requires explicit human approval and should normally be
avoided in shared environments.
### Run Mobile Test Case
The signed-in app exposes a runner contract at:
curl -X POST "$APP_URL/api/qa-mobile-runner" \
-H "Cookie: authenticated_app_session=..." \
-H "Content-Type: application/json" \
-d '{
"projectId": "PROJECT_UUID",
"testCaseId": "MOB-LOGIN-...",
"deviceId": "DEVICE_UUID",
"approvedInteraction": false
}'The app validates QAA policy and then calls the mobile harness when
QA_MOBILE_HARNESS_URL is configured. Without a configured harness, the runner
blocks safely and persists redacted evidence explaining that no ADB execution
occurred.
Harness input shape:
{
"deviceSerial": "emulator-5554",
"appPackage": "com.example.app",
"launch": { "mode": "package", "value": null },
"safetyLevel": "safe_interaction",
"timeoutMs": 60000,
"steps": [],
"assertions": []
}Harness response shape:
{
"status": "passed",
"durationMs": 12340,
"assertionResults": [
{
"type": "text_visible",
"passed": true,
"message": "Welcome was visible."
}
],
"screenshotBeforePath": "https://...",
"screenshotAfterPath": "https://...",
"uiDumpExcerpt": "redacted UIAutomator excerpt",
"logcatExcerpt": "redacted crash/log signal excerpt",
"blockedReason": null,
"errorMessage": null
}Agents should treat mobile screenshots and logcat as sensitive. Store only
redacted excerpts and stable artifact paths returned by the harness. Use
Application Map screenshots for stable product surfaces, test case screenshots
for expected reference states, and run evidence screenshots for what happened
during a specific execution.
Application Map
The Application Map is the diagram workspace for how the system behaves, where tests
attach, and where risk lives. It is separate from executable test cases. Use it
to describe pages, components, customer flows, API calls, repo/service
dependencies, external systems, and important UI states.
Agents should write durable nodes and edges first. The UI can render those
records as a heatmap, Mermaid-style diagrams, app/customer/API/repo flows, and
coverage-shape charts. Link test cases to map nodes only when a case actually
covers or validates that surface. Stakeholders use this map to see product
structure, missing coverage, risky dependencies, and visual context.
Recommended node/edge modeling:
- Build structured graph records first. Mermaid, React Flow, and other diagrams
are renderers/export views, not the source of truth.
- App behavior layer (layer: "application"): page/state/component/flow nodes
connected by navigation, redirect, modal, or contains edges.
- API layer (layer: "api"): api_endpoint, api_operation, service, and
external_service nodes connected by calls, api, or dependency edges.
Include auth requirements, methods, status families, mutation policy, and
redaction notes in metadata.
- Data layer (layer: "data"): data_entity, data_store, and
database_table nodes connected by reads, writes, owns, or
dependency edges. Include sensitive fields, ownership, source, and evidence
in metadata.
- Coverage layer (layer: "coverage"): test_case and coverage_area nodes
connected to application/API/data nodes conceptually through covers,
validates, blocks, or related edges. Continue using
qa-link-test-case-to-app-node for the durable test-to-surface link.
- Test attachment: link test cases to the node they prove with
qa-link-test-case-to-app-node.
- Risk: set riskLevel and coverageStatus from latest results, known
defects, blockers, missing evidence, and untested critical paths.
- Discovery expectation: agents should populate these layers from multiple
observations when available: browser walkthroughs, public routes, network
calls, API docs/OpenAPI, saved QAA API targets, database/schema metadata,
screenshots, prior runs, work items, and retest queue history. Do not only
mirror the same app map in a different format.
### List Application Map
curl -X POST "$SUPABASE_URL/functions/v1/qa-list-app-map" \
-H "Authorization: Bearer qa_agent_..." \
-H "Content-Type: application/json" \
-d '{
"projectId": "PROJECT_UUID"
}'Requires projects:read. Returns nodes, edges, and links. The UI derives
heatmaps, Mermaid-style source, risk paths, and coverage-shape metrics from
these records.
### Upsert Application Map Node
curl -X POST "$SUPABASE_URL/functions/v1/qa-upsert-app-map-node" \
-H "Authorization: Bearer qa_agent_..." \
-H "Content-Type: application/json" \
-d '{
"projectId": "PROJECT_UUID",
"layer": "application",
"key": "login-page",
"label": "Login page",
"kind": "page",
"route": "/login",
"url": "https://example.com/login",
"description": "Authentication entry point for returning users.",
"riskLevel": "critical",
"coverageStatus": "partial",
"position": { "x": 120, "y": 80 },
"screenshots": [
{
"url": "https://...public.blob.vercel-storage.com/qa/project/login.png",
"label": "Login page",
"targetUrl": "https://example.com/login",
"kind": "page",
"capturedAt": "2026-06-18T10:20:00.000Z",
"viewport": { "width": 1440, "height": 900, "device": "desktop" }
}
],
"metadata": {
"authRequired": false,
"discoveredBy": "playwright"
}
}'Requires test_cases:write. key must be stable within the project layer. If a
node with the same projectId, layer, and key exists, it is updated.
Supported layer values: application, api, data, and coverage.
Supported kind values: page, component, flow, service, external,
state, api_endpoint, api_operation, data_entity, data_store,
database_table, external_service, test_case, and coverage_area.
Supported riskLevel values: low, medium, high, and critical.
Supported coverageStatus values: unknown, missing, partial, covered,
and failing.
The endpoint also accepts "nodes": [...] for bulk upsert. To attach visual
context, first upload a screenshot with /api/qa-screenshots, then copy the
returned screenshot object into screenshots.
### Upsert Application Map Edge
curl -X POST "$SUPABASE_URL/functions/v1/qa-upsert-app-map-edge" \
-H "Authorization: Bearer qa_agent_..." \
-H "Content-Type: application/json" \
-d '{
"projectId": "PROJECT_UUID",
"layer": "application",
"sourceKey": "login-page",
"targetKey": "dashboard",
"kind": "navigation",
"label": "Successful login",
"metadata": {
"trigger": "submit valid credentials"
}
}'Requires test_cases:write. Use sourceKey/targetKey for stable agent
writes, or sourceNodeId/targetNodeId if ids are already known. Edges default
to resolving keys inside their own layer; use sourceLayer or targetLayer
when linking across layers. Supported kind values: navigation, redirect,
dependency, api, modal, contains, related, calls, reads, writes,
owns, covers, validates, and blocks.
Use edge kinds consistently:
- navigation: user can move from one surface to another.
- redirect: the system sends the user to another surface.
- modal: one surface opens or controls a transient state.
- contains: parent flow/page owns a component or state.
- api: frontend or service calls another service or endpoint.
- calls: one API operation calls another API/service operation.
- reads: a UI/API/service reads a data entity, store, or table.
- writes: a UI/API/service mutates a data entity, store, or table.
- owns: a service or domain owns a data entity or table.
- covers: a test or coverage area exercises a surface.
- validates: a test validates a surface, contract, or invariant.
- blocks: a failure, defect, or dependency blocks confidence in a surface.
- dependency: repo/service/component depends on another unit.
- related: weak relationship when context exists but a hard flow is unknown.
### Link Test Case To Application Map Node
curl -X POST "$SUPABASE_URL/functions/v1/qa-link-test-case-to-app-node" \
-H "Authorization: Bearer qa_agent_..." \
-H "Content-Type: application/json" \
-d '{
"projectId": "PROJECT_UUID",
"testCaseId": "POOLIO-AUTH-002",
"nodeKey": "login-page",
"relationship": "covers"
}'Requires test_cases:write. Supported relationships: covers, validates,
blocks, and mentions. Prefer covers for normal regression coverage.
### Generate Application Map From Test Cases
curl -X POST "$SUPABASE_URL/functions/v1/qa-generate-app-map-from-test-cases" \
-H "Authorization: Bearer qa_agent_..." \
-H "Content-Type: application/json" \
-d '{
"projectId": "PROJECT_UUID"
}'Requires test_cases:write. This creates or updates one starter map node per
test-case area, links existing cases to those nodes, and infers coverage/risk
from current test statuses and priorities. Use this only as a bootstrap step;
after exploration, replace area-level nodes with concrete pages, flows,
components, services, and states.
Test Suites
Test suites are reusable, named collections of durable test cases. A test case
may belong to several suites. Suites are not execution history: a run plan
resolves the selected suites to a deduplicated test-case snapshot, and the
later run/result records remain unchanged if suite membership changes.
List the active suites for a project:
curl "$SUPABASE_URL/functions/v1/qa-test-suites?projectId=PROJECT_UUID" \
-H "Authorization: Bearer qa_agent_..."Create a suite:
curl -X POST "$SUPABASE_URL/functions/v1/qa-test-suites" \
-H "Authorization: Bearer qa_agent_..." \
-H "Content-Type: application/json" \
-d '{
"projectId": "PROJECT_UUID",
"name": "Authentication smoke",
"description": "Fast release-critical checks for sign-in and sign-out.",
"purpose": "smoke",
"tags": ["authentication", "release-critical"],
"testCaseIds": ["AUTH-001", "AUTH-002", "AUTH-005"]
}'Update the suite or replace its ordered membership:
curl -X PATCH "$SUPABASE_URL/functions/v1/qa-test-suites" \
-H "Authorization: Bearer qa_agent_..." \
-H "Content-Type: application/json" \
-d '{
"projectId": "PROJECT_UUID",
"id": "SUITE_UUID",
"name": "Authentication smoke",
"purpose": "smoke",
"testCaseIds": ["AUTH-001", "AUTH-002", "AUTH-005", "AUTH-008"]
}'Archive a suite without changing historic runs:
curl -X DELETE "$SUPABASE_URL/functions/v1/qa-test-suites?projectId=PROJECT_UUID&id=SUITE_UUID" \
-H "Authorization: Bearer qa_agent_..."Reading requires projects:read; creating, updating, and archiving requires
test_cases:write. Supported purposes are smoke, regression, feature,
release, and custom.
Agent suite rules:
- Reuse an existing relevant suite instead of creating one for every run.
- Create a suite only when the collection is expected to be reused.
- A case may belong to several suites; deduplicate cases before execution.
- Leave a case outside suites when membership is unclear instead of guessing.
- Never treat a suite as a result or a run. Suite membership is organization;
results belong to a specific run.
- Never rewrite historic runs after a suite changes.
List Test Cases
curl -X POST "$SUPABASE_URL/functions/v1/qa-list-test-cases" \
-H "Authorization: Bearer qa_agent_..." \
-H "Content-Type: application/json" \
-d '{
"projectId": "PROJECT_UUID",
"status": "Failed",
"priority": "Critical",
"includeSteps": true,
"includeResults": true,
"limit": 50
}'Requires projects:read. If the token is scoped to one project, projectId is
optional and any other project id is rejected. The endpoint also accepts GET
query parameters, for example:
curl "$SUPABASE_URL/functions/v1/qa-list-test-cases?projectId=PROJECT_UUID&area=Checkout&includeResults=true" \
-H "Authorization: Bearer qa_agent_..."Supported filters: projectId, id, status, priority, area, release,
search, and limit. includeSteps defaults to true; includeResults
defaults to false. Test cases with project_id = null are treated as legacy
global/demo cases and are hidden by default; pass includeGlobal: true or
includeGlobal=true to include them.
Response shape:
{
"testCases": [
{
"id": "LYS-CONTACT-006",
"project_id": "PROJECT_UUID",
"title": "Contact form accepts a qualified lead",
"description": "Validates lead capture submission.",
"area": "Lead capture",
"status": "Failed",
"priority": "Critical",
"preconditions": ["Preview deployment is available"],
"expected_result": "The lead is submitted and confirmed.",
"steps": [
{
"position": 1,
"action": "Open the contact page.",
"expected": "The form is visible."
}
],
"results": [
{
"status": "Failed",
"environment": "Production",
"notes": "Submission returned validation error."
}
]
}
]
}Use this endpoint when the agent needs to answer questions such as:
- "Which critical test cases are currently failing?"
- "Summarize the checkout test coverage and preconditions."
- "What steps should be executed for this test case?"
- "Which tests are blocked for the current release?"
Create Test Case
If the agent has captured a page or component image, upload it first with
/api/qa-screenshots and pass the returned object in screenshots. Screenshots
belong on test cases when they describe the intended page/component/state that
the case covers. Save execution failure evidence on run results later.
curl -X POST "$SUPABASE_URL/functions/v1/qa-create-test-case" \
-H "Authorization: Bearer qa_agent_..." \
-H "Content-Type: application/json" \
-d '{
"projectId": "PROJECT_UUID",
"id": "LYS-CHECKOUT-009",
"title": "Checkout confirms successful card payment",
"description": "Verifies that a shopper can complete payment and reach the receipt page.",
"area": "Checkout",
"priority": "Critical",
"browser": "Chrome",
"device": "Desktop",
"status": "Not run",
"suiteType": "regression",
"qualityDimensions": ["functional"],
"agentGenerated": true,
"strategyNotes": "Durable regression coverage created from checkout release-risk analysis.",
"tags": ["checkout", "payment", "release-critical"],
"screenshots": [
{
"url": "https://...public.blob.vercel-storage.com/qa/project/case/checkout.png",
"label": "Checkout payment form",
"targetUrl": "https://example.com/checkout",
"kind": "page",
"capturedAt": "2026-06-18T10:20:00.000Z",
"viewport": {
"width": 1440,
"height": 900,
"device": "desktop"
}
}
],
"owner": "QA Agent",
"release": "RC-2026.06",
"preconditions": ["Cart contains one product", "Test payment gateway is available"],
"expectedResult": "The order is confirmed and the receipt is visible.",
"testUrl": "https://example.com/checkout",
"steps": [
{
"action": "Open checkout with a product in cart.",
"expected": "Checkout form is visible."
},
{
"action": "Submit valid payment details.",
"expected": "Payment succeeds and receipt page opens."
}
]
}'Requires test_cases:write. If the token is scoped to one project, projectId
is optional and any other project id is rejected. If id is omitted, the
endpoint generates a TC-... id. Prefer stable human-readable ids when the
agent is creating durable catalog tests.
The endpoint can also create several cases at once:
curl -X POST "$SUPABASE_URL/functions/v1/qa-create-test-case" \
-H "Authorization: Bearer qa_agent_..." \
-H "Content-Type: application/json" \
-d '{
"projectId": "PROJECT_UUID",
"testCases": [
{
"id": "LYS-A11Y-010",
"title": "Header navigation exposes visible focus states",
"area": "Accessibility",
"priority": "High",
"expectedResult": "Keyboard focus is visible on every header control.",
"steps": [
{
"action": "Tab through the header navigation.",
"expected": "Focus follows visual order and remains visible."
}
]
},
{
"id": "LYS-SEO-011",
"title": "Product pages expose canonical metadata",
"area": "SEO",
"priority": "Medium",
"expectedResult": "Each product page has one canonical URL."
}
]
}'Supported fields: projectId, id, title, description, area, browser,
device, status, priority, suiteType (legacy classification), agentGenerated,
strategyNotes, qualityDimensions, tags, screenshots, owner, durationSeconds,
lastRunLabel, release, preconditions, expectedResult, testUrl,
sortOrder, and steps. Each step requires action and expected.
When steps is sent to qa-update-test-case, it replaces the full stored
step list for that test case.
Use status: "Not run" for newly planned or enriched test cases. Do not use
Running when only creating or updating coverage metadata; Running should be
reserved for actual execution state written from a run/result workflow.
Update Test Case
Use this endpoint when the agent needs to attach screenshots or update metadata
on an existing durable test case. This is the correct follow-up after
/api/qa-screenshots; uploading a screenshot stores the file in Blob, while
this endpoint writes the returned metadata onto the test case.
curl -X PATCH "$SUPABASE_URL/functions/v1/qa-update-test-case" \
-H "Authorization: Bearer qa_agent_..." \
-H "Content-Type: application/json" \
-d '{
"projectId": "PROJECT_UUID",
"testCaseId": "POOLIO-AUTH-002",
"screenshots": [
{
"url": "https://...public.blob.vercel-storage.com/qa/project/case/login.png",
"label": "Poolio login page",
"targetUrl": "https://poolio.example.com/login",
"kind": "page",
"capturedAt": "2026-06-18T10:20:00.000Z",
"viewport": {
"width": 1440,
"height": 900,
"device": "desktop"
}
}
]
}'Requires test_cases:write. By default, incoming screenshots are appended to
the current screenshots array and deduplicated by url. Send
"replaceScreenshots": true to replace the screenshot list instead.
Supported update fields: projectId, id, testCaseId, title,
description, area, browser, device, status, priority, suiteType (legacy classification),
agentGenerated, strategyNotes, qualityDimensions, tags, screenshots,
replaceScreenshots, owner, durationSeconds, lastRunLabel, release,
preconditions, expectedResult, testUrl, and sortOrder.
Upload QA Screenshot
Use this endpoint when the agent captures a screenshot from any supported test
surface and needs to attach it to QAA. The screenshot surface can be web,
mobile, API-adjacent visual evidence, or a future device/runtime technique. The
endpoint stores the image in Vercel Blob and returns metadata that can be copied
into qa-create-test-case.screenshots, qa-update-test-case.screenshots, or
Application Map node screenshots. The same endpoint also returns an evidence
object. Use that object when a screenshot is evidence for a specific run result
or failed step.
Screenshots are a shared QAA primitive:
- Application Map screenshots describe stable product surfaces.
- Test case screenshots describe expected reference states.
- Run evidence screenshots describe what happened in one execution.
- Mobile screenshots should use the same endpoint when the harness has a safe
image file to upload. Set metadata such as
{"source":"mobile","platform":"android","deviceSerial":"..."}.
curl -X POST "$APP_URL/api/qa-screenshots" \
-H "Authorization: Bearer qa_agent_..." \
-F "projectId=PROJECT_UUID" \
-F "testCaseId=LYS-CHECKOUT-009" \
-F "runId=run-checkout-regression" \
-F "stepPosition=2" \
-F "label=Checkout payment form" \
-F "targetUrl=https://example.com/checkout" \
-F "kind=failure" \
-F "notes=Payment form shows validation error after submit." \
-F 'viewport={"width":1440,"height":900,"device":"desktop"}' \
-F 'metadata={"source":"web","technique":"playwright"}' \
-F "file=@checkout-payment-form.png;type=image/png"Mobile example:
curl -X POST "$APP_URL/api/qa-screenshots" \
-H "Authorization: Bearer qa_agent_..." \
-F "projectId=PROJECT_UUID" \
-F "testCaseId=MOB-LOGIN-001" \
-F "runId=run-mobile-login" \
-F "label=Android login failure" \
-F "targetUrl=com.example.app" \
-F "kind=failure" \
-F "notes=Welcome text did not appear after login." \
-F 'viewport={"width":1080,"height":2400,"device":"Pixel 8","platform":"android"}' \
-F 'metadata={"source":"mobile","platform":"android","technique":"adb-screencap"}' \
-F "file=@android-login-failure.png;type=image/png"Requires an agent token with test_cases:write. If the token is scoped to one
project, any different projectId is rejected. Maximum file size is 8 MB.
Response shape:
{
"screenshot": {
"url": "https://...public.blob.vercel-storage.com/qa/project/case/file.png",
"label": "Checkout payment form",
"targetUrl": "https://example.com/checkout",
"kind": "failure",
"capturedAt": "2026-06-18T10:20:00.000Z",
"viewport": {
"width": 1440,
"height": 900,
"device": "desktop"
},
"metadata": {
"source": "web",
"technique": "playwright"
}
},
"evidence": {
"url": "https://...public.blob.vercel-storage.com/qa/project/case/file.png",
"label": "Checkout payment form",
"targetUrl": "https://example.com/checkout",
"kind": "failure",
"capturedAt": "2026-06-18T10:20:00.000Z",
"runId": "run-checkout-regression",
"resultId": null,
"testCaseId": "LYS-CHECKOUT-009",
"stepPosition": 2,
"notes": "Payment form shows validation error after submit.",
"viewport": {
"width": 1440,
"height": 900,
"device": "desktop"
},
"metadata": {
"source": "web",
"technique": "playwright"
}
}
}Recommended screenshot types:
- For durable test-case screenshots: page, component, or state.
- For run result evidence: before, after, failure, or reference.
When a step fails, prefer uploading a screenshot immediately after the failed
assertion and passing the returned evidence object to qa-add-result. Always
include stepPosition for step-level evidence when a specific step failed.
Public reports render non-sensitive evidence next to the failed step and in the
recent result/run result rows. Mobile evidence is treated as sensitive by
default and should only be shown publicly when a project/report policy explicitly
allows it.
Response shape:
{
"testCases": [
{
"id": "LYS-CHECKOUT-009",
"project_id": "PROJECT_UUID",
"title": "Checkout confirms successful card payment",
"status": "Running",
"priority": "Critical"
}
],
"steps": [
{
"test_case_id": "LYS-CHECKOUT-009",
"position": 1,
"action": "Open checkout with a product in cart.",
"expected": "Checkout form is visible."
}
]
}Use this endpoint when the agent discovers missing test coverage, converts a
bug report into a repeatable regression test, or creates planned cases before
running qa-create-run and qa-add-result.
List Test Runs
curl -X POST "$SUPABASE_URL/functions/v1/qa-list-test-runs" \
-H "Authorization: Bearer qa_agent_..." \
-H "Content-Type: application/json" \
-d '{
"projectId": "PROJECT_UUID",
"status": "Failed",
"includeResults": true,
"limit": 20
}'Requires projects:read. If the token is scoped to one project, projectId is
optional and any other project id is rejected. The endpoint also accepts GET
query parameters, for example:
curl "$SUPABASE_URL/functions/v1/qa-list-test-runs?projectId=PROJECT_UUID&triggerType=AI%20Agent&includeResults=true" \
-H "Authorization: Bearer qa_agent_..."Supported filters: projectId, runId, id, status, environment,
triggerType, search, and limit. includeResults defaults to true.
Response shape:
{
"runs": [
{
"id": "run-lysande-public-web",
"project_id": "PROJECT_UUID",
"name": "Lysande public web regression",
"environment": "Production",
"status": "Failed",
"trigger_type": "AI Agent",
"passed_count": 2,
"failed_count": 1,
"blocked_count": 1,
"total_count": 5,
"scope": ["Homepage", "Lead capture", "SEO"],
"notes": "Contact form validation failed.",
"results": [
{
"test_case_id": "LYS-CONTACT-006",
"test_case_title": "Contact form accepts a qualified lead",
"status": "Failed",
"notes": "Submission returned validation error."
}
]
}
]
}Use this endpoint when the agent needs to answer questions such as:
- "Summarize the latest failed run."
- "What failed, passed, or was blocked in the last production run?"
- "Compare recent AI-agent runs for this project."
- "Which test cases repeatedly fail in recent runs?"
Run Plan
Create a run plan before execution. The plan is Sladdis' saved intent: scope,
selected suites/tests, risks, reason, and expected evidence. The endpoint
resolves selected suites and individually selected cases into a deduplicated
selected_test_case_ids snapshot. The later test run links back to this plan
so QAA can compare plan vs actual even when suite membership changes later.
When Sladdis delegates to subagents, include tracks. Tracks are parallel
worker scopes inside the same run plan. Sladdis remains the owner of the final
run, result normalization, dedupe, and QAA persistence.
curl -X POST "$SUPABASE_URL/functions/v1/qa-run-plan" \
-H "Authorization: Bearer qa_agent_..." \
-H "Content-Type: application/json" \
-d '{
"projectId": "PROJECT_UUID",
"title": "Checkout release readiness",
"runType": "regression",
"environment": "Preview",
"scope": ["Checkout", "Authentication", "Payment confirmation"],
"notInScope": ["Refund admin flows"],
"riskAreas": ["Payment validation", "Session expiry"],
"selectedTestSuiteIds": ["CHECKOUT_REGRESSION_SUITE_UUID"],
"selectedTestCaseIds": ["LYS-CHECKOUT-001", "LYS-AUTH-003"],
"reason": "Recent checkout changes touched payment and auth handoff.",
"expectedEvidence": ["Screenshots for failed steps", "Console/network errors when present"],
"sourcePrompt": "Validate checkout release readiness before deploy.",
"confidenceBefore": 62,
"tracks": [
{
"name": "UX/UI sweep",
"agentName": "Sladdis UX Subagent",
"focusArea": "Navigation, responsive layout, copy, friction",
"status": "planned",
"scope": ["Desktop first-pass", "Mobile viewport smoke"],
"riskAreas": ["Responsive layout", "Primary CTA clarity"],
"expectedEvidence": ["Screenshots for layout defects"]
},
{
"name": "Accessibility smoke",
"agentName": "Sladdis A11y Subagent",
"focusArea": "Keyboard, labels, focus, landmark smoke",
"status": "planned",
"scope": ["Keyboard tab order", "Form labels", "Accessible names"],
"riskAreas": ["Keyboard traps", "Missing labels"],
"expectedEvidence": ["Failed selector or screenshot evidence"]
}
]
}'Requires test_runs:write. The response contains runPlan.id and
runPlan.tracks[] when tracks were saved. Pass the plan id to qa-create-run
as runPlanId.
List recent plans:
curl "$SUPABASE_URL/functions/v1/qa-run-plan?projectId=PROJECT_UUID&status=planned" \
-H "Authorization: Bearer qa_agent_..."Update a plan manually when needed:
curl -X PATCH "$SUPABASE_URL/functions/v1/qa-run-plan" \
-H "Authorization: Bearer qa_agent_..." \
-H "Content-Type: application/json" \
-d '{
"projectId": "PROJECT_UUID",
"id": "RUN_PLAN_UUID",
"status": "completed",
"actualRunId": "run-...",
"actualSummary": {
"status": "Failed",
"total": 5,
"passed": 2,
"failed": 2,
"blocked": 1,
"durationSeconds": 420
}
}'Delete an incorrect or obsolete plan draft when it should no longer appear in
Overview. This removes the run plan and its tracks. Existing test runs and test
cases are not deleted; linked runs lose the plan reference.
curl -X DELETE "$SUPABASE_URL/functions/v1/qa-run-plan?projectId=PROJECT_UUID&id=RUN_PLAN_UUID" \
-H "Authorization: Bearer qa_agent_..."Update one or more subagent tracks as workers finish:
curl -X PATCH "$SUPABASE_URL/functions/v1/qa-run-plan" \
-H "Authorization: Bearer qa_agent_..." \
-H "Content-Type: application/json" \
-d '{
"projectId": "PROJECT_UUID",
"id": "RUN_PLAN_UUID",
"tracks": [
{
"id": "TRACK_UUID",
"status": "completed",
"actualRunId": "run-...",
"summary": "Desktop navigation passed; mobile CTA wraps awkwardly on 390px.",
"findings": [
{
"severity": "Medium",
"title": "Mobile CTA wraps into two uneven lines",
"recommendation": "Shorten the label or widen the button container."
}
],
"coverage": {
"viewports": ["1440x900", "390x844"],
"areas": ["home", "pricing"]
},
"confidenceAfter": 76
}
]
}'Normally qa-create-run marks the linked plan in_progress, and
qa-update-run marks it completed with an actualSummary when the run
finishes as Passed, Failed, or Blocked.
Create Run
curl -X POST "$SUPABASE_URL/functions/v1/qa-create-run" \
-H "Authorization: Bearer qa_agent_..." \
-H "Content-Type: application/json" \
-d '{
"projectId": "PROJECT_UUID",
"name": "Checkout regression",
"environment": "Preview",
"status": "Running",
"runPlanId": "RUN_PLAN_UUID",
"runType": "regression",
"strategy": "Focus on release-critical checkout and authentication paths first.",
"requestedBy": "agent",
"sourcePrompt": "Validate checkout release readiness.",
"startedAt": "2026-06-22T09:16:49.195Z",
"scope": ["Checkout", "Authentication"]
}'Set startedAt to the moment the execution actually begins. The app uses the
run started_at timestamp as the displayed "last run" time for each test case
in that run. If omitted, the API uses the server receive time.
Update Run
curl -X PATCH "$SUPABASE_URL/functions/v1/qa-update-run" \
-H "Authorization: Bearer qa_agent_..." \
-H "Content-Type: application/json" \
-d '{
"runId": "run-...",
"status": "Failed",
"finishedAt": "2026-06-16T14:00:00.000Z",
"durationSeconds": 420,
"notes": "Checkout failed on payment validation."
}'Add Result
curl -X POST "$SUPABASE_URL/functions/v1/qa-add-result" \
-H "Authorization: Bearer qa_agent_..." \
-H "Content-Type: application/json" \
-d '{
"runId": "run-...",
"testCaseId": "LYS-CHECKOUT-001",
"testCaseTitle": "Checkout accepts valid card details",
"status": "Failed",
"durationSeconds": 84,
"failedStepPosition": 2,
"failureMessage": "Payment form returned a validation error.",
"notes": "Payment submit returned validation error.",
"evidence": [
{
"url": "https://...public.blob.vercel-storage.com/qa/project/case/file.png",
"label": "Payment validation failure",
"targetUrl": "https://example.com/checkout",
"kind": "failure",
"capturedAt": "2026-06-18T10:20:00.000Z",
"stepPosition": 2,
"notes": "Validation banner remained visible after submitting valid card details.",
"viewport": {
"width": 1440,
"height": 900,
"device": "desktop"
}
}
]
}'Requires test_results:write. The endpoint writes to the run result log and
also mirrors the result into the test case history used by qa-list-test-cases
and the app's "Recent results" section. If browser or device is omitted,
the endpoint copies those values from the test case. If environment is
omitted, it uses the run environment.
When status is Failed, QAA automatically creates a linked QA Work Board bug
ticket unless one already exists for the same run and test case. The ticket is
created in backlog, has item_type: "bug", qa_status: "failed", links the
test case in linked_test_case_ids, stores the run in qa_run_id, and includes
an agent_prompt repair prompt. The prompt is meant to be copied into a coding
agent: it summarizes the failing test, environment, target, failed step,
evidence labels, reproduction task, and definition of done. Do not create a
second work item for the same failed result unless the first ticket was closed
and the failure is genuinely new.
When a result fails on a specific test step, send failedStepPosition using
the 1-based step number from the test case. Send failureMessage when there is
a concise step-level failure reason. The app uses these fields to highlight the
failed step in the test case detail view and to show the failed step in Recent
results. For credible reports, include evidence screenshots for failed or
blocked results whenever possible. Evidence rows are stored separately from the
durable test case screenshots, so each run can show what happened during that
specific execution.
Response shape:
{
"result": {
"id": "RUN_RESULT_UUID",
"status": "Failed"
},
"workItem": {
"id": "WORK_ITEM_UUID"
},
"workItemError": null,
"autoTicket": {
"functionVersion": "qa-add-result:auto-ticket-v2",
"requestedStatus": "Failed",
"normalizedStatus": "Failed",
"attempted": true,
"createdOrReused": true,
"error": null
}
}workItem is null for passed results. If a matching failure ticket already
exists for the same run and test case, or an open bug already exists for the
same test case, workItem.id is the existing ticket id.
If the result is saved but QAA cannot create the Work Board ticket, the endpoint
still returns 201 for the saved result with workItem: null and a non-empty
workItemError. Treat that as an infrastructure issue: record the error in the
run notes or a triage action, then report it to the QAA owner. A common rollout
case is a missing agent_prompt column; QAA will try a fallback ticket without
that column and return a workItemError telling the operator to apply
202606260001_add_work_item_agent_prompt.sql and reload the PostgREST schema.
Use autoTicket.functionVersion to confirm the deployed Edge Function version.
If the field is missing, the endpoint is running an older deploy. If
attempted is false, check the submitted status and normalizedStatus.
When status is Failed or Blocked, QAA also creates or reuses an open
Retest Queue V2 item. The retest is linked to the runId, saved result id,
testCaseId, and the auto-created/reused work item when available. The
response includes retestItem and retestError. Treat this automatic path as
the default. Create a retest item manually only when retestItem is null,
retestError is non-empty, or the unresolved risk did not originate from a
saved Failed or Blocked result.
Create Triage Action
curl -X POST "$SUPABASE_URL/functions/v1/qa-create-triage-action" \
-H "Authorization: Bearer qa_agent_..." \
-H "Content-Type: application/json" \
-d '{
"projectId": "PROJECT_UUID",
"runId": "run-checkout-regression",
"testCaseId": "LYS-CHECKOUT-001",
"action": "create_defect",
"defectTitle": "Checkout rejects valid card details",
"defectSeverity": "P1",
"note": "Agent reproduced failure on step 2 with payment validation error."
}'Requires test_results:write. Supported actions:
- reviewed
- assign_owner
- mark_flaky
- request_rerun
- create_defect
Use triage actions when the agent has enough evidence to recommend a human
workflow decision. The Work Board bug created or reused by qa-add-result is
the primary failure handoff. Reuse that work item for normal failed-result
follow-up. Use create_defect only when a separate formal defect record is
explicitly needed; it inserts a row in qa_defects and must not be used to
duplicate the existing Work Board bug.
Operational Decision Contract
Use this contract before the detailed loop below:
1. Start from the project brief and choose one QA outcome: verify a handoff,
investigate a failure, close a retest, measure coverage, or support a
release decision. Do not collect unrelated checks into one run.
2. Reuse an existing test case, open defect, work item, and retest whenever they
describe the same behavior. Update the durable record instead of creating a
parallel version of the same problem.
3. A multi-case, coverage, regression, or release run requires a saved run
plan. A single-case linked retest may run without a new plan. It still gets
a new run and result, but keeps traceability to the original failure through
the retest item's source runId, resultId, and testCaseId; omit
runPlanId rather than creating an empty plan.
4. A failed or blocked result requires evidence or an explicit explanation of
why evidence could not be captured. Never present an unsupported failure as
release-blocking certainty.
5. Assess releaseImpact separately from severity. A severe bug can be outside
the active release scope, and a visually small defect can block a critical
flow.
6. Close loops. A passing retest must resolve its retest item and update the
linked Work Board card. A repeated failure must update the existing records
with new evidence and history.
7. Save memory notes only for durable facts. Execution narrative belongs on
the run, actionable failures belong on work items, and future verification
belongs in the retest queue or a next_test note.
8. Finish every run with its decision, unresolved risk, saved QAA links, and one
recommended next QA action.
Recommended Agent Loop
1. Establish the QAA project before doing any durable QA work. Call
qa-list-projects, match the user's URL/request to an existing project,
and reuse its id.
2. If no project matches and the token is workspace-wide, create the project
with qa-create-project immediately. If the token cannot create projects,
ask the user which existing project to use before saving test cases, runs,
defects, workspace data, or agent field notes.
3. Treat the selected or created project id as the anchor for the rest of the
run. Every workspace update, Application Map node, test case, run, result,
triage action, and agent field note should reference this project.
4. Read the project brief with qa-agent-project-brief. Use it as the primary
planning input for release health, coverage gaps, recent failures, active
defects, retests, work items, memory notes, and next-test notes.
5. Read or update the QA workspace with qa-workspace when the brief shows
missing context, missing safe test data, or retest items that need changes.
Use context, testData,
and retestItems as planning input before choosing what to run.
6. Read the QA Work Board with qa-list-work-items. If the user asks the
agent to test queued product work, start from ready_for_qa cards and move
the chosen card to testing with qa-update-work-item.
7. Interpret the user's chat intent and choose a strategy such as smoke,
regression, investigation, coverage, or rerun.
8. Read current product structure with qa-list-app-map when the brief's map
summary is not enough for the task.
9. Read current coverage with qa-list-test-cases and reusable collections
with qa-test-suites when the brief's top tests and coverage gaps are not
enough for the task.
10. If mobile testing is requested, configure safe Android devices and mobile
cases in QAA first. Do not execute arbitrary ADB commands outside the mobile
runner contract.
11. If this is a new or sparse project, bootstrap the project workspace:
upsert project context from the walkthrough, create initial Application Map
application/API/data/coverage layer nodes, create starter smoke test cases,
and save any user-provided test data with the correct sensitivity
classification.
12. During exploration, create or update Application Map nodes and edges with
qa-upsert-app-map-node and qa-upsert-app-map-edge. Use concrete surfaces
such as login page, dashboard, scan form, history table, settings modal,
upload API, database table, customer entity, or payment provider rather than
generic test-case categories.
13. When visual context helps human review, capture a browser or mobile
harness screenshot and upload it with /api/qa-screenshots.
14. Attach relevant screenshots to Application Map nodes and/or durable test
cases. Map screenshots explain what a surface is; test case screenshots
explain the intended page/component/state for that check.
15. Create missing durable coverage with qa-create-test-case, including
quality-oriented tags, strategyNotes, agentGenerated, and uploaded
screenshots. Treat suiteType as legacy compatibility metadata.
16. Update existing durable coverage with qa-update-test-case when screenshots
or metadata need to be attached after creation. Use qa-test-suites to add
cases to reusable named suites; do not encode membership in suiteType.
17. Link each relevant test case to the Application Map with
qa-link-test-case-to-app-node.
18. Before a multi-case, coverage, regression, or release execution, create a
durable run plan with qa-run-plan. Save the selected scope, out-of-scope
areas, selected suite ids and/or individual test case ids, risk areas,
reason, expected evidence,
confidence before the run, and tracks when subagents will execute parallel
UX, accessibility, performance, functional, content, API, or mobile slices.
A single-case linked retest may reuse its existing retest and source-run
context without creating an otherwise empty plan.
19. Start execution with qa-create-run, including runType, strategy,
requestedBy, and sourcePrompt. Include runPlanId when the execution
uses a saved plan; omit it for the single-case linked-retest exception. A
linked plan is marked in_progress automatically.
20. For failed or blocked web steps, capture a Playwright screenshot, upload it with
/api/qa-screenshots, and keep the returned evidence object.
21. For mobile runs, use the mobile runner/harness contract and persist only
redacted mobile evidence. Never paste raw logcat, UI dumps, secrets, or
sensitive screenshots into reports.
22. Add each result with qa-add-result, including failed step metadata and
any uploaded evidence. As subagents finish, patch the matching run-plan
track with status, summary, findings, coverage, and confidence after.
23. Finish the run with qa-update-run. A linked plan is marked completed
and receives an actualSummary when the run finishes as Passed, Failed,
or Blocked.
24. Update the originating Work Board card with qa-update-work-item: use
tested/passed when accepted, in_progress/failed when a bug is found,
or in_progress/blocked with a clear blocked reason when the agent
cannot proceed. done is reserved for accepted/completed work.
25. Create triage actions for failures that need review, defect creation, rerun,
or flaky classification.
26. Persist workspace learnings after the run: update project context when the
product understanding changed, save reusable test data, create retest items
only for unresolved failures that qa-add-result did not already create or
reuse, update resolved retest statuses, and use qa-create-agent-note for
agent field notes that should appear in /agent-notes. Use field notes for
test narrative, reasoning, and project knowledge that is not yet a test
case or finding.
27. Reply in chat with the run summary, saved QAA links, unresolved risks, and
the recommended next test. Do not stop at a chat-only report when QAA access
and a matching project are available.
QAA is the platform where the agent can work with project context, approved
tools, durable QA state, evidence, and triage. Humans talk to the agent, then
watch realtime notifications and the activity timeline, inspect failed runs,
and triage the agent's findings in QAA.
Create Agent Field Notes
Use qa-create-agent-note when Sladdis has useful test narrative that should
appear in /agent-notes and should not overwrite a ticket. Tickets are actions.
Agent field notes preserve observations, reasoning, recommendations, memory,
and next-test ideas. Notes can stand alone or link to a run and/or Work Board
ticket.
qa-create-agent-note creates Agent Field Notes shown in /agent-notes.
curl -X POST "$SUPABASE_FUNCTIONS_URL/qa-create-agent-note" \
-H "Authorization: Bearer $QAA_AGENT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"projectId": "PROJECT_UUID",
"source": "telegram",
"noteType": "recommendation",
"title": "Pure Consulting smoke pass narrative",
"body": "Summary: tested public website metadata and core navigation.\nObserved duplicate social metadata and broken touch icon.\nNext test: rerun metadata crawler after favicon fix.",
"tags": ["metadata", "smoke", "public-web"],
"relatedRunId": "run-...",
"relatedTicketId": "WORK_ITEM_UUID"
}'source must be one of telegram, testbench, qaa_run, manual, or
edge_function. noteType must be one of observation, reasoning,
recommendation, memory, next_test, or reply.
Write notes for scanning and later retrieval:
- Use a short, outcome-oriented title and keep one idea per note.
- Use observation for evidence-backed findings, reasoning for a decision
trace, recommendation for a proposed action, next_test for a concrete
future check, and memory only for durable project facts.
- Link relatedRunId and/or relatedTicketId whenever the note comes from
executable QA work. This keeps the narrative connected to its evidence.
- Prefer three to six stable tags over sentence-like or one-off tags.
- Create replies with noteType: "reply" and the root note's parentNoteId so
follow-up context remains in the same thread.
QA Manager artifacts (QM0)
Use qa-manager-artifacts for structured strategy, risk, coverage, readiness,
gate, metric, and sign-off proposals. This endpoint requires a project-scoped
token with qa_manager:write. Sladdis may write only draft, needs_input, or
ready_for_review; owner/admin approval remains in QAA.
curl -X POST "$SUPABASE_FUNCTIONS_URL/qa-manager-artifacts" \
-H "Authorization: Bearer $QAA_AGENT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"projectId": "PROJECT_UUID",
"artifactType": "test_strategy",
"state": "ready_for_review",
"idempotencyKey": "strategy-build-abc123-v1",
"content": {
"objective": "Protect the release decision",
"scope": {"in": ["application flow"], "out": []},
"qualityGoals": ["No critical-path regression"],
"exitCriteria": ["All release blockers resolved"]
},
"sources": [{"kind": "project_brief", "ref": "current"}],
"evidenceRefs": ["run-abc123"]
}'GET accepts projectId and optional artifactType. PATCH requires id,
projectId, and expectedRevision. A stale update returns 409; re-read and
reconcile it. Reusing an idempotency key with a different payload also returns
409. The full schema and responsibility boundary are documented in
docs/qa-manager-contract.md.
Screenshots uploaded through /api/qa-screenshots are signature-validated and
kept in private project-scoped storage. Persist the returned proxy URL and
metadata; never replace it with a raw storage URL.