mo-qa ยท diff

git:20261023.f8bd1a2 to git:20261028.67a79c8

87 added, 309 removed. Audit A to A.

---
name: mo-qa
description: Run and control Mo QA sessions with Momentic's `qa` CLI. Use for bug bashes, status checks, report export, replies to Mo, file transfer, or repairs based on Mo findings.
---
# Run QA with Mo
Mo runs browser QA in a hosted sandbox. Its files and processes are remote.
- ## Work while Mo runs
-
- Keep the tested revision stable through the initial QA pass and its
- reproductions. Do not change anything the target serves or hot-reloads, restart
- the app, deploy to its URL, or mutate shared test data while Mo uses it. Other
- work, such as code review, is fine. For isolated bug fixes during a longer run,
- follow [Repair loop](references/remediation-loop.md).
+ ## Workflow at a glance
- Findings arrive through `qa status "$session_id" --full`. A `kind: "bug"`
- entry has been independently reproduced. A `kind: "flag"` entry is a static,
- single-frame mistake such as a typo, recorded without reproduction. Agent
- verdicts are `verified`, `issues_found`, or `blocked`. When you report a finding
- to the user, include its name, evidence, status, and `web_url`.
+ Run these phases in order:
- If Mo is blocked, answer from the brief, repository, or authorized environment
- data when you can. Otherwise ask the user for the missing access, permission,
- or decision, then send the answer to root Mo. Root Mo relays context to its
- internal sub-agents. Never invent or reveal a secret.
+ 1. **Install and authenticate.** Run `qa version`. If Mo is missing or
+ outdated, read [Installation](references/installation.md). After a `qa`
+ login error, read [Authentication](references/authentication.md#authenticate-mo).
+ 2. **Start.** Read [Brief](references/brief.md) for a worked example and how
+ to pick `--effort`. Then [prepare the brief](#prepare-the-brief) and
+ [start the session](#start-the-session) with `qa start "$brief"`.
+ 3. **Follow the session.** First read
+ [Session control](references/session-control.md): it explains each
+ `status` and how to run the poll. Then
+ [follow the session](#follow-the-session) and read Mo's questions and replies
+ with `qa read "$session_id" --from start --timeout 45s --json`.
+ `qa <command> --help` shows the JSON a command prints.
+ 4. **Answer questions.** `needs_you` means Mo is waiting on you;
+ `attentionPending: true` alone means its reply is still being classified.
+ Answer with `qa send --session-id "$session_id" --wait 45s "<answer>"`. A
+ plain `qa send` answers the oldest open question card. To answer a specific
+ card, add `--question-id <id>`; answer each card in `qa read`'s
+ `pendingQuestions` (`pendingInput` is the oldest).
+ 5. **Finish.** Check coverage, export with
+ `qa report "$session_id" --output <dir>`, then end spending with
+ `qa stop "$session_id" --subagents`; see [Finish](#finish). After the
+ last session, run `qa tunnel stop "$tunnel_id"` if you started a tunnel.
## Prepare the brief
- Run `qa version`. If Mo is missing or outdated, read
- [Installation](references/installation.md). Read
- [Authentication](references/authentication.md) after an authentication error.
-
- Use the target from the request. For a local or private target, read
- [Tunneling](references/tunneling.md).
-
- Give Mo the testing goal and operating boundaries. Include what applies:
-
- 1. The target URL when known, including any relevant path and query.
- 2. The user's goal, relevant product intent, and known requirements.
- 3. The login method, test account label, and allowed test data.
- 4. Prohibited actions and data that Mo must not change.
- 5. What the user wants tested and any requested exclusions.
- 6. Any non-default browser setup. Read
- [Browser settings](references/browser-settings.md) when needed.
+ Give Mo the testing goal and operating boundaries. Include what applies: the
+ target URL from the request, with any relevant path and query; the user's goal,
+ relevant product intent, and known requirements; the login method, test account
+ label, and allowed test data; prohibited actions and data that Mo must not
+ change; and what the user wants tested, with any requested exclusions.
- Supply factual context from the request and repository without turning it into
- a prescribed test plan. Ask only when missing scope, access, or permission would
- change the run.
+ Read a reference when it applies:
- ### Set high level parameters, let Mo plan by default
+ - Local or private target: [Tunneling](references/tunneling.md).
+ - Testing a GitHub PR or local changes, or Mo can't access the app's Git
+ repository: [Source context](references/source-context.md).
+ - Signing Mo into the app, or giving Mo secrets or environment variables:
+ [Authentication](references/authentication.md#authenticate-the-application).
+ - Any change to Mo's browser or how fast it interacts:
+ [Browser settings](references/browser-settings.md).
- Let Mo choose flows and sub-agent orchestration within the right
- scope and budget for the user's request. Describe what the user wants tested.
- Preserve user-specified coverage; otherwise do not prescribe a checklist,
- interaction steps, or a fixed number of checks. Supply implementation facts as context.
+ Supply factual context from the request and repository, not a prescribed test
+ plan. Preserve user-specified coverage; otherwise do not prescribe a checklist,
+ interaction steps, or a fixed number of checks. Let Mo choose flows and
+ sub-agent orchestration within the right scope and budget. Ask only when
+ missing scope, access, or permission would change the run.
You own target setup, authentication, and actions Mo cannot perform remotely.
- Before starting, remove unrequested testing restrictions. Leave normal
- concurrency available; reduce `--max-concurrency` only for an explicit request
- or a demonstrated target limitation, and record the reason.
-
- Give Mo the agreed duration or spend budget and let it prioritize coverage.
- "Light" does not imply a fixed number of checks or serial execution. Mo has no
- session-wide time or test-count option. For a user-specified hard cap, monitor wall time or
- `qa cost <session-id>`, stop with `qa stop <session-id> --subagents` at the cap,
- and report unfinished coverage.
+ Before starting, pick `--effort` for the agreed duration and let Mo prioritize
+ coverage.
## Start the session
- Pass the brief as one argument to `qa start`.
- For a user requesting express-checkout smoke coverage and a standard-checkout
- regression check:
+ Pass the brief as one argument and keep the returned values:
```bash
- brief=$(cat <<'EOF'
- Target: https://preview.example.com/checkout?variant=express
- Goal: Keep the selected shipping method after returning from payment.
- Sign in: Use the staging QA buyer account.
- Test data: Create test carts and orders only.
- Do not: Submit payment, send email, delete data, or change the shared catalog.
- Coverage: Smoke the express-checkout happy path and standard checkout. Skip other flows and failure states.
- Pass criteria:
- - The shipping method stays selected.
- - The total does not change.
- - Standard checkout still works.
- EOF
- )
session_json=$(qa start "$brief")
session_id=$(jq -r .sessionId <<<"$session_json")
web_url=$(jq -r .webUrl <<<"$session_json")
created_at=$(jq -r '.createdAt // empty' <<<"$session_json")
```
- Starting returns before Mo finishes. Preserve these values: every later command
- needs `session_id`, the user can watch or join through `web_url`, and
- `created_at` records when the session began. `createdAt` can be absent when the
- server runs an earlier API version.
-
- For example, when testing an unreleased CLI that Mo cannot build or run:
-
- > Ask me to start sessions with the branch's unreleased QA CLI. Send the required
- > arguments and wait for a session URL, then verify it in the web UI.
-
- For a GitHub PR, use PR mode instead of placing its reference only in the brief.
- Pass the deployment URL you created or verified with `--url`; any HTTP or HTTPS
- preview host works:
-
- ```bash
- session_json=$(qa start "$brief" --pr https://github.com/acme/shop/pull/412 --url https://preview.example.com)
- ```
-
- `--pr` accepts a full GitHub PR URL, including comment permalinks and links with
- query parameters. Mo keeps the repository and PR number, dropping the query and
- fragment. `owner/repo#number` also works. Name the PR explicitly;
- do not infer it from the current branch or remote. Mo captures the remote PR
- revision and tests its changed app behavior and regressions. Omit the brief to
- use that default scope. Changes with no testable app behavior finish without
- launching testing agents.
-
- For PR sessions, let Mo identify changed behavior and affected flows from the
- PR context. Plan for about 10 minutes of aggregate interaction time and about
- 20 minutes elapsed, including planning and reproduction. Mo's PR instructions
- guide it toward focused verification and fewer explorers. Follow explicit
- user scope, duration, and concurrency requests instead of these defaults.
- These are planning targets, not enforced limits; monitor and stop the session
- when the user supplies a hard cap.
-
- An environment supplies the target without requiring a preview URL. Otherwise,
- Mo looks for a preview tied to the PR commit in Vercel, Cloudflare Pages, or
- Netlify bot posts. Failed or ambiguous discovery does not block startup: Mo
- can inspect the PR or ask for the target. Don't invent a URL from a branch name.
- Use `--tunnel` as well when the chosen deployment is local or private.
- Without `--pr`, `--url` and `--patch` are ignored; put a general session's
- target URL in its brief.
-
- Each PR session posts a GitHub comment with its session link and updates that
- comment with the results. A new session collapses older Mo comments as outdated;
- their session and report links remain available. The Mo check on the same
- commit follows the latest request. Use the session report for findings instead
- of treating a superseded comment as the current result.
-
- For local changes, commit and push to that PR before starting whenever authorized.
- If pushing is disallowed, pass a curated Git patch with `--patch FILE` from the
- PR checkout. The CLI uses local `HEAD` as the base; it must match the published
- PR head or startup is rejected. Use a target serving those local changes,
- with `--url` when known and `--tunnel` if needed.
- A patch gives Mo source context; it does not deploy the changes.
-
- Select only files and hunks needed to test the change, including relevant
- untracked files. Review the patch before sending it. Exclude secrets,
- credentials, logs, temporary files, large blobs, and unrelated changes or
- customer data. Do not collect the entire working tree or force-add
- ignored files. Patches must be text-only Git diffs of at most 256 KiB.
-
- To include tracked and untracked files without changing your real Git
- index, use a temporary index and an explicit file list. Verify that local HEAD
- matches the named PR head before running this example, then replace its paths
- with the files you reviewed:
-
- ```bash
- patch_base=$(git rev-parse --verify HEAD)
- patch_file=$(mktemp)
- (
- set -eu
- patch_index=$(mktemp)
- rm "$patch_index"
- trap 'rm -f "$patch_index"' EXIT
- export GIT_INDEX_FILE="$patch_index"
- git read-tree "$patch_base"
- git add -- src/checkout.ts src/discount.ts
- git diff --cached --no-renames --no-ext-diff --no-textconv "$patch_base" -- src/checkout.ts src/discount.ts > "$patch_file"
- )
- git apply --stat "$patch_file"
- ```
-
- Read every hunk in `patch_file` with your file-reading tool before starting Mo.
- Remove any excluded content, then start the session:
-
- ```bash
- qa start "$brief" --pr https://github.com/acme/shop/pull/412 --url https://preview.example.com --patch "$patch_file"
- rm -f "$patch_file"
- ```
-
- Send the patch in the initial start request. Do not start a session and upload it
- afterward: Mo may begin reading code before that upload finishes. Mo uses the PR
- revision plus the supplied patch for source investigation. Keep the target and
- patch stable throughout testing and reproduction. Clean patch runs produce a
- neutral GitHub check because their source includes unpublished changes.
-
- A Momentic environment groups a `BASE_URL` with reusable non-secret variables
- under a name such as `staging`. `--environment NAME` selects one created under
- **Environments** in the Momentic dashboard, not one from the shell or
- `momentic.config.yaml`. Mo copies its variables into the session at start.
-
- Use repeatable `--env-file` or `--env-var NAME` options for local values and
- secrets; they override matching environment variables. `--env-var` forwards a
- variable that is already present in the `qa start` process environment; it does
- not accept `NAME=value`. Never put the secret value in the command or brief.
- Use `--tunnel` for private access. The concurrency limit is fixed at session
- start. If target overload requires a lower limit, run `qa stop --subagents`
- and start a new session. `--interaction-speed <default|human>` slows browser
- interaction to a human pace when the target needs it.
+ Starting returns before Mo finishes. The user watches or joins through
+ `web_url`; `created_at` can be absent on older servers.
## Follow the session
- Use this watcher to print new bug names, verdict states, and display-state
- changes. It exits when Mo needs input or reports a terminal state:
-
- ```bash
- seen=""
- while :; do
- snapshot=$(qa status "$session_id" --full) ||
- { echo "status request failed"; sleep 30; continue; }
- events=$(jq -r '"displayState \(.displayState // .state)",
- (.findings.bugs[] | "\(.kind) \(.name)"),
- (.findings.verdicts[] | "verdict \(.status)")' \
- <<<"$snapshot" | sort)
- comm -13 <(printf '%s\n' "$seen") <(printf '%s\n' "$events")
- seen=$events
- if [[ $(jq -r '.attentionPending // false' <<<"$snapshot") != true ]] || [[ $(jq -r '.displayState // .state' <<<"$snapshot") == needs_you ]]; then
- case $(jq -r '.displayState // .state' <<<"$snapshot") in
- needs_you | blocked | findings | incomplete | replied | ready | sleeping | cancelled | failed_start | failed | waitingOnUser | idle | stopped) break ;;
- esac
- fi
- sleep 30
- done
- ```
-
- On older lifecycle-only responses, `idle` or `stopped` does not establish that
- sub-agents finished; confirm completion in the web session.
-
- Use a host tool that reports watcher output while you work, or a runner
- sub-agent when the environment supports messages from running agents.
- Otherwise retain the command session and check its output between tasks.
-
- In Codex without a runner sub-agent, keep the watcher in the foreground of a
- long-running `exec_command`, retain its returned session ID, and drain it with
- `write_stdin`. Do not append `&`, detach it, or finish the turn while it runs.
- Shell variables do not persist across separate commands, so interpolate the
- literal Mo session ID or start the watcher in the same shell that set it.
-
- You answer blockers and send Mo the user's decisions.
-
- `displayState` is the session's state as the UI shows it. `status`, `read`,
- and `report` all report the same word there:
-
- | State | Meaning |
- | ------------------- | --------------------------------------------------------------------------------------------------- |
- | `running` | Root Mo is working. |
- | `waiting_on_agents` | Root Mo is idle; its internal sub-agents are working. |
- | `needs_you` | Mo needs access, a decision, or an answer. Read its reply and respond with `qa send`. |
- | `blocked` | The request hit a tool, access, quota, or agent failure. Inspect the reply and resolve the blocker. |
- | `findings` | No agent is running; bugs or issues need review. |
- | `incomplete` | No agent is running; report coverage is blocked or unverified. |
- | `replied` | No agent is running; Mo replied without a complete QA report. |
- | `checking` | Mo is checking its latest reply. Keep polling for the outcome. |
- | `ready` | No agent is running; the saved report is complete and clean. |
- | `sleeping` | No agent is running and there is no report or final reply. |
- | `cancelled` | Work was stopped. |
- | `failed_start` | The session never started. Start a new one. |
- | `failed` | The session failed. Inspect its transcript. |
-
- `sessionState` is the lifecycle state (`starting`, `working`,
- `waitingOnUser`, `waitingOnAgents`, `idle`, `stopped`, or `failed`), also identical
- across all three commands. `state` is the legacy field, an alias of
- `sessionState` wherever it appears. `createdAt` is the session creation time,
- and `lastActivityAt` is the last persisted update. `latestTurn` contains the
- most recent persisted assistant timing metadata: `startedAt`, `completedAt`,
- and `durationMs`. A partial timing uses `null`. Servers running an earlier API
- can omit `displayState` on `read` and `report`; use `qa status` for the
- display word on those.
-
- Use this bounded read for Mo's questions and replies, not findings:
-
- ```bash
- qa read "$session_id" --from start --timeout 45s --json
- ```
-
- It reads persisted conversation messages and user-facing messages streamed
- during the wait.
- `--from latest` also misses a turn that finishes before the read begins. In a
- `read` response, `displayState` matches the `status` word, while `state` and
- `sessionState` carry the lifecycle state. Servers running an earlier API omit
- `displayState`; use `qa status` for the display word on those.
- `timedOut: true` means Mo is still working.
-
- With `--json`, `read` writes one JSON response to stdout and no progress text.
- Without `--json`, a read that waits longer than two seconds prints liveness to
- stderr. Returned messages stay on stdout, while timeout or stopped status text
- stays on stderr.
-
- `qa wait "$session_id" --json` returns when root Mo's turn finishes, stops, or
- needs input. Exit code `2` means Mo needs input; `4` means it was stopped.
- Internal sub-agents can still be running. Confirm `qa status` no longer reports
- `running`, `waiting_on_agents`, or `checking`, then inspect the outcome. `needs_you` and
- `blocked` require attention; `findings`, `incomplete`, and `replied` are settled
- outcomes that do not establish passing QA. Saved partial reports remain
- exportable with `qa report --require-idle` once all work has settled.
-
- Never send a message to ask for progress. Use `status`, `read`, or `wait`.
- Send only to answer a blocker or deliberately steer or recheck work:
-
- ```bash
- qa send --session-id "$session_id" --wait 45s "Use the staging account."
- ```
-
- Sending while active steers root Mo between tool calls while in-flight work
- continues. When Mo is idle, the message starts its next turn. Without `--wait`,
- confirm the reply with `read`.
+ Poll `qa status "$session_id" --full` about every 30 seconds. Report new
+ `findings.bugs` names and `status` changes as they appear. Read one bug's
+ details with `qa bugs describe "$session_id" <bug-id>`. Keep polling while
+ `status` is `running`, `waiting_on_agents`, or `checking`, or while
+ `attentionPending` is true and `status` is not `needs_you`. Stop on any
+ other state and act on it as Session control describes. Stop and tell the user
+ after 3 failed status calls in a row, or once the session has run twice its
+ effort's testing time.
- ## Finish or repair
+ Run the poll with a host tool that reports output while you work, a runner
+ sub-agent, or the fallback in
+ [Session control](references/session-control.md#polling).
- Before presenting final results, read [Reports](references/reports.md). Confirm
- coverage against the user's scope. An idle session or zero bugs alone does not
- establish passing QA.
+ `ready` and `sleeping` are settled but do not establish passing QA.
- If the user asked for fixes, read
- [Repair loop](references/remediation-loop.md). Otherwise, do not change app code.
+ ## Read and answer Mo
- Choose how to stop:
+ Use the bounded `qa read --from start` above for Mo's questions and replies,
+ not findings; `--from latest` misses a turn that finishes before the read
+ begins, and `timedOut: true` means Mo is still working.
- - `qa stop <session-id>` interrupts root Mo only. Sub-agents keep testing,
- filing findings, and billing. Root Mo stays stopped until your next
- `qa send`. Use it to redirect Mo without losing in-flight tests.
- - `qa stop <session-id> --subagents` also stops every running sub-agent. Use it
- to end spending. `qa send` can still resume the session.
- - `qa archive <session-id>` cancels all work, hides the session, and rejects
- further `qa send`. Unarchive is web-only.
+ If Mo is blocked, answer from the brief, repository, or authorized environment
+ data when you can. Otherwise ask the user for the missing access, permission,
+ or decision, then send the answer to root Mo, which relays context to its
+ internal sub-agents. Never invent or reveal a secret.
- `qa upload` returns a sandbox path. Send that path to Mo because local paths do
- not exist in its sandbox. Create the destination directory before `qa download`
- when `--output` names a directory. Run `qa <command> --help` for syntax.
+ For routine progress, use `status`, `read`, or `wait`. Send a message to answer
+ a blocker, to steer or recheck work, or to ask Mo for a summary. Sending while
+ active steers root Mo between tool calls while in-flight work continues. When
+ Mo is idle, the message starts its next turn. Without `--wait`, confirm the
+ reply with `read`.
- ## Fallback: provide source context without repository access
+ ## Work while Mo runs
- Prefer setting up Git access in Mo for repository context. If access is
- unavailable, tell the user and recommend configuring it.
+ Keep the tested revision stable through the initial QA pass and its
+ reproductions. Do not change anything the target serves or hot-reloads, restart
+ the app, deploy to its URL, or mutate shared test data while Mo uses it. Other
+ work, such as code review, is fine. For bug fixes during the run, or if the
+ user asked for fixes, read [Repair loop](references/remediation-loop.md).
+ Otherwise, do not change app code.
- When Git access cannot be configured, prepare a temporary source snapshot
- containing only the files Mo needs. Review its contents before uploading;
- exclude `.env` files, credentials, private keys, tokens, `node_modules`, build
- output, and unrelated customer data. Include relevant local changes or a
- reviewed patch when testing uncommitted work.
+ ## Finish
- After starting the session, upload the snapshot with `qa upload` and send Mo
- the returned sandbox path with `qa send`. Remove the temporary local snapshot
- after upload. Tell Mo to use it as read-only context, which paths are
- authoritative, whether it represents baseline or modified code, and which
- commands or dependency files explain how to use it. Local paths do not work
- inside Mo's sandbox.
+ Before presenting results, check Mo's coverage against the user's scope. If
+ something in scope is untested, steer Mo with `qa send` to test it and follow
+ the session again. Then read [Reports](references/reports.md) and export with
+ `qa report "$session_id" --output <dir>`. End spending with
+ `qa stop "$session_id" --subagents`, even if the export failed. Then give the
+ user the findings, any coverage gaps, the export path, and `web_url`. If
+ `qa report` warned that the report may be partial, tell the user.