# Twice

> Use Twice to approve browser-test plans, run user journeys, read reproduction evidence, export reports, and gate pull requests on confirmed defects.

Twice tests an app through user journeys. It proposes flows and expectations for your approval, runs the approved plan in a cloud browser, retries failures, and attaches screenshots and per-attempt evidence to its findings. A failed journey is not automatically a confirmed defect, and a journey that could not run is never a pass.

![The Twice page in the dashboard before a test run](https://docs.fatelabs.ca/docs/shots/qa.webp)

**Note:**
Twice repair delivery is a draft pull request for a person to review. QA verification does not authorize an automatic merge or deployment. A passing journey, a visual measurement and a proposed improvement are different kinds of evidence; a measurement alone does not prove a fix.

## Choose what Twice should test

Use Twice for sign-up, checkout, search, and other behavior that needs evidence from a running app. It complements unit and integration tests in [Estelle CI](https://docs.fatelabs.ca/ci) and code review in [Prism](https://docs.fatelabs.ca/prism).

The **Test my app** form offers two targets:

- **Your code: QA builds and runs it.** Twice checks out a repository and ref in a Silo, builds and starts that copy, and tests it there. Its clicks do not reach your deployed app. Choose this when you want to test a commit without creating a preview deployment.
- **A deployed address.** Twice visits a staging or preview URL that the internet can reach. Choose this when you want to test the build and configuration you actually deployed. The cloud browser cannot open `localhost`.

## Plan and run a test

### Describe the app and what matters

Open **Twice → Test my app**. Choose the target, then enter the repository and ref or the deployed address. Add the behavior you care about, such as “a new customer can sign up and complete checkout.”

### Choose access and limits

Pick how Twice signs in. Under **Advanced**, you can name the run, choose its environment, set time and credit limits, and decide whether it only reports problems or also prepares fixes. For a deployed app, supply a source repository only if you want fix work.

### Confirm planning

Choose **Plan the tests**, then **Confirm and plan**. Planning uses a cloud browser; it does not run the test journeys.

### Review and approve the plan

Check every journey, its steps, and its expected results. Expectations marked as Estelle's guesses are hypotheses: a failure on one can only be suspected. Confirm the expectations you mean, rewrite unclear ones, and remove journeys you do not want. Nothing is tested until you approve.

### Watch the run and read the report

The live view shows the browser and steps. The finished report totals journeys, passes, problems, and anything that could not be checked. Open failed journeys first; their screenshots and findings are already expanded.

You can also select **Paste it as JSON** to supply a written plan. **Check the plan** previews its journeys, access requirements, restrictions, and cost ceiling without spending credits. Optional Gherkin scenarios can add supported Given/When/Then steps; steps outside the supported sentences are returned as questions rather than guessed.

## What happens when it finds something

Twice also looks at every page it visits. A vision model reads the final screen and each step that changed the page, against your repository's design file or, without one, the other pages in the run. It reports UI that is off your design, controls that don't work, friction, a wrong flow, and whether the page does what it is for. Each finding shows its screenshot with the element marked, what was expected and why. It reads as suspected unless a measurement agrees, such as a click that really changed nothing.

Automatic fix sessions are partly held for now. A finding on a commit Twice built, and any finding from reading a page, gets a written fix brief instead of a coding session, because a web page can write part of what the session would read, and that session must not hold your GitHub token. The plan says so before the run starts.

If you fix it on a branch, Twice can build that branch and the original side by side and re-test them. The result goes in the run report: fixed, not fixed, the fix broke something else, or could not check.

A person merges. Nothing merges or deploys on its own. A model's reading of a screen never changes whether a check passed and cannot fail a pull request check. To turn off page judging for a run, set `"page_judge": false` in the plan. Page judging can spend up to 60 credits a run, so an automation that re-runs a stored plan does not start it by itself: turn it on for that automation on the Twice page. A repository's `.estelle/qa.json` turns it on with `"page_judge": true`.

Each run states its limits on the plan before it starts: at most three coding sessions, one per finding, and one re-check per fix. To only report problems, choose **Just report problems** under **Advanced**. Your workspace's autonomy setting must allow proposing a pull request; if it does not, the plan says so and Twice reports without fixing.

## Give the build its environment

Variables under **Settings → Environment → Secrets** reach the install, build, start, and check commands of a run that tests your code, for the workspace and for that repository. Their values are stripped from logs, findings, and pull requests before they are stored. Text is all that can be stripped: a value your app draws on the page can appear in a screenshot, so keep real production secrets out of a test environment.

Twice detects Node, Python, and static sites. For any other stack, name the commands in `target.from_code.commands` or `.estelle/qa-from-code.json`; without them it reports that it cannot build that stack yet.

## Provide safe sign-in access

The available choices depend on the target:

| Choice | Use it when |
|---|---|
| **QA gives itself an account in its own build** | You test from code. Twice creates its own account in the copy it started. |
| **Signed out (no sign-in)** | The journeys only need visitor access. |
| **Let QA sign up a test user** | A deployed app allows public sign-up. Twice creates a test user and uses a throwaway inbox for confirmation. |
| **Use a saved test login** | A deployed app needs an existing test account. Enter the login's secret-name prefix. |

For a saved login, store the fields under **Settings → Environment → Secrets** with one prefix. For example, `STAGING_TESTER` can refer to `STAGING_TESTER_USERNAME`, `STAGING_TESTER_PASSWORD`, and, when needed, `STAGING_TESTER_EMAIL`. The plan carries the prefix, not the credential values. If required access is missing, the plan names it; **What this run used** records the items granted to the run.

**Limit:**
Use a staging or preview environment and a disposable test account. Never provide a real customer's or administrator's login. On a deployed address, Twice holds actions such as paying, deleting, sending messages, and changing account settings unless you explicitly allow them.

## Interpret the results

Every result belongs to one of these categories:

| Result | Meaning and next step |
|---|---|
| **Passed** | The journey's checks held in the recorded run. Read any separate notes about mocks, visual checks, or unreviewed criteria before treating it as complete coverage. |
| **Confirmed defect** | The same approved expectation failed again on the same build with evidence. Reproduce it, fix it, and test the new build. |
| **Suspected defect** | Something failed, but confirmation is incomplete: it did not reproduce, evidence is missing, or the expectation was only a hypothesis. Confirm the expectation and rerun before using it as a merge blocker. |
| **Blocked / inconclusive** | Twice could not finish or judge the journey. Fix the access or environment problem; this is not a pass. A failed build of your own code is a confirmed defect with its log instead. |
| **Observed anomaly** | Something is worth inspecting, but no stated expectation failed. |
| **Improvement suggestion** | A possible improvement, not a defect. |

A completed run with no findings is evidence only about the journeys and checks that ran. A cancelled, failed, budget-stopped, or partly blocked run does not become clean because it found nothing.

## Use the evidence, not just the verdict

Open a finding to inspect:

- the expectation and whether a person approved it;
- what each attempt observed and whether the build stayed the same;
- the reproduction receipt and screenshots from each try;
- failed backend calls seen during the journey, which are associated evidence rather than a proven cause;
- any mocked responses, locator healing, visual differences, or environment details that limit the result.

Use **Watch it in the recording** to inspect the journey in context. The recording is a sampled reel of browser frames, not a full-fidelity video. A run retains at most 150 frames, 12 MB, or 30 minutes of recording; the report says when a limit cut the reel short.

## Download a report or reproduce locally

When a finished run has stored reports, Twice offers **Markdown**, **HTML**, **JUnit XML**, **JSON**, and, for runs with failed journeys, **Repro script (Playwright)**. Only reports stored for that run appear.

The Playwright download reruns each failed journey once against the address the app serves now. It reports whether the same checks reproduce, fail differently, or hold. It is a fresh reproduction attempt, not another Twice two-attempt verdict, and it does not embed secrets. Supply any required login or session values through your own environment.

```bash
npm install playwright
npx playwright install chromium
node twice-repro-<id>.mjs
echo $?
```

The exit code is `0` when the exported checks hold, `1` when a check fails, and `2` when a journey or the repro itself cannot run.

## Test pull requests automatically

Commit a `.estelle/qa.json` campaign contract to opt the repository into **Estelle QA** checks.

1. For preview testing, put your preview URL pattern in the contract and its allowed origins. The placeholders `{pr}`, `{sha}`, and `{branch}` are filled from the pull request.
2. Alternatively, set `from_code: true` to build and test the pull request's commit without a preview URL.
3. Add `.estelle/qa.feature` if you keep journey scenarios in supported Gherkin.
4. Open or update a pull request and follow the **Estelle QA** check back to the run and its evidence.

Pull-request runs are test-only. A confirmed defect fails the check; suspected or inconclusive results and a run that could not start leave it neutral. A green check means the measured journeys passed, subject to the review notes in the run. Fork pull requests do not run. Set `on_pull_request: false` to opt out of these runs.

From-code contracts can also run on pushes: by default on the repository's default branch, or on branches named in `on_push.branches`. Set `on_push: false` to disable push runs.

## Choose whether Twice prepares fixes

Under **Advanced → When something breaks**, choose:

- **Just report problems** for test-only evidence;
- **Report and plan fixes** to evaluate a proposed repair;
- **Prepare fixes on a branch** to have repair work prepared for review.

A repository is required for fix work. Fixes remain reviewable changes; a person reviews and merges them. Automatic pull-request tests always remain test-only.

## Limits and cost

- Deployed targets must be reachable over the internet; `localhost` is not supported. Estelle opens no tunnel, so an app that only runs on a private network has to be tested through **Your code: QA builds and runs it**.
- A written `wait_for.timeout_ms` must be between 1,000 and 120,000 milliseconds. Out-of-range values are refused.
- An upload step accepts 1 to 5 files, up to 512 KiB each and 1 MiB in total.
- Reproduction scripts omit checks that require Twice-only instrumentation and label them as not checked. Inbox-dependent steps cannot run outside a campaign.
- Planning, cloud browser time, and fix work use Estelle Credits. Time and credit limits stop a run; work already used remains billed. See [Plans and credits](https://docs.fatelabs.ca/plans).

## Related

- [Estelle CI](https://docs.fatelabs.ca/ci): Run your build and test commands.
- [Prism](https://docs.fatelabs.ca/prism): Review the change before merging.
- [Silos](https://docs.fatelabs.ca/silos): Understand the isolated machines used for test runs.

## Match a repair to the tested source

Code-built campaigns can record the exact commit they built. A code-backed or explicitly pinned repair must agree with its trusted source record; it refuses an unavailable or mismatched required pin instead of silently switching to a newer branch tip.

A deployed URL alone does not identify the source commit serving it. URL-only campaigns without a trusted build mapping remain unbound to a commit, even when a repository is attached. Do not describe their repair as proof that a specific commit served that URL. Check the recorded build and repair source before comparing results.
