# Execution panel

> Create test runs with environment selection, execute run cases in the slide-over panel — keyboard shortcuts, resize, expand, history.

## Creating a run

Open a project and click **New run** (or use the **+** button in the runs list). The **New test run** modal asks for:

| Field                | Notes                                                                                                                                                                                                                                                                           |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Name**             | Required. A short label for this run.                                                                                                                                                                                                                                           |
| **Description**      | Optional notes about what this run covers. It can hold formatting and images.                                                                                                                                                                                                   |
| **Environment**      | Select one of the project's configured environments. The project **default environment** is preselected automatically when the list loads. If the project has no environments yet, the selector is disabled — see [Environments](https://docs.probara.net/en/guides/environments/) to create one first. |
| **Default assignee** | Optional. Assign a team member to all cases in the run.                                                                                                                                                                                                                         |
| **Cases**            | Optional. Add test cases from the project repository now, or add them later from the execution board.                                                                                                                                                                           |

To clear the environment selection after the default has been preselected, choose the **None** option at the top of the dropdown.

The linked environment is shown in the run detail header, the runs list table, and the summary rail. When the associated environment is later deleted, the runs that referenced it continue to display its name as a historical label.

## Run execution panel

When you open a case from the execution board (`/projects/:projectId/runs/:runId`, where `:runId` is the run's readable ID, for example `/projects/ACME/runs/R-12`), Probara renders a **slide-over panel** with everything you need to execute the case in place: the case description and conditions, the steps with their data and expected results, the result band, and the case's history of prior attempts. A Gherkin step shows its keyword and argument instead of data and an expected result; see [Gherkin test cases](https://docs.probara.net/en/guides/gherkin-test-cases/). The address bar keeps the open case, its tab and the attempt you are viewing, so reloading the page or sending the link to a teammate shows the same view.

The panel is the same on `open` and `closed` runs — controls disable or hide only on an **aborted** run (a closed, non-aborted run stays fully editable), but the layout stays consistent.

## Keyboard shortcuts

Once the panel has focus, you can run the entire case without touching the mouse.

| Key         | Action                                                      |
| ----------- | ----------------------------------------------------------- |
| `1` / `P`   | Mark the focused step (or the case) as **Passed**           |
| `2` / `F`   | Mark as **Failed**                                          |
| `3` / `S`   | Mark as **Skipped**                                         |
| `4` / `B`   | Mark as **Blocked**                                         |
| `ArrowDown` | Move focus to the next step header                          |
| `ArrowUp`   | Move focus to the previous step header                      |
| `Escape`    | Close the panel (or close the shortcut hint when it's open) |
| `?`         | Toggle a popover listing every shortcut                     |

If no step has keyboard focus, the marking shortcuts dispatch on the **case** result band. Once a step header is focused, the shortcuts target that step.

Shortcuts are **suppressed** while you're typing in an input, textarea, or contenteditable element (so typing `P` in the actual-result field inserts the character rather than marking the step).

On the **read-only attempt panel** (opened from a case's Retries tab), the marking shortcuts are inert — you can still navigate between steps with the arrow keys and close with `Escape`.

## Auto-advance

After you mark a step, Probara scrolls the next untested step into view automatically and expands its disclosure if it carries data, expected output, or an actual-result note. If no untested step remains forward, focus falls back to the nearest untested step before the current one.

Auto-advance does not change the active **case** in the slide-over — only the step you're focused on inside that case. Case-level marking (via the Case result band) still advances to the next untested case in the visible list — except when you mark **Failed** or **Blocked**, which opens the defect capture flow below before advancing.

## Defect capture (Failed / Blocked)

When you mark the **case** as **Failed** or **Blocked**, Probara opens a **Report defect** modal after the mark is saved. Auto-advance to the next untested case waits until the modal closes.

You can:

- **Create new** — title prefilled from the case snapshot (autofocused), severity defaults to `major`, priority to `medium`. Press **Enter** or **Create** to create the defect and link it to this attempt.
- **Attach existing** — one list that starts with up to five defects already tied to this test case and filters every project defect as you type a title or `D-<number>`; select one and press **Attach defect**.
- **Cancel** — close without a defect; advance resumes.

Press **Escape** in the modal (same as Cancel). While the modal is open, **Escape** on the panel does not close the slide-over beneath it.

**Passed** and **Skipped** case marks behave as before — no modal, immediate advance. Per-step marks never open this modal. Bulk **Submit result** from the run selection bar opens its own bulk-mode **Add result** modal instead (see [Run selection bar](#run-selection-bar)); when it reports a defect, it opens this SAME **Report defect** modal — create a new defect or search and attach an existing one, with automatic reopen when the attached defect is resolved or closed, exactly as in the single-case flow. The only difference is scope: one defect is created or attached and linked to every selected case's new result in a single request.

Linked defects appear as **`D-<n>` chips** under the panel header (status-colored dot, full title on hover). They refresh after a successful link without reloading the page.

On the **Create new** tab, the description is prefilled as formatted text from the case's failed steps (title, precondition, expected-vs-actual per step, with the images the precondition and actual results hold) and, when the attempt has [result attachments](#result-attachments), an **Evidence** section listing each attachment's filename and download link. The Evidence heading and the rest of the auto-composed description are always in **English**, regardless of your interface language — this keeps the prefill consistent for cross-team defect triage. You can edit the description freely before creating or linking the defect, with formatting and images: paste or drop an image into it, or type **/** and choose **Image**.

## Resizing the panel

Drag the left edge of the panel to resize it horizontally. Your preferred width is persisted in `localStorage` (`tcms:run-case-panel-width`) and restored across reloads.

The width clamps between **360 px** (minimum) and **min(1100 px, 95 % of the viewport)** (maximum). Both the live slide-over and the read-only attempt panel share the same width.

## Resizing and collapsing the summary panel

The run detail's right-hand **summary panel** — the rail that reports the run's progress and metadata — carries the same two manual controls as the other three summary panels.

Drag its **leading edge** to set the width: dragging left widens the panel and dragging right narrows it. With the handle focused, `ArrowLeft` widens by one step and `ArrowRight` narrows by one step, and a **double-click** restores the panel's default width.

The **round chevron button on the panel's left edge** — shown when you point at the panel or tab to it — or the `]` key collapses it into a 56px readout column: the progress ring, the completion percentage and the executed/total count stay, a hairline separates them from a rack of icon keys, and each key opens that section's values — state, environment, dates, configurations, custom fields, tags — in a popover anchored beside the column. Collapsing removes no value; every section is one click deeper. The same toggle, in the same place, expands the panel again.

**The width and the collapsed state are shared by all four summary panels** — runs, test plans, milestones and defects — so a resize or collapse made here also applies to the other three, and both settings survive a reload.

## Expand to full screen

Click the **Maximize** icon (⤢) in the panel header to expand the panel into a centered full-screen dialog. On viewports of **1024 px or wider**, the **Data** and **Expected result** blocks lay out side-by-side inside each step; everything else (actual result, attachments) spans the full width.

Click the **Minimize** icon (now in the same slot) or the backdrop to collapse back to the slide-over. The slide-over preserves its scroll position when it returns.

On a narrow (phone-width) screen the panel already opens full-screen, so there is no resize handle and no expand control — nothing to resize or expand into on a screen this width.

## Run overflow menu (edit, abort, run again, and delete)

The header **⋯** menu exposes lifecycle actions gated by run state and your organization role. The same menu, with the same items and behavior, is also available from each row's **⋯** trigger on the project's runs list — you don't need to open a run to edit, abort, run again, or delete it.

| Action        | Visible when                               | Minimum role                     |
| ------------- | ------------------------------------------ | -------------------------------- |
| **Edit**      | Run is `open`                              | Member (`viewer` cannot execute) |
| **Abort**     | Run is `open`                              | Member (`viewer` cannot execute) |
| **Run again** | Run is `closed` (completed **or** aborted) | Member (`viewer` cannot execute) |
| **Delete**    | Run is `open` or `closed`                  | Admin or owner                   |

**Edit** opens a dedicated modal to change the run's `name`, `description`, `environment`, and `milestone`, and to reselect its cases — reusing the same repository-only case picker as creation, seeded with the run's current selection and per-case assignees. **Default assignee is a creation-only seed**: the Edit modal never renders it and never sends it, whether or not the run has one. Saving persists metadata via the existing `PATCH /api/v1/runs/:runUlid` and case changes via the atomic `PATCH /api/v1/runs/:runUlid/cases` (see [API reference](https://docs.probara.net/en/developers/api-reference/v1/)). Deselecting a case that already carries a recorded result or tracked time triggers a **destructive confirmation** naming the affected count before the save proceeds; cancelling leaves the run fully untouched. A case removed and later re-added always comes back `untested` — its prior result and time are not restored. Edit is offered only while the run is `open`; once a run closes — automatically or via Complete — use **Run again** instead of Edit.

**Abort** ends an open run early without pretending it finished: cases still untested stay untested, existing results and attachments are kept, and the run closes with a projected **Aborted** status (yellow/warn chip). Abort is **terminal**: an aborted run's case controls stay disabled and its board shows a read-only banner, unlike a completed (closed, non-aborted) run, which stays fully editable. Aborted runs still count as finished for milestone auto-completion and test-plan progress, but contribute no case counts to either.

There is no **Reopen** — a run, once closed, stays closed; nothing done inside it (marking, retrying, adding cases) ever reopens it. **Run again** replaces Reopen in the same menu slot, on **both** completed and aborted runs: it opens the **Clone test run** dialog (required Title, a "Clone with assignees" toggle, and a searchable multi-select of the six case-result statuses to carry over — empty means every case) and creates a brand-new **open** run from the finished one. The source run is never modified.

**Delete** is irreversible: it removes the run, every case, step result, attachment, and snapshot in one cascade. A `run.deleted` audit row survives in the **workspace** Activity feed with the final scorecard snapshot; the per-run Activity tab disappears with the run. Only admins and owners may delete. Delete remains available on aborted runs.

All actions open a confirmation dialog (or the Clone modal, for Run again) before the mutation runs; Abort's dialog and Edit's destructive-removal step are destructive-styled. Edit's own metadata/case-picker step is the editing surface itself, not a confirmation.

## Saving the actual result

When a step is marked, the **Actual result** field becomes available. It can hold formatting. As you type, Probara persists changes on blur and shows a small indicator next to the label:

Edits to the actual-result text on an **open**, **untested** case emit `run.case_step_actual_result_set` events on the run's **Activity** tab (and in workspace Activity) with a `from → to` diff.

- `Saving…` while a save request is in flight.
- `Saved` once it succeeds.

The field grows vertically as you type, so multi-paragraph notes stay visible without an inner scrollbar.

To show what happened, paste an image into the actual result, drop it there, or type **/** and choose **Image**: it goes where you put it.

## Time tracking

Probara times how long each case takes to execute and stores it on the case as **time spent**. The run total in the runs list and the per-suite total on the execution board are the sums of those values, so the more cases you mark, the fuller the picture of where time goes.

### The silent stopwatch

When you open a case for execution, a stopwatch starts and accumulates while the case is open — **silently**. There's no visible timer or time field while you work, so you're never watching a clock tick as you test. When you mark the case, the elapsed time is saved in the **same request** as the result — one action records both the status and the time.

It's a plain stopwatch: it simply counts the time the case is open, with no pause or idle detection. Legitimate testing often produces no browser activity — you might be testing on a phone or another device, working in a separate app, or reading a long case — so Probara never tries to guess when you've "stopped working". If the recorded time doesn't match reality, you can correct it once the case is marked (see below).

You don't have to mark a case for the time to be kept: if you leave the page mid-execution (close the tab, navigate away), Probara flushes the accumulated time to the case before unloading, without changing its status. Reopening the case later resumes from that stored value.

### Reviewing and correcting the recorded time

The captured time is shown — as an editable **Time spent** field — **only after you mark the case**. There is no time field while the case is unmarked; it appears once the case has a result so you can review what was recorded and fix it if needed (you executed part of the case offline, or stepped away for something unrelated).

- The field is segmented into **hours, minutes, and seconds**, each labelled with its unit on screen. It seeds with the recorded value, each segment is typed or stepped with the arrow keys, and one pair of minus and plus buttons at the end of the field raises or lowers whichever segment is focused. The value is confirmed with **Enter** or by leaving the field. Focusing it pins the draft, so the display holds still while you edit. A total can also be pasted, in any of `1:30:05`, `02:30`, `2m 30s`, or `45s`.
- Confirm with **Enter** or by leaving the field. A valid value is saved immediately and **replaces** the recorded time — in either direction, so you can shrink an over-counted value as well as raise it. Editing a marked case updates only its time, not its result. An invalid value surfaces only when you leave the field or confirm it — it shows an inline hint and is not saved.
- A later confirmed edit replaces the previously stored value, so you can correct it as many times as you need.

A manually entered value always wins over the auto-captured one.

### What does not get timed

Cases marked through the run selection bar's **Submit result** action follow whatever total you enter there — left empty, they keep an empty time (shown as `—`) exactly like a single-case mark with no time; a total you do enter is split evenly across the selected cases (see [Run selection bar](#run-selection-bar)). Either way, time can always be entered or corrected manually afterward. Runs created before time tracking existed are not backfilled; their cases stay `—` until re-executed.

## Retries tab and attempt panel

Switch to the **Retries** tab to see every recorded marking for the case, oldest first to most recent at the top, on a vertical timeline:

- The most recent row carries a **Current** badge.
- Earlier rows that are not the case's first attempt carry a **Retry** indicator.
- Each row shows the actor (avatar + name), the status as a tonal chip, the duration with tabular numerals, and a relative timestamp ("2 h ago" / "yesterday" / etc.).

Click any row to open the **read-only attempt panel** — a snapshot of the case exactly as it was when that marking happened. The header icon toolbar lets you expand the snapshot to full screen, jump back to the Retries tab, and close.

A parameterized case has one run case for each combination, so the **Retries** tab lists the attempts of this run case only. Choose **Same values only** to show just the attempts that ran with this run case's values. The **Siblings** tab lists the other run cases of the same case in the run, with their status and values. See [Parameterized test cases](https://docs.probara.net/en/guides/parameterized-test-cases/).

## Result attachments

Marking a case **Failed** or **Blocked** opens an **Add result** modal to capture a comment and reviewed time — you can also attach files as evidence for the whole attempt, separate from the images in the comment and in each step's actual result.

- **Any file type** is accepted (screenshots, logs, videos, HAR files, PDFs, and so on), up to **32 MB per file** and **20 files per attempt**. A short deny-list blocks executable/script types (for example `.exe`, `.sh`, `.bat`-equivalent files); everything else is allowed.
- **Drag and drop** files onto the dropzone, or click it to open a file picker. Each file uploads with its own progress indicator; you can remove a file before confirming.
- Every file shows as a tile with its name and extension: **images** (`PNG`, `JPEG`, `WebP`) with a thumbnail, **every other file type** — including GIF and SVG, which can carry embedded scripts and so never render inline — with an icon. Clicking any tile opens the attachment viewer at that file: images, videos, audio and text files (logs, Markdown, JSON) preview there, and a file with no preview, such as a PDF or a ZIP, shows **No preview available** with a **Download** button. To download directly, hover a tile and use its download button.
- **Playwright traces** (a `.zip` with `trace` in its name, such as `trace.zip`) also get an **Open in the trace viewer** button (↗) on their tile, and the attachment viewer offers **Open in trace viewer** beside **Download**: the trace opens in the Playwright Trace Viewer in a new tab, served by Probara from `trace.probara.net`, with nothing to download or upload. A trace recorded with a newer Playwright version than the viewer supports may not open; download it and open it with your local Playwright instead.
- Files appear in the **Actual result** section once you confirm, alongside your comment.
- **Editable while the run is open or closed-but-not-aborted**: open **Add result** to add or remove attachments on the current attempt. **Re-marking** a case starts a brand-new attempt with **zero** attachments — the previous attempt's evidence stays intact in its own entry in the Retries tab. Once the run is **aborted**, attachments (and the rest of the attempt) become **read-only**.
- **Organization storage quota**: your organization has a shared storage allowance (1 GiB by default on the Free tier) covering every attachment type in Probara, not just result attachments. Organizations created before the Free tier's rollout keep their grandfathered 5 GiB allowance. If a commit would push your organization over its allowance, the whole batch is rejected with a clear error — the comment and reviewed time you already saved are never affected. There is no self-service quota UI yet; an organization's allowance can be raised on request (contact support). Images in a step's actual result follow the same rule: an over-quota save leaves the actual result as it was. See [what counts toward storage](https://docs.probara.net/en/guides/attachments/).

## Run selection bar

Hover a case row or a suite-band header (or move keyboard focus into it) to reveal its **selection checkbox** — checking a suite band selects every case in its full subtree, including nested descendants and any cases currently hidden by an active filter. Selecting one or more cases animates in an inline **run selection bar** directly under the filter toolbar (hidden only on an **aborted** run — an open or closed-but-not-aborted run keeps it). It shows the selection count and lets you **submit a result**, assign, retry, or remove every selected case in one click.

**Submit result** opens the same **Add result** modal used for a single case, but in bulk mode: the Result field always defaults to **Passed** when the modal opens (whatever the selected cases' current statuses are, and even if you changed it and cancelled last time), Recorded time is optional and is entered in the segmented hours : minutes : seconds field — a total you enter is split evenly across the selected cases server-side, and a subtle hint under the field states how many cases it will split across; **leaving the field untouched means no time is recorded**, not `00:00` — and the shared comment and attachments are copied to every case's new result. Reporting a defect (offered only for Failed/Blocked, exactly like the single-case modal) opens the same **Report defect** modal used for a single case: **create** a new defect, or **search and attach** an existing one, with an automatic reopen when the attached defect is resolved or closed — the choice folds into the same bulk request, so one defect is created or attached and linked to every selected case's new result. On success, a toast confirms the defect's `D-<n>` number, whether it was newly created or an existing defect you attached. Each gesture — including the defect step, when used — issues a single bulk request and produces **one** timeline row on the run's **Activity** tab (and in workspace Activity), with an expandable list of the touched cases.

**Assign** opens a modal listing your organization's members plus **Unassigned**; pick one (search by name or email) and confirm to set — or clear — the assignee of every selected case. If all selected cases already share an assignee, that member is preselected. Confirming issues one bulk request and adds one row to the run's **Activity** tab.

The bar collapses when you clear the selection — via **Clear selection** or by unchecking every case — or when the run is aborted.

## Removing a single case

Each case row on an open or closed-but-not-aborted run carries a **⋮ actions menu** at the end of the row. For now it holds a single entry, **Remove from run**, which takes that one case out of the run. Choosing it asks for confirmation first; confirming removes the case (along with its recorded result) and records a `run.case_removed` entry on the run's **Activity** tab. The menu is hidden only on an **aborted** run, where the board is read-only.

## Searching and filtering cases

Above the suite bands, the **Test cases** tab uses the same search-and-filter bar as the rest of Probara: a **search field** followed by a **filter bar** with three dimensions.

- **Search** — type to match a case's **title**, or the name and value of its parameters (case-insensitively). Filtering runs entirely in the browser over the cases already loaded for the run; there is no extra request.
- **Status** — a permanent multi-select over the five outcome statuses (**Untested**, **Passed**, **Failed**, **Skipped**, **Blocked**). Selecting more than one status is **OR-combined**: a case is shown if it matches **any** selected status. Leaving Status empty does not filter by status.
- **Assignee** — a removable filter built from the run's members, with an **Unassigned** option that matches cases nobody is assigned to (`assigneeUlid` is empty).
- **Priority** — a removable filter whose options are **derived from the priority values present on the run's cases**. Only priorities that actually appear are offered; there is no synthetic "no priority" option, and the list updates as the run's cases change.

Across the search field and the three filters, every active dimension is **AND-combined** — a case stays visible only when it satisfies all of them. When nothing matches, the board shows an **empty state** instead of any suite band. **Suite is intentionally not a filter** — the board already groups cases into suite bands.

The full filter state (search text plus Status, Assignee, and Priority selections) is **synced to the URL**, so a filtered execution view is shareable, bookmarkable, and **survives a reload** — reopening the URL restores the same visible cases.

## Reduced motion

If your operating system requests reduced motion (`prefers-reduced-motion: reduce`), all the panel's transitions — entrance/exit, step body stagger, donut update, history hover translate — collapse to a non-animated update. Functionality stays identical.
