# Gap: Tests page — Pause / Retry-phase / Skip-phase controls
## Context
The Tests wizard (`Plan → Preconditions → Running → Done`) introduces three controls
in the Running step's bottom bar:
| Control | Purpose |
|---|---|
| **Pause** | Temporarily halts phase progression — bench coasts to a safe-RPM hold, keep-alive and CAN traffic continue, timers freeze. |
| **Retry current phase** | Aborts the current phase (without failing it), zeroes pump outputs, restarts the same phase from conditioning. |
| **Skip current phase** | Marks the current phase as "skipped" (neither passed nor failed) and advances to the next enabled phase. |
At the time of writing these three buttons exist in `Views/UserControls/TestRunningView.xaml`
rendered `IsEnabled="False"` with a `Test.Running.ComingSoon` tooltip. This document
captures the full requirements for a follow-up task that wires them to `IBenchService`.
## Required `IBenchService` API additions
```csharp
///
/// Pauses the currently running test. The bench holds at idle RPM, oil pump stays on,
/// keep-alive continues. Phase timers are frozen. No-op if not running.
///
Task PauseAsync(CancellationToken ct = default);
///
/// Resumes a paused test. Re-arms phase timers from where they were frozen.
/// Throws if not paused.
///
Task ResumeAsync(CancellationToken ct = default);
///
/// Aborts the current phase (without marking it failed), zeros pump outputs
/// (ME/FBKW/PreIn → 0), and restarts the same phase from the beginning of
/// its conditioning sub-section. No-op if not running.
///
Task RetryCurrentPhaseAsync(CancellationToken ct = default);
///
/// Marks the current phase as "skipped" and advances to the next enabled phase.
/// Pump outputs are zeroed between phases. No-op if at the last enabled phase
/// (in that case the test completes normally).
///
Task SkipPhaseAsync(CancellationToken ct = default);
```
All four methods must be callable from a UI thread and must **not** block — they
queue a transition request onto the existing `BenchService` worker loop and await
completion via a `TaskCompletionSource`.
## Safe-state expectations per operation
### Pause
- RPM: ramp to **idle hold** (configurable, default 600 rpm) using the existing ramp logic.
- Oil pump relay: stays on.
- Pump outputs: unchanged (paused at current values).
- Counter / encoder relays: unchanged.
- Keep-alive loop: continues.
- Phase countdown: frozen in place. Timer resumes on `ResumeAsync`.
- Measurement buffer: continues accumulating samples while paused? **Decision needed** —
current recommendation is to discard samples received while paused, so tolerance
evaluation uses only "active" measurement time.
### Resume
- RPM: ramp from idle-hold back to the current phase's target RPM before un-freezing timers.
- Un-freeze phase timers only **after** RPM is within ±5% of target for ≥500 ms (same
condition the initial conditioning ramp uses).
### Retry
- Pump outputs: **zeroed immediately** (ME=0, FBKW=0, PreIn=0). This addresses the
HIGH-priority gap in CLAUDE.md ("Pump parameters (ME/FBKW/PreIn) not zeroed
between test phases") for the retry path specifically.
- Phase: same phase re-entered at start of conditioning.
- Results: any partial samples for the retried phase are discarded. `TestResult.Samples`
for that phase is cleared.
- `PhaseChanged` event fires with the same phase name again so the UI re-highlights the card.
### Skip
- Pump outputs: zeroed.
- Phase disposition: `TestResult` row is inserted with `Status = Skipped` (new enum value
on `TestResult.Status` or existing `Pass/Fail` with a separate `IsSkipped` flag).
- Progress: next enabled phase in the same test, or next enabled test if this was the last
phase. If no enabled phase follows, the test completes as normal (`IsTestRunning = false`).
## UI state transitions
| Button | Enabled when | Label change |
|---|---|---|
| Pause | `IsTestRunning && !IsPaused` | becomes "Resume" (`Test.Running.Resume`) while paused |
| Retry | `IsTestRunning && !IsPaused` | static — no relabel |
| Skip | `IsTestRunning && !IsPaused` | static |
| Abort | always while running (paused or not) | static |
When paused, the Retry and Skip buttons are **disabled** to simplify the state machine
(operator must Resume first, then Retry/Skip). This keeps the BenchService state space
small (Running ↔ Paused; Retry/Skip only transition inside Running).
The **Abort confirm dialog** already used for Abort should be reused for **Skip**
but with different title/message (`Test.Skip.ConfirmTitle`, `Test.Skip.ConfirmMessage`).
Retry does **not** require confirmation — it's a non-destructive restart of the current
phase only.
## Interaction with PID temperature control
PID loop (`BenchService.TemperatureLoopAsync`) should **continue** during all four
operations — temperature is an environmental condition, not a test output. The oil-pump
interlock (`IsOilPumpOn`) must stay satisfied throughout; a Pause that loses the oil
pump (e.g. operator toggles the relay via the Bench page) must **abort** the test
through the existing safety path, not silently resume with oil off.
## Interaction with the oil-pump safety interlock
The "no critical alarms" precondition is evaluated at Preconditions-step entry only.
Once Running, the existing `DashboardAlarmsViewModel.HasCritical` observer is the
authoritative safety check. Pause/Retry/Skip do **not** bypass it — any of the four
operations should fail (throw) if a critical alarm is latched when invoked, surfacing
the same warning banner the existing Running state already shows.
## Localization keys
Already added to `Resources/Strings.en.xaml` and `Resources/Strings.es.xaml`:
- `Test.Running.Pause`
- `Test.Running.Resume`
- `Test.Running.Retry`
- `Test.Running.Skip`
- `Test.Running.ComingSoon` (tooltip while buttons remain disabled)
- `Test.Running.Abort`
To add when Skip confirmation lands:
- `Test.Skip.ConfirmTitle`
- `Test.Skip.ConfirmMessage`
- `Test.Skip.Confirm`
- `Test.Skip.Cancel`
## Relation to existing HIGH-priority gap
CLAUDE.md ("Pump parameters (ME/FBKW/PreIn) not zeroed between test phases") is the
broader gap. This document's Retry path implements the zero-between-phases behaviour
for the retry transition specifically. The general phase-to-phase zeroing in
`BenchService.RunPhaseAsync` must also be fixed — tracked separately.
## Manual verification checklist
1. Start a multi-phase test. During a measurement phase click **Pause** — RPM drops
to idle, countdown freezes, button relabels to "Resume". Flow charts stop
advancing, oil pump stays on, no critical alarm.
2. Click **Resume** — RPM ramps back to the phase target, countdown unfreezes from
the exact value, label returns to "Pause".
3. During a measurement phase with partial samples, click **Retry** — pump outputs
zero, phase restarts at conditioning, card highlight refreshes, previous samples
cleared from `TestResult`.
4. Click **Skip** on the second of three enabled phases — confirm dialog opens
(`Test.Skip.ConfirmMessage`). Cancel: test continues. Confirm: phase disposition
set to Skipped, pump outputs zeroed, next enabled phase starts conditioning.
5. Skip the final enabled phase — test completes, transitions to Done step.
6. Trigger a critical alarm mid-Pause — test aborts via the existing critical-alarm
path, Done step shows "Failed — safety stop".
7. Toggle language mid-Pause — button text updates to localized "Resume" / "Pause".
8. Unplug CAN during Pause — CAN-liveness watchdog aborts the test.
## Out of scope (acknowledged)
- Skipping / retrying a phase while paused (requires Resume first).
- A Skip-all-remaining-phases shortcut.
- A "Retry this test" that restarts from the first phase of the current test.