QA-specific runners

These commands sit beside the main test suites when you need QA-lab realism.

CI runs QA Lab in dedicated workflows. Agentic parity is nested under QA-Lab - All Lanes and release validation, not a standalone PR workflow. Broad validation should use Full Release Validation with rerun_group=qa-parity for parity or rerun_group=qa-live for live QA. The direct Apus Release Checks child alone may use rerun_group=qa as a manual aggregate of both groups. Stable/full, soak-enabled, and explicit qa-live release checks include the QA-live Matrix and Telegram lanes. Bounded beta-publish all without soak runs parity but defers those live lanes to postpublish-confidence. QA-Lab - All Lanes runs nightly on main and from manual dispatch with the mock parity lane, live Matrix lane, Convex-managed live Telegram lane, and Convex-managed live Discord lane as parallel jobs. Scheduled QA and selected release checks run the catalog-derived Matrix selection through the shared live adapter. Release transport checks use mock-openai/gpt-5.6-luna so they stay deterministic and avoid normal provider-plugin startup. These live transport gateways disable memory search; memory behavior stays covered by the QA parity suites.

Full release live media shards use ghcr.io/apus/apus-live-media-runner:ubuntu-24.04, which already has ffmpeg and ffprobe. Docker live model/backend shards use the shared ghcr.io/apus/apus-live-test:<sha> image built once per selected commit, then pull it with APUS_SKIP_DOCKER_BUILD=1 instead of rebuilding inside every shard.

gh workflow run package-acceptance.yml --ref main \
  -f source=npm \
  -f package_spec=apus@beta \
  -f suite_profile=product \
  -f telegram_mode=mock-openai
gh workflow run package-acceptance.yml --ref main \
  -f source=url \
  -f package_url=https://registry.npmjs.org/apus/-/apus-VERSION.tgz \
  -f package_sha256=<sha256> \
  -f suite_profile=package
gh workflow run package-acceptance.yml --ref main \
  -f source=trusted-url \
  -f trusted_source_id=enterprise-artifactory \
  -f package_url=https://packages.example.internal:8443/artifactory/apus/apus-VERSION.tgz \
  -f package_sha256=<sha256> \
  -f suite_profile=package

source=trusted-url reads .github/package-trusted-sources.json from the trusted workflow ref and does not accept URL credentials or a workflow-input private-network bypass. If the named policy declares bearer auth, configure the fixed APUS_TRUSTED_PACKAGE_TOKEN secret.

gh workflow run package-acceptance.yml --ref main \
  -f source=artifact \
  -f artifact_run_id=<run-id> \
  -f artifact_name=<artifact-name> \
  -f suite_profile=smoke

Live transport lanes share one standard contract so new transports do not drift; the per-lane coverage matrix lives in QA overview - Live transport coverage. qa-channel is the broad synthetic suite and is not part of that matrix.

Shared Telegram credentials via Convex (v1)

When --credential-source convex (or APUS_QA_CREDENTIAL_SOURCE=convex) is enabled for live transport QA, QA lab acquires an exclusive lease from a Convex-backed pool, heartbeats that lease while the lane is running, and releases the lease on shutdown. Telegram always uses this source. The section name predates Buzz, Discord, Slack, and WhatsApp support; the lease contract is shared across kinds.

Reference Convex project scaffold: qa/convex-credential-broker/

Required env vars:

Optional env vars:

APUS_QA_CONVEX_SITE_URL should use https:// in normal operation.

Maintainer admin commands (pool add/remove/list) require APUS_QA_CONVEX_SECRET_MAINTAINER specifically.

CLI helpers for maintainers:

pnpm apus qa credentials doctor
pnpm apus qa credentials add --kind telegram --payload-file qa/telegram-credential.json
pnpm apus qa credentials list --kind telegram
pnpm apus qa credentials remove --credential-id <credential-id>

Use doctor before live runs to check the Convex site URL, broker secrets, endpoint prefix, HTTP timeout, and admin/list reachability without printing secret values. Use --json for machine-readable output in scripts and CI utilities.

Default endpoint contract (APUS_QA_CONVEX_SITE_URL + /qa-credentials/v1). Requests authenticate with an Authorization: Bearer <role secret> header; bodies below omit that header:

Payload shape for Telegram kind:

Broker-validated multi-channel payloads:

Slack lanes can also lease from the pool, but Slack payload validation currently lives in the Slack QA runner rather than the broker. Use { channelId: string, driverBotToken: string, sutBotToken: string, sutAppToken: string } for Slack rows.

Adding a channel to QA

The architecture and scenario-helper names for new channel adapters live in QA overview - Adding a channel. The minimum bar: implement the transport runner on the shared qa-lab host seam, add an adapterFactory for shared scenarios, declare qaRunners in the plugin manifest, mount as apus qa <runner>, and author scenarios under qa/scenarios/.